Refactor MPSolver interface registration to include runtime readiness checks. (#4973)

This PR is removing the need for `linear_solver` to depend on `GurobiIsCorrectlyInstalled` and `XpressIsCorrectlyInstalled`.
This commit is contained in:
Guillaume Chatelet
2026-01-07 15:49:29 +01:00
committed by Corentin Le Molgat
parent 6a603cc183
commit 204b31a748
4 changed files with 42 additions and 38 deletions

View File

@@ -1417,7 +1417,8 @@ namespace {
const void* const kRegisterGurobiLp ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new GurobiInterface(solver, false); },
MPSolver::GUROBI_LINEAR_PROGRAMMING);
MPSolver::GUROBI_LINEAR_PROGRAMMING,
[]() { return GurobiIsCorrectlyInstalled(); });
return nullptr;
}();
@@ -1425,7 +1426,8 @@ const void* const kRegisterGurobiLp ABSL_ATTRIBUTE_UNUSED = [] {
const void* const kRegisterGurobiMip ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new GurobiInterface(solver, true); },
MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING);
MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING,
[]() { return GurobiIsCorrectlyInstalled(); });
return nullptr;
}();

View File

@@ -412,26 +412,10 @@ MPSolver::MPSolver(const std::string& name,
MPSolver::~MPSolver() { Clear(); }
extern bool GurobiIsCorrectlyInstalled();
extern bool XpressIsCorrectlyInstalled();
// static
bool MPSolver::SupportsProblemType(OptimizationProblemType problem_type) {
if (!MPSolverInterfaceFactoryRepository::GetInstance()->Supports(
problem_type)) {
return false;
}
switch (problem_type) {
case GUROBI_LINEAR_PROGRAMMING:
case GUROBI_MIXED_INTEGER_PROGRAMMING:
return GurobiIsCorrectlyInstalled();
case XPRESS_LINEAR_PROGRAMMING:
case XPRESS_MIXED_INTEGER_PROGRAMMING:
return XpressIsCorrectlyInstalled();
default:
break;
}
return true;
return MPSolverInterfaceFactoryRepository::GetInstance()->Supports(
problem_type);
}
// TODO(user): post c++ 14, instead use
@@ -2238,9 +2222,14 @@ MPSolverInterfaceFactoryRepository::~MPSolverInterfaceFactoryRepository() {
void MPSolverInterfaceFactoryRepository::Register(
MPSolverInterfaceFactory factory,
MPSolver::OptimizationProblemType problem_type) {
MPSolver::OptimizationProblemType problem_type,
std::function<bool()> is_runtime_ready) {
absl::MutexLock lock(mutex_);
map_[problem_type] = std::move(factory);
if (!is_runtime_ready) is_runtime_ready = []() { return true; };
map_[problem_type] = Entry{
.factory = std::move(factory),
.is_runtime_ready = std::move(is_runtime_ready),
};
}
bool MPSolverInterfaceFactoryRepository::Unregister(
@@ -2252,17 +2241,20 @@ bool MPSolverInterfaceFactoryRepository::Unregister(
MPSolverInterface* MPSolverInterfaceFactoryRepository::Create(
MPSolver* solver) const {
absl::MutexLock lock(mutex_);
const MPSolverInterfaceFactory factory =
gtl::FindWithDefault(map_, solver->ProblemType(), nullptr);
if (!factory) {
return nullptr;
}
return factory(solver);
const Entry* entry = gtl::FindOrNull(map_, solver->ProblemType());
CHECK(entry != nullptr) << "No factory registered for problem type "
<< ToString(solver->ProblemType());
CHECK(entry->is_runtime_ready())
<< "Solver for problem type " << ToString(solver->ProblemType())
<< " is not ready.";
return entry->factory(solver);
}
bool MPSolverInterfaceFactoryRepository::Supports(
MPSolver::OptimizationProblemType problem_type) const {
return map_.count(problem_type) > 0;
const Entry* entry = gtl::FindOrNull(map_, problem_type);
if (entry == nullptr) return false;
return entry->is_runtime_ready();
}
std::vector<MPSolver::OptimizationProblemType>

View File

@@ -152,6 +152,7 @@
#include "absl/container/flat_hash_map.h"
#include "absl/flags/declare.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
@@ -1959,17 +1960,20 @@ class MPSolverInterfaceFactoryRepository {
public:
static MPSolverInterfaceFactoryRepository* GetInstance();
// Maps the given factory to the given problem type. If a factory was already
// assigned to this problem type, it will be replaced.
// Maps the given factory to the given problem type. For solver needing
// runtime checks an additional `is_runtime_ready` argument can be set. If
// a factory was already assigned to this problem type, it will be replaced.
void Register(MPSolverInterfaceFactory factory,
MPSolver::OptimizationProblemType problem_type);
MPSolver::OptimizationProblemType problem_type,
std::function<bool()> is_runtime_ready = {});
// Invokes the factory associated to the given solver's problem type,
// or return NULL if no factory was found for it.
// Invokes the factory associated to the given solver's problem type and fails
// if no factory is registered or its runtime is not ready.
// Use `Supports` below to check if `Create` succeeds.
MPSolverInterface* Create(MPSolver* solver) const;
// Whether the implementation associated to the given problem type is
// available.
// available and ready to use.
bool Supports(MPSolver::OptimizationProblemType problem_type) const;
// List all the problem types.
@@ -1991,7 +1995,11 @@ class MPSolverInterfaceFactoryRepository {
~MPSolverInterfaceFactoryRepository();
mutable absl::Mutex mutex_;
std::map<MPSolver::OptimizationProblemType, MPSolverInterfaceFactory> map_;
struct Entry {
MPSolverInterfaceFactory factory;
std::function<bool()> is_runtime_ready;
};
std::map<MPSolver::OptimizationProblemType, Entry> map_;
};
} // namespace operations_research

View File

@@ -2289,7 +2289,8 @@ namespace {
const void* const kRegisterXpress ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new XpressInterface(solver, false); },
MPSolver::XPRESS_LINEAR_PROGRAMMING);
MPSolver::XPRESS_LINEAR_PROGRAMMING,
[]() { return XpressIsCorrectlyInstalled(); });
return nullptr;
}();
@@ -2297,7 +2298,8 @@ const void* const kRegisterXpress ABSL_ATTRIBUTE_UNUSED = [] {
const void* const kRegisterXpressMip ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new XpressInterface(solver, true); },
MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING);
MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING,
[]() { return XpressIsCorrectlyInstalled(); });
return nullptr;
}();