From 204b31a748dda752835c1300556dc56b2a223343 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Wed, 7 Jan 2026 15:49:29 +0100 Subject: [PATCH] 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`. --- ortools/linear_solver/gurobi_interface.cc | 6 ++- ortools/linear_solver/linear_solver.cc | 46 ++++++++++------------- ortools/linear_solver/linear_solver.h | 22 +++++++---- ortools/linear_solver/xpress_interface.cc | 6 ++- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/ortools/linear_solver/gurobi_interface.cc b/ortools/linear_solver/gurobi_interface.cc index af035792cc..6b2e8b1614 100644 --- a/ortools/linear_solver/gurobi_interface.cc +++ b/ortools/linear_solver/gurobi_interface.cc @@ -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; }(); diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 74c14c54cf..824128d510 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -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 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 diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h index 4656ac1369..087ce54789 100644 --- a/ortools/linear_solver/linear_solver.h +++ b/ortools/linear_solver/linear_solver.h @@ -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 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 map_; + struct Entry { + MPSolverInterfaceFactory factory; + std::function is_runtime_ready; + }; + std::map map_; }; } // namespace operations_research diff --git a/ortools/linear_solver/xpress_interface.cc b/ortools/linear_solver/xpress_interface.cc index 40b14845df..0e09df6053 100644 --- a/ortools/linear_solver/xpress_interface.cc +++ b/ortools/linear_solver/xpress_interface.cc @@ -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; }();