Update linear solver build (#4945)

This commit is contained in:
Guillaume Chatelet
2025-12-12 09:33:50 +01:00
committed by Corentin Le Molgat
parent 6d76575f3d
commit 69dc22f35d
30 changed files with 595 additions and 489 deletions

View File

@@ -14,6 +14,7 @@
// Integer programming example that shows how to use the API.
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
@@ -26,47 +27,40 @@
#include "ortools/linear_solver/linear_solver.h"
namespace operations_research {
void RunIntegerProgrammingExample(absl::string_view solver_id) {
void RunIntegerProgrammingExample(const std::string& solver_id) {
LOG(INFO) << "---- Integer programming example with " << solver_id << " ----";
MPSolver::OptimizationProblemType problem_type;
if (!MPSolver::ParseSolverType(solver_id, &problem_type)) {
LOG(INFO) << "Solver id " << solver_id << " not recognized";
std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver(solver_id));
if (!solver) {
LOG(INFO) << "Unable to create solver : " << solver_id;
return;
}
if (!MPSolver::SupportsProblemType(problem_type)) {
LOG(INFO) << "Supports for solver " << solver_id << " not linked in.";
return;
}
MPSolver solver("IntegerProgrammingExample", problem_type);
const double infinity = solver.infinity();
const double infinity = solver->infinity();
// x and y are integer non-negative variables.
MPVariable* const x = solver.MakeIntVar(0.0, infinity, "x");
MPVariable* const y = solver.MakeIntVar(0.0, infinity, "y");
MPVariable* const x = solver->MakeIntVar(0.0, infinity, "x");
MPVariable* const y = solver->MakeIntVar(0.0, infinity, "y");
// Maximize x + 10 * y.
MPObjective* const objective = solver.MutableObjective();
MPObjective* const objective = solver->MutableObjective();
objective->SetCoefficient(x, 1);
objective->SetCoefficient(y, 10);
objective->SetMaximization();
// x + 7 * y <= 17.5.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 17.5);
MPConstraint* const c0 = solver->MakeRowConstraint(-infinity, 17.5);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 7);
// x <= 3.5
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 3.5);
MPConstraint* const c1 = solver->MakeRowConstraint(-infinity, 3.5);
c1->SetCoefficient(x, 1);
c1->SetCoefficient(y, 0);
LOG(INFO) << "Number of variables = " << solver.NumVariables();
LOG(INFO) << "Number of constraints = " << solver.NumConstraints();
LOG(INFO) << "Number of variables = " << solver->NumVariables();
LOG(INFO) << "Number of constraints = " << solver->NumConstraints();
const MPSolver::ResultStatus result_status = solver.Solve();
const MPSolver::ResultStatus result_status = solver->Solve();
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
LOG(FATAL) << "The problem does not have an optimal solution!";
@@ -77,21 +71,33 @@ void RunIntegerProgrammingExample(absl::string_view solver_id) {
LOG(INFO) << "Optimal objective value = " << objective->Value();
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations";
LOG(INFO) << "Problem solved in " << solver.nodes()
LOG(INFO) << "Problem solved in " << solver->wall_time() << " milliseconds";
LOG(INFO) << "Problem solved in " << solver->iterations() << " iterations";
LOG(INFO) << "Problem solved in " << solver->nodes()
<< " branch-and-bound nodes";
}
void RunAllExamples() {
RunIntegerProgrammingExample("CBC");
RunIntegerProgrammingExample("SAT");
RunIntegerProgrammingExample("SCIP");
RunIntegerProgrammingExample("GUROBI");
RunIntegerProgrammingExample("GLPK");
RunIntegerProgrammingExample("CPLEX");
RunIntegerProgrammingExample("XPRESS");
RunIntegerProgrammingExample("HIGHS");
std::vector<MPSolver::OptimizationProblemType> supported_problem_types =
MPSolverInterfaceFactoryRepository::GetInstance()
->ListAllRegisteredProblemTypes();
for (MPSolver::OptimizationProblemType type : supported_problem_types) {
const std::string type_name = MPModelRequest::SolverType_Name(
static_cast<MPModelRequest::SolverType>(type));
if (!SolverTypeIsMip(type)) continue;
if (absl::StrContains(type_name, "KNAPSACK")) continue;
if (absl::StrContains(type_name, "BOP")) continue;
if (absl::StrContains(type_name, "HIGHS")) continue;
// ASAN issues a warning in CBC code which cannot be avoided for now:
// AddressSanitizer: float-cast-overflow
// third_party/cbc/Cgl/src/CglPreProcess/CglPreProcess.cpp:1717:36
#ifdef ADDRESS_SANITIZER
if (type_name.find("CBC") != std::string::npos) {
continue;
}
#endif
RunIntegerProgrammingExample(type_name);
}
}
} // namespace operations_research

View File

@@ -14,6 +14,7 @@
// Linear programming example that shows how to use the API.
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
@@ -22,71 +23,64 @@
#include "absl/log/log.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/init_google.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/linear_solver/linear_solver.pb.h"
namespace operations_research {
void RunLinearProgrammingExample(absl::string_view solver_id) {
void RunLinearProgrammingExample(const std::string& solver_id) {
LOG(INFO) << "---- Linear programming example with " << solver_id << " ----";
MPSolver::OptimizationProblemType problem_type;
if (!MPSolver::ParseSolverType(solver_id, &problem_type)) {
LOG(INFO) << "Solver id " << solver_id << " not recognized";
std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver(solver_id));
if (!solver) {
LOG(INFO) << "Unable to create solver : " << solver_id;
return;
}
if (!MPSolver::SupportsProblemType(problem_type)) {
LOG(INFO) << "Supports for solver " << solver_id << " not linked in.";
return;
}
MPSolver solver("IntegerProgrammingExample", problem_type);
const double infinity = solver.infinity();
const double infinity = solver->infinity();
// x1, x2 and x3 are continuous non-negative variables.
MPVariable* const x1 = solver.MakeNumVar(0.0, infinity, "x1");
MPVariable* const x2 = solver.MakeNumVar(0.0, infinity, "x2");
MPVariable* const x3 = solver.MakeNumVar(0.0, infinity, "x3");
MPVariable* const x1 = solver->MakeNumVar(0.0, infinity, "x1");
MPVariable* const x2 = solver->MakeNumVar(0.0, infinity, "x2");
MPVariable* const x3 = solver->MakeNumVar(0.0, infinity, "x3");
// Maximize 10 * x1 + 6 * x2 + 4 * x3.
MPObjective* const objective = solver.MutableObjective();
MPObjective* const objective = solver->MutableObjective();
objective->SetCoefficient(x1, 10);
objective->SetCoefficient(x2, 6);
objective->SetCoefficient(x3, 4);
objective->SetMaximization();
// x1 + x2 + x3 <= 100.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 100.0);
MPConstraint* const c0 = solver->MakeRowConstraint(-infinity, 100.0);
c0->SetCoefficient(x1, 1);
c0->SetCoefficient(x2, 1);
c0->SetCoefficient(x3, 1);
// 10 * x1 + 4 * x2 + 5 * x3 <= 600.
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 600.0);
MPConstraint* const c1 = solver->MakeRowConstraint(-infinity, 600.0);
c1->SetCoefficient(x1, 10);
c1->SetCoefficient(x2, 4);
c1->SetCoefficient(x3, 5);
// 2 * x1 + 2 * x2 + 6 * x3 <= 300.
MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 300.0);
MPConstraint* const c2 = solver->MakeRowConstraint(-infinity, 300.0);
c2->SetCoefficient(x1, 2);
c2->SetCoefficient(x2, 2);
c2->SetCoefficient(x3, 6);
// TODO(user): Change example to show = and >= constraints.
LOG(INFO) << "Number of variables = " << solver.NumVariables();
LOG(INFO) << "Number of constraints = " << solver.NumConstraints();
LOG(INFO) << "Number of variables = " << solver->NumVariables();
LOG(INFO) << "Number of constraints = " << solver->NumConstraints();
const MPSolver::ResultStatus result_status = solver.Solve();
const MPSolver::ResultStatus result_status = solver->Solve();
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
LOG(FATAL) << "The problem does not have an optimal solution!";
}
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
LOG(INFO) << "Problem solved in " << solver->wall_time() << " milliseconds";
// The objective value of the solution.
LOG(INFO) << "Optimal objective value = " << objective->Value();
@@ -97,11 +91,11 @@ void RunLinearProgrammingExample(absl::string_view solver_id) {
LOG(INFO) << "x3 = " << x3->solution_value();
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations";
LOG(INFO) << "Problem solved in " << solver->iterations() << " iterations";
LOG(INFO) << "x1: reduced cost = " << x1->reduced_cost();
LOG(INFO) << "x2: reduced cost = " << x2->reduced_cost();
LOG(INFO) << "x3: reduced cost = " << x3->reduced_cost();
const std::vector<double> activities = solver.ComputeConstraintActivities();
const std::vector<double> activities = solver->ComputeConstraintActivities();
LOG(INFO) << "c0: dual value = " << c0->dual_value()
<< " activity = " << activities[c0->index()];
LOG(INFO) << "c1: dual value = " << c1->dual_value()
@@ -111,14 +105,16 @@ void RunLinearProgrammingExample(absl::string_view solver_id) {
}
void RunAllExamples() {
RunLinearProgrammingExample("GLOP");
RunLinearProgrammingExample("CLP");
RunLinearProgrammingExample("GUROBI_LP");
RunLinearProgrammingExample("CPLEX_LP");
RunLinearProgrammingExample("GLPK_LP");
RunLinearProgrammingExample("XPRESS_LP");
RunLinearProgrammingExample("PDLP");
RunLinearProgrammingExample("HIGHS_LP");
std::vector<MPSolver::OptimizationProblemType> supported_problem_types =
MPSolverInterfaceFactoryRepository::GetInstance()
->ListAllRegisteredProblemTypes();
for (MPSolver::OptimizationProblemType type : supported_problem_types) {
const std::string type_name = MPModelRequest::SolverType_Name(
static_cast<MPModelRequest::SolverType>(type));
if (!absl::StrContains(type_name, "LINEAR_PROGRAMMING")) continue;
if (absl::StrContains(type_name, "HIGHS")) continue;
RunLinearProgrammingExample(type_name);
}
}
} // namespace operations_research

View File

@@ -610,36 +610,29 @@ int main(int argc, char** argv) {
operations_research::MPSolver::OptimizationProblemType solver_type;
bool found = false;
#if defined(USE_CLP)
if (absl::GetFlag(FLAGS_colgen_solver) == "clp") {
const std::string solver_name = absl::GetFlag(FLAGS_colgen_solver);
if (solver_name == "clp") {
solver_type = operations_research::MPSolver::CLP_LINEAR_PROGRAMMING;
found = true;
}
#endif // USE_CLP
if (absl::GetFlag(FLAGS_colgen_solver) == "glop") {
if (solver_name == "glop") {
solver_type = operations_research::MPSolver::GLOP_LINEAR_PROGRAMMING;
found = true;
}
#if defined(USE_XPRESS)
if (absl::GetFlag(FLAGS_colgen_solver) == "xpress") {
if (solver_name == "xpress") {
solver_type = operations_research::MPSolver::XPRESS_LINEAR_PROGRAMMING;
// solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING;
found = true;
}
#endif
#if defined(USE_CPLEX)
if (absl::GetFlag(FLAGS_colgen_solver) == "cplex") {
if (solver_name == "cplex") {
solver_type = operations_research::MPSolver::CPLEX_LINEAR_PROGRAMMING;
found = true;
}
#endif
if (!found) {
LOG(ERROR) << "Unknown solver " << absl::GetFlag(FLAGS_colgen_solver);
return 1;
LOG(ERROR) << "Unknown solver " << solver_name;
return EXIT_FAILURE;
}
LOG(INFO) << "Chosen solver: " << absl::GetFlag(FLAGS_colgen_solver)
<< std::endl;
LOG(INFO) << "Chosen solver: " << solver_name << std::endl;
if (absl::GetFlag(FLAGS_colgen_instance) == -1) {
for (int i = 0; i < operations_research::kInstanceCount; ++i) {

View File

@@ -11,36 +11,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
package(default_visibility = ["//visibility:public"])
# Description:
# Home of algorithms used in OR solvers
# OSS solvers
bool_flag(
name = "with_cbc",
build_setting_default = False,
)
config_setting(
name = "use_cbc",
flag_values = {":with_cbc": "true"},
)
bool_flag(
name = "with_scip",
build_setting_default = True,
)
config_setting(
name = "use_scip",
flag_values = {":with_scip": "true"},
)
cc_library(
name = "binary_search",
srcs = [],
@@ -190,14 +165,8 @@ cc_library(
name = "knapsack_solver_lib",
srcs = ["knapsack_solver.cc"],
hdrs = ["knapsack_solver.h"],
copts = [] + select({
":use_cbc": ["-DUSE_CBC"],
"//conditions:default": [],
}) + select({
":use_scip": ["-DUSE_SCIP"],
"//conditions:default": [],
}),
deps = [
"@abseil-cpp//absl/log",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/time",

View File

@@ -23,6 +23,7 @@
#include <vector>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "ortools/base/stl_util.h"
@@ -1071,7 +1072,7 @@ class KnapsackDivideAndConquerSolver : public BaseKnapsackSolver {
}
private:
// 'DP 2' computes solution 'z' for 0 up to capacitiy
// 'DP 2' computes solution 'z' for 0 up to capacity
void SolveSubProblem(bool first_storage, int64_t capacity, int start_item,
int end_item);
@@ -1411,30 +1412,22 @@ KnapsackSolver::KnapsackSolver(SolverType solver_type,
case KNAPSACK_DIVIDE_AND_CONQUER_SOLVER:
solver_ = std::make_unique<KnapsackDivideAndConquerSolver>(solver_name);
break;
#if defined(USE_CBC)
case KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER:
solver_ = std::make_unique<KnapsackMIPSolver>(
MPSolver::CBC_MIXED_INTEGER_PROGRAMMING, solver_name);
break;
#endif // USE_CBC
#if defined(USE_SCIP)
case KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER:
solver_ = std::make_unique<KnapsackMIPSolver>(
MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING, solver_name);
break;
#endif // USE_SCIP
#if defined(USE_XPRESS)
case KNAPSACK_MULTIDIMENSION_XPRESS_MIP_SOLVER:
solver_ = std::make_unique<KnapsackMIPSolver>(
MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING, solver_name);
break;
#endif
#if defined(USE_CPLEX)
case KNAPSACK_MULTIDIMENSION_CPLEX_MIP_SOLVER:
solver_ = std::make_unique<KnapsackMIPSolver>(
MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING, solver_name);
break;
#endif
case KNAPSACK_MULTIDIMENSION_CP_SAT_SOLVER:
solver_ = std::make_unique<KnapsackCpSat>(solver_name);
break;

View File

@@ -130,14 +130,12 @@ class KnapsackSolver {
*/
KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER = 2,
#if defined(USE_CBC)
/** CBC Based Solver
*
* This solver can deal with both large number of items and several
* dimensions. This solver is based on Integer Programming solver CBC.
*/
KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER = 3,
#endif // USE_CBC
/** Generic Solver.
*
@@ -146,32 +144,27 @@ class KnapsackSolver {
*/
KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER = 5,
#if defined(USE_SCIP)
/** SCIP based solver
*
* This solver can deal with both large number of items and several
* dimensions. This solver is based on Integer Programming solver SCIP.
*/
KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER = 6,
#endif // USE_SCIP
#if defined(USE_XPRESS)
/** XPRESS based solver
*
* This solver can deal with both large number of items and several
* dimensions. This solver is based on Integer Programming solver XPRESS.
*/
KNAPSACK_MULTIDIMENSION_XPRESS_MIP_SOLVER = 7,
#endif
#if defined(USE_CPLEX)
/** CPLEX based solver
*
* This solver can deal with both large number of items and several
* dimensions. This solver is based on Integer Programming solver CPLEX.
*/
KNAPSACK_MULTIDIMENSION_CPLEX_MIP_SOLVER = 8,
#endif
/** Divide and Conquer approach for single dimension problems
*
* Limited to one dimension, this solver is based on a divide and conquer
@@ -180,6 +173,7 @@ class KnapsackSolver {
* space complexity is O(capacity + number_of_items).
*/
KNAPSACK_DIVIDE_AND_CONQUER_SOLVER = 9,
/** CP-SAT based solver
*
* This solver can deal with both large number of items and several

View File

@@ -13,33 +13,11 @@
# Description: python wrapping of the libraries in ../
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@pip_deps//:requirements.bzl", "requirement")
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_python//python:py_test.bzl", "py_test")
# OSS solvers
bool_flag(
name = "with_cbc",
build_setting_default = False,
)
config_setting(
name = "use_cbc",
flag_values = {":with_cbc": "true"},
)
bool_flag(
name = "with_scip",
build_setting_default = True,
)
config_setting(
name = "use_scip",
flag_values = {":with_scip": "true"},
)
# knapsack_solver
cc_library(
name = "knapsack_solver_doc",
@@ -50,13 +28,6 @@ cc_library(
pybind_extension(
name = "knapsack_solver",
srcs = ["knapsack_solver.cc"],
copts = select({
":use_cbc": ["-DUSE_CBC"],
"//conditions:default": [],
}) + select({
":use_scip": ["-DUSE_SCIP"],
"//conditions:default": [],
}),
visibility = ["//visibility:public"],
deps = [
":knapsack_solver_doc",

View File

@@ -67,19 +67,15 @@ PYBIND11_MODULE(knapsack_solver, m) {
KnapsackSolver::SolverType::KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER,
DOC(operations_research, KnapsackSolver, SolverType,
KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER))
#if defined(USE_CBC)
.value("KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER",
KnapsackSolver::SolverType::KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER,
DOC(operations_research, KnapsackSolver, SolverType,
KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER))
#endif // USE_CBC
#if defined(USE_SCIP)
.value(
"KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER",
KnapsackSolver::SolverType::KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER,
DOC(operations_research, KnapsackSolver, SolverType,
KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER))
#endif // USE_SCIP
.value("KNAPSACK_DIVIDE_AND_CONQUER_SOLVER",
KnapsackSolver::SolverType::KNAPSACK_DIVIDE_AND_CONQUER_SOLVER,
DOC(operations_research, KnapsackSolver, SolverType,

View File

@@ -73,6 +73,16 @@ config_setting(
flag_values = {":with_glop": "true"},
)
bool_flag(
name = "with_gurobi",
build_setting_default = True,
)
config_setting(
name = "use_gurobi",
flag_values = {":with_gurobi": "true"},
)
bool_flag(
name = "with_glpk",
build_setting_default = False,
@@ -124,6 +134,16 @@ config_setting(
flag_values = {":with_cplex": "true"},
)
bool_flag(
name = "with_xpress",
build_setting_default = True,
)
config_setting(
name = "use_xpress",
flag_values = {":with_xpress": "true"},
)
# Linear solver proto, used for (efficient!) model storage.
proto_library(
name = "linear_solver_proto",
@@ -150,13 +170,10 @@ py_proto_library(
cc_library(
name = "linear_solver",
srcs = [
"gurobi_interface.cc",
"gurobi_util.cc",
"linear_expr.cc",
"linear_solver.cc",
"linear_solver_callback.cc",
"sat_interface.cc",
"xpress_interface.cc",
] + select({
":use_bop": ["bop_interface.cc"],
"//conditions:default": [],
@@ -175,6 +192,12 @@ cc_library(
}) + select({
":use_glpk": ["glpk_interface.cc"],
"//conditions:default": [],
}) + select({
":use_gurobi": [
"gurobi_interface.cc",
"gurobi_util.cc",
],
"//conditions:default": [],
}) + select({
":use_highs": ["highs_interface.cc"],
"//conditions:default": [],
@@ -190,15 +213,20 @@ cc_library(
}) + select({
":use_cplex": ["cplex_interface.cc"],
"//conditions:default": [],
}) + select({
":use_xpress": ["xpress_interface.cc"],
"//conditions:default": [],
}),
hdrs = [
"gurobi_util.h",
"linear_expr.h",
"linear_solver.h",
"linear_solver_callback.h",
] + select({
":use_glop": ["glop_utils.h"],
"//conditions:default": [],
}) + select({
":use_gurobi": ["gurobi_util.h"],
"//conditions:default": [],
}) + select({
":use_scip": [
"scip_callback.h",
@@ -206,34 +234,6 @@ cc_library(
],
"//conditions:default": [],
}),
copts = [] + select({
":use_bop": ["-DUSE_BOP"],
"//conditions:default": [],
}) + select({
":use_cbc": ["-DUSE_CBC"],
"//conditions:default": [],
}) + select({
":use_clp": ["-DUSE_CLP"],
"//conditions:default": [],
}) + select({
":use_glop": ["-DUSE_GLOP"],
"//conditions:default": [],
}) + select({
":use_glpk": ["-DUSE_GLPK"],
"//conditions:default": [],
}) + select({
":use_highs": ["-DUSE_HIGHS"],
"//conditions:default": [],
}) + select({
":use_pdlp": ["-DUSE_PDLP"],
"//conditions:default": [],
}) + select({
":use_scip": ["-DUSE_SCIP"],
"//conditions:default": [],
}) + select({
":use_cplex": ["-DUSE_CPLEX"],
"//conditions:default": [],
}),
deps = [
":linear_solver_cc_proto",
":model_exporter",
@@ -246,15 +246,12 @@ cc_library(
"//ortools/base:status_macros",
"//ortools/base:stl_util",
"//ortools/base:timer",
"//ortools/linear_solver/proto_solver:gurobi_proto_solver",
"//ortools/linear_solver/proto_solver:sat_proto_solver",
"//ortools/port:file",
"//ortools/port:proto_utils",
"//ortools/sat:cp_model_cc_proto",
"//ortools/sat:cp_model_solver",
"//ortools/sat:lp_utils",
"//ortools/third_party_solvers:gurobi_environment",
"//ortools/third_party_solvers:xpress_environment",
"//ortools/util:fp_utils",
"//ortools/util:lazy_mutable_copy",
"@abseil-cpp//absl/status",
@@ -281,6 +278,12 @@ cc_library(
"@glpk",
],
"//conditions:default": [],
}) + select({
":use_gurobi": [
"//ortools/linear_solver/proto_solver:gurobi_proto_solver",
"//ortools/third_party_solvers:gurobi_environment",
],
"//conditions:default": [],
}) + select({
":use_pdlp": [
"//ortools/linear_solver/proto_solver:pdlp_proto_solver",
@@ -302,7 +305,11 @@ cc_library(
"@highs",
],
"//conditions:default": [],
}) + select({
":use_xpress": ["//ortools/third_party_solvers:xpress_environment"],
"//conditions:default": [],
}),
alwayslink = 1, # Important! Library is used via dependency injection.
)
cc_library(

View File

@@ -11,24 +11,73 @@
# See the License for the specific language governing permissions and
# limitations under the License.
file(GLOB _SRCS "*.h" "*.cc")
list(REMOVE_ITEM _SRCS
${CMAKE_CURRENT_SOURCE_DIR}/solve.cc
list(APPEND _SRCS
linear_expr.cc
linear_expr.h
linear_solver_callback.cc
linear_solver_callback.h
linear_solver.cc
linear_solver.h
model_exporter_swig_helper.h
model_exporter.cc
model_exporter.h
model_validator.cc
model_validator.h
solve_mp_model.cc
solve_mp_model.h
sat_interface.cc
)
list(FILTER _SRCS EXCLUDE REGEX "/model_exporter_main\\.cc")
list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc")
if(USE_BOP)
list(APPEND _SRCS bop_interface.cc)
endif()
if(USE_COINOR)
list(APPEND _SRCS cbc_interface.cc clp_interface.cc)
endif()
if(USE_CPLEX)
list(APPEND _SRCS cplex_interface.cc)
endif()
if(USE_GLOP)
list(APPEND _SRCS glop_interface.cc glop_utils.cc glop_utils.h)
endif()
if(USE_GUROBI)
list(APPEND _SRCS gurobi_util.h gurobi_interface.cc gurobi_util.cc)
endif()
if(USE_GLPK)
list(APPEND _SRCS glpk_interface.cc)
endif()
if(USE_HIGHS)
list(APPEND _SRCS highs_interface.cc)
endif()
if(USE_PDLP)
list(APPEND _SRCS pdlp_interface.cc)
endif()
if(USE_SCIP)
list(APPEND _SRCS ${LPI_GLOP_SRC})
list(APPEND _SRCS
scip_callback.cc
scip_callback.h
scip_helper_macros.h
scip_interface.cc
${LPI_GLOP_SRC})
endif()
if(USE_XPRESS)
list(APPEND _SRCS xpress_interface.cc)
endif()
set(NAME ${PROJECT_NAME}_linear_solver)
# Will be merge in libortools.so
#add_library(${NAME} STATIC ${_SRCS})
add_library(${NAME} OBJECT ${_SRCS})
set_target_properties(${NAME} PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
if(MSVC AND BUILD_SHARED_LIBS)
target_compile_definitions(${NAME} PUBLIC "OR_BUILD_DLL")
target_compile_definitions(${NAME} PRIVATE "OR_EXPORT")
@@ -50,7 +99,6 @@ target_link_libraries(${NAME} PRIVATE
$<$<BOOL:${USE_PDLP}>:Eigen3::Eigen>
$<$<BOOL:${USE_SCIP}>:SCIP::libscip>
${PROJECT_NAMESPACE}::ortools_proto)
#add_library(${PROJECT_NAMESPACE}::linear_solver ALIAS ${NAME})
# solve
add_executable(solve)

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_BOP)
#include <atomic>
#include <cstdint>
#include <memory>
@@ -21,11 +19,6 @@
#include <vector>
#include "absl/base/attributes.h"
#include "google/protobuf/text_format.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/file.h"
#include "ortools/base/hash.h"
#include "ortools/base/helpers.h"
#include "ortools/base/logging.h"
#include "ortools/bop/bop_parameters.pb.h"
#include "ortools/bop/integral_solver.h"
@@ -387,10 +380,16 @@ void BopInterface::NonIncrementalChange() {
sync_status_ = MUST_RELOAD;
}
// Register BOP in the global linear solver factory.
MPSolverInterface* BuildBopInterface(MPSolver* const solver) {
return new BopInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterBop ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new BopInterface(solver); },
MPSolver::BOP_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_BOP)

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_CBC)
#include <cstdint>
#include <limits>
#include <memory>
@@ -23,8 +21,6 @@
#include "absl/base/attributes.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/hash.h"
#include "ortools/base/logging.h"
#include "ortools/base/timer.h"
#include "ortools/linear_solver/linear_solver.h"
@@ -529,9 +525,16 @@ void CBCInterface::SetLpAlgorithm(int value) {
SetUnsupportedIntegerParam(MPSolverParameters::LP_ALGORITHM);
}
MPSolverInterface* BuildCBCInterface(MPSolver* const solver) {
return new CBCInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterCBC ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new CBCInterface(solver); },
MPSolver::CBC_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_CBC)

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_CLP) || defined(USE_CBC)
#include <algorithm>
#include <cstdint>
#include <memory>
@@ -617,9 +615,16 @@ void CLPInterface::SetLpAlgorithm(int value) {
}
}
MPSolverInterface* BuildCLPInterface(MPSolver* const solver) {
return new CLPInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterCLP ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new CLPInterface(solver); },
MPSolver::CLP_LINEAR_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_CBC) || defined(USE_CLP)

View File

@@ -1,4 +1,4 @@
// Copyright 2014 IBM Corporation
// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -12,8 +12,6 @@
// limitations under the License.
// Initial version of this code was written by Daniel Junglas (IBM)
#if defined(USE_CPLEX)
#include <cstdint>
#include <limits>
#include <memory>
@@ -1282,11 +1280,6 @@ MPSolver::ResultStatus CplexInterface::Solve(MPSolverParameters const& param) {
sync_status_ = SOLUTION_SYNCHRONIZED;
return result_status_;
}
MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver) {
return new CplexInterface(solver, mip);
}
bool CplexInterface::SetSolverSpecificParametersAsString(
const std::string& parameters) {
if (parameters.empty()) return true;
@@ -1329,5 +1322,24 @@ bool CplexInterface::SetSolverSpecificParametersAsString(
return true;
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterCplex ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new CplexInterface(solver, false); },
MPSolver::CPLEX_LINEAR_PROGRAMMING);
return nullptr;
}();
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterCplexMip ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new CplexInterface(solver, true); },
MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_CPLEX)

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_GLOP)
#include <atomic>
#include <cstdint>
#include <memory>
@@ -35,8 +33,6 @@
#include "ortools/util/time_limit.h"
namespace operations_research {
namespace {} // Anonymous namespace
class GLOPInterface : public MPSolverInterface {
public:
explicit GLOPInterface(MPSolver* solver);
@@ -437,10 +433,16 @@ void GLOPInterface::NonIncrementalChange() {
sync_status_ = MUST_RELOAD;
}
// Register GLOP in the global linear solver factory.
MPSolverInterface* BuildGLOPInterface(MPSolver* const solver) {
return new GLOPInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterGlop ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new GLOPInterface(solver); },
MPSolver::GLOP_LINEAR_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_GLOP)

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_GLPK)
#include <algorithm>
#include <cmath>
#include <cstdint>
@@ -957,9 +955,24 @@ void GLPKInterface::SetLpAlgorithm(int value) {
}
}
MPSolverInterface* BuildGLPKInterface(bool mip, MPSolver* const solver) {
return new GLPKInterface(solver, mip);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterGLPKLP ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new GLPKInterface(solver, false); },
MPSolver::GLPK_LINEAR_PROGRAMMING);
return nullptr;
}();
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterGLPKMIP ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new GLPKInterface(solver, true); },
MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_GLPK)

View File

@@ -1407,12 +1407,28 @@ void GurobiInterface::Write(const std::string& filename) {
}
}
MPSolverInterface* BuildGurobiInterface(bool mip, MPSolver* const solver) {
return new GurobiInterface(solver, mip);
}
void GurobiInterface::SetCallback(MPCallback* mp_callback) {
callback_ = mp_callback;
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterGurobiLp ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new GurobiInterface(solver, false); },
MPSolver::GUROBI_LINEAR_PROGRAMMING);
return nullptr;
}();
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterGurobiMip ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new GurobiInterface(solver, true); },
MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_HIGHS)
#include <atomic>
#include <cstdint>
#include <optional>
@@ -288,10 +286,24 @@ void HighsInterface::NonIncrementalChange() {
sync_status_ = MUST_RELOAD;
}
// Register PDLP in the global linear solver factory.
MPSolverInterface* BuildHighsInterface(bool mip, MPSolver* const solver) {
return new HighsInterface(solver, mip);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterHighsLp ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new HighsInterface(solver, false); },
MPSolver::HIGHS_LINEAR_PROGRAMMING);
return nullptr;
}();
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterHighsMip ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* solver) { return new HighsInterface(solver, true); },
MPSolver::HIGHS_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_HIGHS)

View File

@@ -24,7 +24,6 @@
#include <vector>
#include "absl/base/attributes.h"
#include "absl/memory/memory.h"
#include "ortools/algorithms/knapsack_solver.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/util/fp_utils.h"
@@ -358,9 +357,16 @@ double KnapsackInterface::GetVariableValueFromSolution(
: 0.0;
}
// Register Knapsack solver in the global linear solver factory.
MPSolverInterface* BuildKnapsackInterface(MPSolver* const solver) {
return new KnapsackInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterKnapsack ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new KnapsackInterface(solver); },
MPSolver::KNAPSACK_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research

View File

@@ -374,100 +374,15 @@ bool MPSolver::SetSolverSpecificParametersAsString(
// ----- Solver -----
#if defined(USE_BOP)
extern MPSolverInterface* BuildBopInterface(MPSolver* const solver);
#endif
#if defined(USE_CBC)
extern MPSolverInterface* BuildCBCInterface(MPSolver* const solver);
#endif
#if defined(USE_CLP) || defined(USE_CBC)
extern MPSolverInterface* BuildCLPInterface(MPSolver* const solver);
#endif
#if defined(USE_GLOP)
extern MPSolverInterface* BuildGLOPInterface(MPSolver* const solver);
#endif
#if defined(USE_GLPK)
extern MPSolverInterface* BuildGLPKInterface(bool mip, MPSolver* const solver);
#endif
#if defined(USE_HIGHS)
extern MPSolverInterface* BuildHighsInterface(bool mip, MPSolver* const solver);
#endif
#if defined(USE_PDLP)
extern MPSolverInterface* BuildPdlpInterface(MPSolver* const solver);
#endif
extern MPSolverInterface* BuildSatInterface(MPSolver* const solver);
#if defined(USE_SCIP)
extern MPSolverInterface* BuildSCIPInterface(MPSolver* const solver);
#endif
extern MPSolverInterface* BuildGurobiInterface(bool mip,
MPSolver* const solver);
#if defined(USE_CPLEX)
extern MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver);
#endif
extern MPSolverInterface* BuildXpressInterface(bool mip,
MPSolver* const solver);
namespace {
MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
DCHECK(solver != nullptr);
switch (solver->ProblemType()) {
#if defined(USE_BOP)
case MPSolver::BOP_INTEGER_PROGRAMMING:
return BuildBopInterface(solver);
#endif
#if defined(USE_CBC)
case MPSolver::CBC_MIXED_INTEGER_PROGRAMMING:
return BuildCBCInterface(solver);
#endif
#if defined(USE_CLP) || defined(USE_CBC)
case MPSolver::CLP_LINEAR_PROGRAMMING:
return BuildCLPInterface(solver);
#endif
#if defined(USE_GLOP)
case MPSolver::GLOP_LINEAR_PROGRAMMING:
return BuildGLOPInterface(solver);
#endif
#if defined(USE_GLPK)
case MPSolver::GLPK_LINEAR_PROGRAMMING:
return BuildGLPKInterface(false, solver);
case MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING:
return BuildGLPKInterface(true, solver);
#endif
#if defined(USE_HIGHS)
case MPSolver::HIGHS_LINEAR_PROGRAMMING:
return BuildHighsInterface(false, solver);
case MPSolver::HIGHS_MIXED_INTEGER_PROGRAMMING:
return BuildHighsInterface(true, solver);
#endif
#if defined(USE_PDLP)
case MPSolver::PDLP_LINEAR_PROGRAMMING:
return BuildPdlpInterface(solver);
#endif
case MPSolver::SAT_INTEGER_PROGRAMMING:
return BuildSatInterface(solver);
#if defined(USE_SCIP)
case MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING:
return BuildSCIPInterface(solver);
#endif
case MPSolver::GUROBI_LINEAR_PROGRAMMING:
return BuildGurobiInterface(false, solver);
case MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING:
return BuildGurobiInterface(true, solver);
#if defined(USE_CPLEX)
case MPSolver::CPLEX_LINEAR_PROGRAMMING:
return BuildCplexInterface(false, solver);
case MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING:
return BuildCplexInterface(true, solver);
#endif
case MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING:
return BuildXpressInterface(true, solver);
case MPSolver::XPRESS_LINEAR_PROGRAMMING:
return BuildXpressInterface(false, solver);
default:
// TODO(user): Revert to the best *available* interface.
LOG(FATAL) << "Linear solver not recognized.";
}
return nullptr;
MPSolverInterface* interface =
MPSolverInterfaceFactoryRepository::GetInstance()->Create(solver);
QCHECK(interface != nullptr)
<< "Unsupported problem type: '" << solver->ProblemType()
<< "'. Did you forget to link the library for this solver?";
return interface;
}
} // namespace
@@ -502,53 +417,21 @@ extern bool XpressIsCorrectlyInstalled();
// static
bool MPSolver::SupportsProblemType(OptimizationProblemType problem_type) {
#ifdef USE_BOP
if (problem_type == BOP_INTEGER_PROGRAMMING) return true;
#endif
#ifdef USE_CBC
if (problem_type == CBC_MIXED_INTEGER_PROGRAMMING) return true;
#endif
#ifdef USE_CLP
if (problem_type == CLP_LINEAR_PROGRAMMING) return true;
#endif
#ifdef USE_GLOP
if (problem_type == GLOP_LINEAR_PROGRAMMING) return true;
#endif
#ifdef USE_GLPK
if (problem_type == GLPK_LINEAR_PROGRAMMING ||
problem_type == GLPK_MIXED_INTEGER_PROGRAMMING) {
return true;
if (!MPSolverInterfaceFactoryRepository::GetInstance()->Supports(
problem_type)) {
return false;
}
#endif
#ifdef USE_HIGHS
if (problem_type == HIGHS_MIXED_INTEGER_PROGRAMMING ||
problem_type == HIGHS_LINEAR_PROGRAMMING) {
return true;
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;
}
#endif
#ifdef USE_PDLP
if (problem_type == PDLP_LINEAR_PROGRAMMING) return true;
#endif
if (problem_type == GUROBI_LINEAR_PROGRAMMING ||
problem_type == GUROBI_MIXED_INTEGER_PROGRAMMING) {
return GurobiIsCorrectlyInstalled();
}
if (problem_type == SAT_INTEGER_PROGRAMMING) return true;
#ifdef USE_SCIP
if (problem_type == SCIP_MIXED_INTEGER_PROGRAMMING) return true;
#endif
#ifdef USE_CPLEX
if (problem_type == CPLEX_LINEAR_PROGRAMMING ||
problem_type == CPLEX_MIXED_INTEGER_PROGRAMMING) {
return true;
}
#endif
if (problem_type == XPRESS_MIXED_INTEGER_PROGRAMMING ||
problem_type == XPRESS_LINEAR_PROGRAMMING) {
return XpressIsCorrectlyInstalled();
}
return false;
return true;
}
// TODO(user): post c++ 14, instead use
@@ -740,7 +623,7 @@ class MPVariableNamesIterator {
int index_ = 0;
};
// Iterates over all the constraint and general_constaint names. See usage.
// Iterates over all the constraint and general_constraint names. See usage.
class MPConstraintNamesIterator {
public:
explicit MPConstraintNamesIterator(const MPModelProto& model)
@@ -2339,4 +2222,68 @@ std::string MPSolver::GetMPModelRequestLoggingInfo(
return out;
}
MPSolverInterfaceFactoryRepository*
MPSolverInterfaceFactoryRepository::GetInstance() {
static auto* const kInstance = new MPSolverInterfaceFactoryRepository();
return kInstance;
}
// This can't be covered by unit test coverage framework, because the Singleton
// destruction occurs after it finished collecting coverage.
// COV_NF_START
MPSolverInterfaceFactoryRepository::~MPSolverInterfaceFactoryRepository() {
absl::MutexLock lock(mutex_);
map_.clear();
}
// COV_NF_END
void MPSolverInterfaceFactoryRepository::Register(
MPSolverInterfaceFactory factory,
MPSolver::OptimizationProblemType problem_type) {
absl::MutexLock lock(mutex_);
map_[problem_type] = std::move(factory);
}
bool MPSolverInterfaceFactoryRepository::Unregister(
MPSolver::OptimizationProblemType problem_type) {
absl::MutexLock lock(mutex_);
return map_.erase(problem_type) == 1;
}
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);
}
bool MPSolverInterfaceFactoryRepository::Supports(
MPSolver::OptimizationProblemType problem_type) const {
return map_.count(problem_type) > 0;
}
std::vector<MPSolver::OptimizationProblemType>
MPSolverInterfaceFactoryRepository::ListAllRegisteredProblemTypes() const {
std::vector<MPSolver::OptimizationProblemType> out;
for (auto it = map_.begin(); it != map_.end(); ++it) {
out.push_back(it->first);
}
return out;
}
std::string MPSolverInterfaceFactoryRepository // NOLINT
::PrettyPrintAllRegisteredProblemTypes() const {
std::string out;
for (auto it = map_.begin(); it != map_.end(); ++it) {
out += ProtoEnumToString<MPModelRequest::SolverType>(
static_cast<MPModelRequest::SolverType>(it->first)) +
"\n";
}
return out;
}
} // namespace operations_research

View File

@@ -157,7 +157,6 @@
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/optional.h"
#include "ortools/base/base_export.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_expr.h"
@@ -1231,23 +1230,23 @@ class MPVariable {
void SetBranchingPriority(int priority);
protected:
friend class MPSolver;
friend class MPSolverInterface;
friend class BopInterface;
friend class CBCInterface;
friend class CLPInterface;
friend class GLPKInterface;
friend class SCIPInterface;
friend class SLMInterface;
friend class GurobiInterface;
friend class CplexInterface;
friend class XpressInterface;
friend class GLOPInterface;
friend class MPVariableSolutionValueTest;
friend class BopInterface;
friend class SatInterface;
friend class PdlpInterface;
friend class GLPKInterface;
friend class GurobiInterface;
friend class HighsInterface;
friend class KnapsackInterface;
friend class MPSolver;
friend class MPSolverInterface;
friend class MPVariableSolutionValueTest;
friend class PdlpInterface;
friend class SatInterface;
friend class SCIPInterface;
friend class SLMInterface;
friend class XpressInterface;
// Constructor. A variable points to a single MPSolverInterface that
// is specified in the constructor. A variable cannot belong to
@@ -1929,6 +1928,72 @@ class MPSolverInterface {
virtual void SetLpAlgorithm(int value) = 0;
};
// Handy type name for callbacks that create a fresh MPSolverInterface tied
// to the given MPSolver. The underlying callback should *always* be permanent.
typedef std::function<MPSolverInterface*(MPSolver*)> MPSolverInterfaceFactory;
// This class must be instantiated only once, through GetInstance(). It is
// thread-safe.
//
// - To register an existing MPSolverInterface in the global repository:
//
// MPSolverInterfaceFactory cbc_solver_interface_factory =
// CreateCBCInterface;
// MPSolverInterfaceFactoryRepository::GetInstance()->Register(
// cbc_solver_interface_factory, CBC_MIXED_INTEGER_PROGRAMMING);
//
// - To get the MPSolverInterfaceFactory associated to a given problem type:
//
// MPSolverInterface* my_solver_interface =
// MPSolverInterfaceFactoryRepository::GetInstance()->Create(
// CBC_MIXED_INTEGER_PROGRAMMING);
// CHECK(my_solver_interface != NULL) << "CBC not supported.";
//
// The implementations of MPSolverInterface defined here (e.g. ScipInterface)
// are registered at with the MPSolverInterfaceFactoryRepository Singleton
// automatically at link time, as long as the cc file where they are defined
// (e.g. scip_interface.cc) is included in your binary (i.e. you have a
// transitive dependency on the build rule ":linear_solver_scip", which sets
// alwayslink=1).
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.
void Register(MPSolverInterfaceFactory factory,
MPSolver::OptimizationProblemType problem_type);
// Invokes the factory associated to the given solver's problem type,
// or return NULL if no factory was found for it.
MPSolverInterface* Create(MPSolver* solver) const;
// Whether the implementation associated to the given problem type is
// available.
bool Supports(MPSolver::OptimizationProblemType problem_type) const;
// List all the problem types.
std::vector<MPSolver::OptimizationProblemType> ListAllRegisteredProblemTypes()
const;
// Returns a human-readable list of supported OptimizationProblemType.
std::string PrettyPrintAllRegisteredProblemTypes() const;
// FOR TESTING ONLY.
bool Unregister(MPSolver::OptimizationProblemType problem_type);
private:
// The constructor / destructor are private to prevent this class from ever
// being instantiated outside GetInstance().
// TODO(user): consider adding the GLOP factory by default, and then expose
// it via a CreateDefault() method.
MPSolverInterfaceFactoryRepository() = default;
~MPSolverInterfaceFactoryRepository();
mutable absl::Mutex mutex_;
std::map<MPSolver::OptimizationProblemType, MPSolverInterfaceFactory> map_;
};
} // namespace operations_research
#endif // ORTOOLS_LINEAR_SOLVER_LINEAR_SOLVER_H_

View File

@@ -11,11 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_PDLP)
#include <atomic>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -24,7 +21,6 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/optional.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/linear_solver/linear_solver.pb.h"
@@ -310,10 +306,16 @@ void PdlpInterface::NonIncrementalChange() {
sync_status_ = MUST_RELOAD;
}
// Register PDLP in the global linear solver factory.
MPSolverInterface* BuildPdlpInterface(MPSolver* const solver) {
return new PdlpInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterPdlp ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new PdlpInterface(solver); },
MPSolver::PDLP_LINEAR_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_PDLP)

View File

@@ -11,33 +11,54 @@
# See the License for the specific language governing permissions and
# limitations under the License.
file(GLOB _SRCS "*.h" "*.cc")
if(NOT USE_COINOR)
list(FILTER _SRCS EXCLUDE REGEX "/clp_proto_solver.")
list(FILTER _SRCS EXCLUDE REGEX "/cbc_proto_solver.")
list(APPEND _SRCS
preprocessor.cc
preprocessor.h
proto_utils.h
sat_proto_solver.cc
sat_proto_solver.h
sat_solver_utils.cc
sat_solver_utils.h
)
if(USE_GLOP)
list(APPEND _SRCS
glop_proto_solver.cc
glop_proto_solver.h
)
endif()
if(NOT USE_GLPK)
list(FILTER _SRCS EXCLUDE REGEX "/glpk_proto_solver.")
if(USE_GUROBI)
list(APPEND _SRCS
gurobi_proto_solver.cc
gurobi_proto_solver.h
)
endif()
if(NOT USE_HIGHS)
list(FILTER _SRCS EXCLUDE REGEX "/highs_proto_solver.")
if(USE_HIGHS)
list(APPEND _SRCS
highs_proto_solver.cc
highs_proto_solver.h
)
endif()
if(NOT USE_PDLP)
list(FILTER _SRCS EXCLUDE REGEX "/pdlp_proto_solver.")
if(USE_PDLP)
list(APPEND _SRCS
pdlp_proto_solver.cc
pdlp_proto_solver.h
)
endif()
if(NOT USE_SCIP)
list(FILTER _SRCS EXCLUDE REGEX "/scip_proto_solver.")
list(FILTER _SRCS EXCLUDE REGEX "/scip_params.")
if(USE_SCIP)
list(APPEND _SRCS
scip_params.cc
scip_params.h
scip_proto_solver.cc
scip_proto_solver.h
)
endif()
set(NAME ${PROJECT_NAME}_linear_solver_proto_solver)
# Will be merge in libortools.so
#add_library(${NAME} STATIC ${_SRCS})
add_library(${NAME} OBJECT ${_SRCS})
set_target_properties(${NAME} PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(${NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>)
@@ -50,4 +71,3 @@ target_link_libraries(${NAME} PRIVATE
$<$<BOOL:${USE_SCIP}>:SCIP::libscip>
$<$<BOOL:${USE_HIGHS}>:highs::highs>
${PROJECT_NAMESPACE}::ortools_proto)
#add_library(${PROJECT_NAMESPACE}::linear_solver_proto_solver ALIAS ${NAME})

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_SCIP)
#include "ortools/linear_solver/proto_solver/scip_proto_solver.h"
#include <algorithm>
@@ -1011,5 +1009,3 @@ absl::StatusOr<MPSolutionResponse> ScipSolveProto(
}
} // namespace operations_research
#endif // #if defined(USE_SCIP)

View File

@@ -13,14 +13,12 @@
#include <atomic>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/base/attributes.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/linear_solver/linear_solver.pb.h"
@@ -271,9 +269,16 @@ void SatInterface::NonIncrementalChange() {
sync_status_ = MUST_RELOAD;
}
// Register Sat in the global linear solver factory.
MPSolverInterface* BuildSatInterface(MPSolver* const solver) {
return new SatInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterSat ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new SatInterface(solver); },
MPSolver::SAT_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_SCIP)
#include "ortools/linear_solver/scip_callback.h"
#include <algorithm>
@@ -22,9 +20,9 @@
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/linear_solver/scip_helper_macros.h"
#include "scip/cons_linear.h"
#include "scip/def.h"
@@ -459,4 +457,3 @@ void AddCallbackConstraintImpl(SCIP* scip, const std::string& handler_name,
} // namespace internal
} // namespace operations_research
#endif // #if defined(USE_SCIP)

View File

@@ -11,8 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#if defined(USE_SCIP)
#include <stddef.h>
#include <algorithm>
@@ -1165,12 +1163,19 @@ void SCIPInterface::SetCallback(MPCallback* mp_callback) {
callback_ = mp_callback;
}
MPSolverInterface* BuildSCIPInterface(MPSolver* const solver) {
return new SCIPInterface(solver);
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterSCIP ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new SCIPInterface(solver); },
MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research
#endif // #if defined(USE_SCIP)
#undef RETURN_AND_STORE_IF_SCIP_ERROR
#undef RETURN_IF_ALREADY_IN_ERROR_STATE

View File

@@ -15,11 +15,8 @@ file(GLOB _SRCS "*.h" "*.cc")
set(NAME ${PROJECT_NAME}_linear_solver_wrappers)
# Will be merge in libortools.so
#add_library(${NAME} STATIC ${_SRCS})
add_library(${NAME} OBJECT ${_SRCS})
set_target_properties(${NAME} PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(${NAME} PRIVATE
${PROJECT_SOURCE_DIR}
${PROJECT_BINARY_DIR})
@@ -27,4 +24,3 @@ target_link_libraries(${NAME} PRIVATE
absl::status
$<$<BOOL:${USE_SCIP}>:SCIP::libscip>
${PROJECT_NAMESPACE}::ortools_proto)
#add_library(${PROJECT_NAMESPACE}::linear_solver_wrappers ALIAS ${NAME})

View File

@@ -1,4 +1,4 @@
// Copyright 2019-2023 RTE
// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -2098,10 +2098,6 @@ void XpressInterface::Write(const std::string& filename) {
LOG(ERROR) << "Xpress: Failed to write MPS!";
}
}
MPSolverInterface* BuildXpressInterface(bool mip, MPSolver* const solver) {
return new XpressInterface(solver, mip);
}
// TODO useless ?
template <class Container>
void splitMyString(const std::string& str, Container& cont, char delim = ' ') {
@@ -2287,4 +2283,24 @@ double XpressMPCallbackContext::SuggestSolution(
return NAN;
}
namespace {
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterXpress ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new XpressInterface(solver, false); },
MPSolver::XPRESS_LINEAR_PROGRAMMING);
return nullptr;
}();
// See MpSolverInterfaceFactoryRepository for details.
const void* const kRegisterXpressMip ABSL_ATTRIBUTE_UNUSED = [] {
MPSolverInterfaceFactoryRepository::GetInstance()->Register(
[](MPSolver* const solver) { return new XpressInterface(solver, true); },
MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING);
return nullptr;
}();
} // namespace
} // namespace operations_research

View File

@@ -15,19 +15,36 @@ if(NOT USE_PDLP)
return()
endif()
file(GLOB _SRCS "*.h" "*.cc")
list(FILTER _SRCS EXCLUDE REGEX "/[^/]*_test\\.cc$")
list(FILTER _SRCS EXCLUDE REGEX "/gtest[^/]*$")
list(FILTER _SRCS EXCLUDE REGEX "/test[^/]*$")
list(APPEND _SRCS
iteration_stats.cc
iteration_stats.h
primal_dual_hybrid_gradient.cc
primal_dual_hybrid_gradient.h
quadratic_program_io.cc
quadratic_program_io.h
quadratic_program.cc
quadratic_program.h
scheduler.cc
scheduler.h
sharded_optimization_utils.cc
sharded_optimization_utils.h
sharded_quadratic_program.cc
sharded_quadratic_program.h
sharder.cc
sharder.h
solvers_proto_validation.cc
solvers_proto_validation.h
termination.cc
termination.h
trust_region.cc
trust_region.h
)
set(NAME ${PROJECT_NAME}_pdlp)
# Will be merge in libortools.so
#add_library(${NAME} STATIC ${_SRCS})
add_library(${NAME} OBJECT ${_SRCS})
set_target_properties(${NAME} PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(${NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>)
@@ -37,4 +54,3 @@ target_link_libraries(${NAME} PRIVATE
absl::str_format
Eigen3::Eigen
${PROJECT_NAMESPACE}::ortools_proto)
#add_library(${PROJECT_NAMESPACE}::pdlp ALIAS ${NAME})