diff --git a/CMakeLists.txt b/CMakeLists.txt
index 85d7261e9b..1457bcd559 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -274,8 +274,8 @@ option(USE_CPLEX "Use the CPLEX solver" OFF)
message(STATUS "CPLEX support: ${USE_CPLEX}")
## XPRESS
-option(USE_XPRESS "Use the XPRESS solver" OFF)
-message(STATUS "XPRESS support: ${USE_XPRESS}")
+# Since it is dynamicaly loaded upon use, OFF is currently not supported.
+CMAKE_DEPENDENT_OPTION(USE_XPRESS "Use the Xpress solver" ON "BUILD_CXX" OFF)
# Language specific options
if(BUILD_CXX)
diff --git a/cmake/FindXPRESS.cmake b/cmake/FindXPRESS.cmake
deleted file mode 100644
index a7678366af..0000000000
--- a/cmake/FindXPRESS.cmake
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright 2010-2022 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
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#[=======================================================================[.rst:
-FindXPRESS
---------
-
-This module determines the XPRESS library of the system.
-
-IMPORTED Targets
-^^^^^^^^^^^^^^^^
-
-This module defines :prop_tgt:`IMPORTED` target ``XPRESS::XPRESS``, if
-XPRESS has been found.
-
-Result Variables
-^^^^^^^^^^^^^^^^
-
-This module defines the following variables:
-
-::
-
-XPRESS_FOUND - True if XPRESS found.
-
-Hints
-^^^^^
-
-A user may set ``XPRESS_ROOT`` to a XPRESS installation root to tell this
-module where to look.
-#]=======================================================================]
-set(XPRESS_FOUND FALSE)
-
-if(CMAKE_C_COMPILER_LOADED)
- include (CheckIncludeFile)
- include (CheckCSourceCompiles)
-elseif(CMAKE_CXX_COMPILER_LOADED)
- include (CheckIncludeFileCXX)
- include (CheckCXXSourceCompiles)
-else()
- message(FATAL_ERROR "FindXPRESS only works if either C or CXX language is enabled")
-endif()
-
-if(NOT XPRESS_ROOT)
- set(XPRESS_ROOT $ENV{XPRESS_ROOT})
-endif()
-message(STATUS "XPRESS_ROOT: ${XPRESS_ROOT}")
-if(NOT XPRESS_ROOT)
- message(FATAL_ERROR "XPRESS_ROOT: not found")
-else()
- set(XPRESS_FOUND TRUE)
-endif()
-
-if(XPRESS_FOUND AND NOT TARGET XPRESS::XPRESS)
- add_library(XPRESS::XPRESS UNKNOWN IMPORTED)
-
- if(UNIX)
- target_include_directories(XPRESS::XPRESS SYSTEM INTERFACE "${XPRESS_ROOT}/include")
- endif()
-
- if(APPLE) # be aware that `UNIX` is `TRUE` on OS X, so this check must be first
- set_target_properties(XPRESS::XPRESS PROPERTIES
- #INSTALL_RPATH_USE_LINK_PATH TRUE
- #BUILD_WITH_INSTALL_RPATH TRUE
- #INTERFACE_LINK_DIRECTORIES "${XPRESS_ROOT}/lib"
- #INSTALL_RPATH "${XPRESS_ROOT}/lib;${INSTALL_RPATH}"
- IMPORTED_LOCATION "${XPRESS_ROOT}/lib/libxprs.dylib")
- elseif(UNIX)
- set_target_properties(XPRESS::XPRESS PROPERTIES
- INTERFACE_LINK_DIRECTORIES "${XPRESS_ROOT}/lib"
- IMPORTED_LOCATION ${XPRESS_ROOT}/lib/libxprs.so)
- elseif(MSVC)
- set_target_properties(XPRESS::XPRESS PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${XPRESS_ROOT}\\include"
- IMPORTED_LOCATION "${XPRESS_ROOT}\\lib\\xprs.lib")
- else()
- message(FATAL_ERROR "XPRESS not supported for ${CMAKE_SYSTEM}")
- endif()
-endif()
diff --git a/cmake/README.md b/cmake/README.md
index 76c7dcca4c..4d09125fcc 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -91,7 +91,7 @@ Here the list of supported solvers:
* HiGHS\*
* PDLP
* SCIP
-* XPRESS\*
+* XPRESS
\*: these solvers are disabled by default.
@@ -127,7 +127,6 @@ support for the following third-party solvers:
note: You must enable the support of GLPK solver by using `-DUSE_GLPK=ON`
(`OFF` by default).
* CPLEX (`USE_CPLEX`),
-* XPRESS (`USE_XPRESS`)
**warning: Since these solvers are either proprietary (and require a specific
license) or available under the GPL, we can't test them on public CI and their
@@ -145,14 +144,6 @@ For ease of migration from legacy `make third_party` builds, CMake will also
read the CPLEX installation path from the `UNIX_CPLEX_DIR` environment variable,
if defined.
-### Enabling XPRESS Support
-
-To enable XPRESS support, configure with `-DUSE_XPRESS=ON` and
-`-DXPRESS_ROOT=/absolute/path/to/XPRESS/root/dir`, replacing
-`/absolute/path/to/XPRESS/root/dir` with the path to your XPRESS installation.
-`XPRESS_ROOT` can also be defined as an environment variable rather than an
-option at configure time.
-
## CMake Options
There are several options that can be passed to CMake to modify how the code
@@ -217,8 +208,6 @@ cmake -S. -Bbuild -LH
| | | |
| `USE_CPLEX` | OFF | Enable CPLEX support |
| | | |
-| `USE_XPRESS` | OFF | Enable XPRESS support |
-| | | |
| `BUILD_DOC` | OFF\* | Build all documentations |
| `BUILD_CXX_DOC` | OFF\* | Build C++ documentation
**Forced** to ON if `BUILD_DOC=ON` |
| `BUILD_DOTNET_DOC` | OFF\* | Build .Net documentation
**Forced** to ON if `BUILD_DOC=ON` |
diff --git a/cmake/cpp.cmake b/cmake/cpp.cmake
index 8ea55a8854..78b19b9a48 100644
--- a/cmake/cpp.cmake
+++ b/cmake/cpp.cmake
@@ -66,14 +66,6 @@ endif()
if(USE_CPLEX)
list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "USE_CPLEX")
endif()
-if(USE_XPRESS)
- list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "USE_XPRESS")
- if(MSVC)
- list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "XPRESS_PATH=\"${XPRESS_ROOT}\"")
- else()
- list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "XPRESS_PATH=${XPRESS_ROOT}")
- endif()
-endif()
if(WIN32)
list(APPEND OR_TOOLS_COMPILE_DEFINITIONS "__WIN32__")
@@ -327,7 +319,8 @@ foreach(SUBPROJECT IN ITEMS
port
sat
scheduling
- util)
+ util
+ xpress)
add_subdirectory(ortools/${SUBPROJECT})
#target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_${SUBPROJECT})
target_sources(${PROJECT_NAME} PRIVATE $)
@@ -365,7 +358,6 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
$<$:HIGHS::HIGHS>
${PDLP_DEPS}
$<$:libscip>
- $<$:XPRESS::XPRESS>
Threads::Threads)
if(WIN32)
target_link_libraries(${PROJECT_NAME} PUBLIC psapi.lib ws2_32.lib)
diff --git a/cmake/deps.cmake b/cmake/deps.cmake
index 918649d56f..4b4579abce 100644
--- a/cmake/deps.cmake
+++ b/cmake/deps.cmake
@@ -145,10 +145,6 @@ if(USE_CPLEX)
find_package(CPLEX REQUIRED)
endif()
-if(USE_XPRESS)
- find_package(XPRESS REQUIRED)
-endif()
-
# Check language Dependencies
if(BUILD_PYTHON)
if(NOT BUILD_pybind11)
diff --git a/examples/cpp/integer_programming.cc b/examples/cpp/integer_programming.cc
index 86400484f4..58b22fb332 100644
--- a/examples/cpp/integer_programming.cc
+++ b/examples/cpp/integer_programming.cc
@@ -87,6 +87,7 @@ void RunAllExamples() {
RunIntegerProgrammingExample("GUROBI");
RunIntegerProgrammingExample("GLPK");
RunIntegerProgrammingExample("CPLEX");
+ RunIntegerProgrammingExample("XPRESS");
}
} // namespace operations_research
diff --git a/examples/cpp/xpress_use.cc b/examples/cpp/xpress_use.cc
new file mode 100644
index 0000000000..600918e762
--- /dev/null
+++ b/examples/cpp/xpress_use.cc
@@ -0,0 +1,104 @@
+// Copyright Artelys for RTE.
+// 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
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This example that shows how to use Xpress Solver.
+
+#include
+
+#include "ortools/base/commandlineflags.h"
+#include "ortools/base/init_google.h"
+#include "ortools/base/logging.h"
+#include "ortools/linear_solver/linear_solver.h"
+
+using namespace operations_research;
+
+/**
+ * This method shows two ways to initialize a Xpress solver instance.
+ * Two environment variables are used to specify the Xpress installation paths:
+ * * XPRESSDIR : Path to the Xpress root directory (containing bin and lib folders)
+ * * XPRESS : Path to the directory containing Xpress license
+ */
+void useXpressSolver(bool solveAsMip, bool useFactory) {
+ std::unique_ptr solver = nullptr;
+ if (useFactory) {
+ /* This is the preferred way as the program won't stop if anything went
+ wrong. In such a case, `solver` will take value `nullptr` */
+ std::string xpressName = (solveAsMip ? "XPRESS" : "XPRESS_LP");
+ solver.reset(MPSolver::CreateSolver(xpressName));
+ } else {
+ MPSolver::OptimizationProblemType problemType = (solveAsMip ?
+ MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING
+ : MPSolver::XPRESS_LINEAR_PROGRAMMING);
+ /* MPSolver::SupportsProblemType(problem_type) will test if Xpress is
+ correctly loaded and has a valid license. This check is important to keep
+ the program running if Xpress is not correctly installed. With the
+ constructor usage, if Xpress is badly loaded or if there is a problem
+ with the license, the program will abort (SIGABRT)
+ */
+ if (MPSolver::SupportsProblemType(problemType)) {
+ solver.reset(new MPSolver("IntegerProgrammingExample", problemType));
+ }
+ }
+ if (solver == nullptr) {
+ LOG(WARNING) << "Xpress solver is not available";
+ return;
+ }
+ // Use the solver
+ /*
+ max -100 x1 + 10 x2
+ s.t. x2 <= 20 x1;
+ 30 x1 + 3.5 x2 <= 350
+ 0 <= x1 <= 5
+ 0 <= x2
+ */
+ const double infinity = MPSolver::infinity();
+ const MPVariable* x1 = solver->MakeIntVar(0, 5, "x1");
+ const MPVariable* x2 = solver->MakeNumVar(0.0, infinity, "x2");
+
+ MPObjective* const objective = solver->MutableObjective();
+ objective->SetCoefficient(x1, -100);
+ objective->SetCoefficient(x2, 10);
+ objective->SetMaximization();
+
+ MPConstraint* const c0 = solver->MakeRowConstraint(-infinity, 0.0);
+ c0->SetCoefficient(x1, -20.0);
+ c0->SetCoefficient(x2, 1);
+
+ MPConstraint* const c1 = solver->MakeRowConstraint(-infinity, 350.0);
+ c1->SetCoefficient(x1, 30.0);
+ c1->SetCoefficient(x2, 3.5);
+
+
+ const MPSolver::ResultStatus result_status = solver->Solve();
+
+ // Check that the problem has an optimal solution.
+ if (result_status != MPSolver::OPTIMAL) {
+ LOG(FATAL) << "Solver returned with non-optimal status.";
+ } else {
+ LOG(WARNING) << "Optimal solution found: obj=" << objective->Value();
+ }
+}
+#define ABSL_MIN_LOG_LEVEL INFO;
+int main(int argc, char** argv) {
+ absl::SetFlag(&FLAGS_stderrthreshold, 0);
+ absl::SetFlag(&FLAGS_logtostderr, true);
+ InitGoogle(argv[0], &argc, &argv, true);
+ std::cout << "start\n";
+ LOG(WARNING) << "start";
+ for (bool solveAsMip: {true, false}) {
+ for (bool useFactory: {true, false}) {
+ useXpressSolver(solveAsMip, useFactory);
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/examples/dotnet/csintegerprogramming.cs b/examples/dotnet/csintegerprogramming.cs
index d3e1041e99..ee61c75c12 100644
--- a/examples/dotnet/csintegerprogramming.cs
+++ b/examples/dotnet/csintegerprogramming.cs
@@ -117,6 +117,8 @@ public class CsIntegerProgramming
RunIntegerProgrammingExample("SAT");
Console.WriteLine("---- Linear programming example with GUROBI ----");
RunIntegerProgrammingExample("GUROBI");
+ Console.WriteLine("---- Linear programming example with XPRESS ----");
+ RunIntegerProgrammingExample("XPRESS");
Console.WriteLine("---- Integer programming example (Natural API) with GLPK ----");
RunIntegerProgrammingExampleNaturalApi("GLPK");
Console.WriteLine("---- Linear programming example (Natural API) with CBC ----");
@@ -127,5 +129,7 @@ public class CsIntegerProgramming
RunIntegerProgrammingExampleNaturalApi("SAT");
Console.WriteLine("---- Linear programming example (Natural API) with GUROBI ----");
RunIntegerProgrammingExampleNaturalApi("GUROBI");
+ Console.WriteLine("---- Linear programming example (Natural API) with XPRESS ----");
+ RunIntegerProgrammingExampleNaturalApi("XPRESS");
}
}
diff --git a/examples/dotnet/cslinearprogramming.cs b/examples/dotnet/cslinearprogramming.cs
index c3f2188db0..3872550cc8 100644
--- a/examples/dotnet/cslinearprogramming.cs
+++ b/examples/dotnet/cslinearprogramming.cs
@@ -160,6 +160,7 @@ public class CsLinearProgramming
RunLinearProgrammingExample("GLOP");
RunLinearProgrammingExample("GLPK_LP");
RunLinearProgrammingExample("CLP");
+ RunLinearProgrammingExample("XPRESS_LP");
RunLinearProgrammingExampleNaturalApi("GLOP", true);
RunLinearProgrammingExampleNaturalApi("GLPK_LP", false);
diff --git a/examples/java/IntegerProgramming.java b/examples/java/IntegerProgramming.java
index 6e5ede6ee8..7e730f48f8 100644
--- a/examples/java/IntegerProgramming.java
+++ b/examples/java/IntegerProgramming.java
@@ -84,5 +84,7 @@ public class IntegerProgramming {
runIntegerProgrammingExample("GLPK");
System.out.println("---- Integer programming example with CP-SAT ----");
runIntegerProgrammingExample("SAT");
+ System.out.println("---- Integer programming example with XPRESS ----");
+ runIntegerProgrammingExample("XPRESS");
}
}
diff --git a/examples/java/LinearProgramming.java b/examples/java/LinearProgramming.java
index 8457d77435..34749e4046 100644
--- a/examples/java/LinearProgramming.java
+++ b/examples/java/LinearProgramming.java
@@ -116,5 +116,7 @@ public class LinearProgramming {
runLinearProgrammingExample("GLOP", true);
System.out.println("---- Linear programming example with CLP ----");
runLinearProgrammingExample("CLP", false);
+ System.out.println("---- Linear programming example with XPRESS ----");
+ runLinearProgrammingExample("XPRESS_LP", false);
}
}
diff --git a/examples/python/integer_programming.py b/examples/python/integer_programming.py
index 21d62a1985..c61f90258d 100755
--- a/examples/python/integer_programming.py
+++ b/examples/python/integer_programming.py
@@ -102,6 +102,7 @@ def RunAllIntegerExampleNaturalLanguageAPI():
# RunIntegerExampleNaturalLanguageAPI('CBC')
RunIntegerExampleNaturalLanguageAPI("SCIP")
RunIntegerExampleNaturalLanguageAPI("SAT")
+ RunIntegerExampleNaturalLanguageAPI("XPRESS")
def RunAllIntegerExampleCppStyleAPI():
@@ -110,6 +111,7 @@ def RunAllIntegerExampleCppStyleAPI():
# RunIntegerExampleCppStyleAPI('CBC')
RunIntegerExampleCppStyleAPI("SCIP")
RunIntegerExampleCppStyleAPI("SAT")
+ RunIntegerExampleCppStyleAPI("XPRESS")
def main():
diff --git a/examples/python/linear_programming.py b/examples/python/linear_programming.py
index 291ebe4f59..6b383360df 100644
--- a/examples/python/linear_programming.py
+++ b/examples/python/linear_programming.py
@@ -138,11 +138,13 @@ def main():
RunLinearExampleNaturalLanguageAPI("GLPK_LP")
RunLinearExampleNaturalLanguageAPI("CLP")
RunLinearExampleNaturalLanguageAPI("PDLP")
+ RunLinearExampleNaturalLanguageAPI("XPRESS_LP")
RunLinearExampleCppStyleAPI("GLOP")
RunLinearExampleCppStyleAPI("GLPK_LP")
RunLinearExampleCppStyleAPI("CLP")
RunLinearExampleCppStyleAPI("PDLP")
+ RunLinearExampleCppStyleAPI("XPRESS_LP")
if __name__ == "__main__":
diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk
index f8b73d0c38..1074477385 100644
--- a/makefiles/Makefile.cpp.mk
+++ b/makefiles/Makefile.cpp.mk
@@ -39,7 +39,6 @@ USE_HIGHS ?= OFF
USE_PDLP := ON # OFF not supported
USE_SCIP ?= ON
USE_CPLEX ?= OFF
-USE_XPRESS ?= OFF
USE_DOTNET_CORE_31 ?= OFF
USE_DOTNET_6 ?= ON
@@ -72,7 +71,6 @@ third_party:
-DUSE_PDLP=$(USE_PDLP) \
-DUSE_SCIP=$(USE_SCIP) \
-DUSE_CPLEX=$(USE_CPLEX) \
- -DUSE_XPRESS=$(USE_XPRESS) \
-DUSE_DOTNET_CORE_31=$(USE_DOTNET_CORE_31) \
-DUSE_DOTNET_6=$(USE_DOTNET_6) \
-DBUILD_VENV=$(BUILD_VENV) \
@@ -665,7 +663,6 @@ detect_cpp:
@echo USE_SCIP = $(USE_SCIP)
@echo USE_GLPK = $(USE_GLPK)
@echo USE_CPLEX = $(USE_CPLEX)
- @echo USE_XPRESS = $(USE_XPRESS)
ifdef GLPK_ROOT
@echo GLPK_ROOT = $(GLPK_ROOT)
endif
diff --git a/ortools/linear_solver/BUILD.bazel b/ortools/linear_solver/BUILD.bazel
index d328cbfb2c..7156a4c939 100644
--- a/ortools/linear_solver/BUILD.bazel
+++ b/ortools/linear_solver/BUILD.bazel
@@ -143,18 +143,6 @@ config_setting(
},
)
-bool_flag(
- name = "with_xpress",
- build_setting_default = False,
-)
-
-config_setting(
- name = "use_xpress",
- flag_values = {
- ":with_xpress": "true",
- },
-)
-
proto_library(
name = "linear_solver_proto",
srcs = ["linear_solver.proto"],
@@ -187,6 +175,7 @@ cc_library(
"linear_solver_callback.cc",
"lpi_glop.cpp",
"sat_interface.cc",
+ "xpress_interface.cc",
] + select({
":use_bop": ["bop_interface.cc"],
"//conditions:default": [],
@@ -220,9 +209,6 @@ cc_library(
}) + select({
":use_cplex": ["cplex_interface.cc"],
"//conditions:default": [],
- }) + select({
- ":use_xpress": ["xpress_interface.cc"],
- "//conditions:default": [],
}),
hdrs = [
"linear_expr.h",
@@ -261,9 +247,6 @@ cc_library(
}) + select({
":use_cplex": ["-DUSE_CPLEX"],
"//conditions:default": [],
- }) + select({
- ":use_xpress": ["-DUSE_XPRESS"],
- "//conditions:default": [],
}),
deps = [
":linear_solver_cc_proto",
@@ -279,6 +262,7 @@ cc_library(
"//ortools/base:stl_util",
"//ortools/base:timer",
"//ortools/gurobi:environment",
+ "//ortools/xpress:environment",
"//ortools/gurobi:gurobi_util",
"//ortools/linear_solver/proto_solver:glop_proto_solver",
"//ortools/linear_solver/proto_solver:gurobi_proto_solver",
diff --git a/ortools/linear_solver/CMakeLists.txt b/ortools/linear_solver/CMakeLists.txt
index bdea358e66..6a3ccfdeb5 100644
--- a/ortools/linear_solver/CMakeLists.txt
+++ b/ortools/linear_solver/CMakeLists.txt
@@ -16,6 +16,7 @@ list(REMOVE_ITEM _SRCS
${CMAKE_CURRENT_SOURCE_DIR}/solve.cc
)
list(FILTER _SRCS EXCLUDE REGEX "/model_exporter_main\\.cc")
+list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc")
if(USE_SCIP)
list(APPEND _SRCS ${LPI_GLOP_SRC})
endif()
@@ -44,7 +45,6 @@ target_link_libraries(${NAME} PRIVATE
$<$:HIGHS::HIGHS>
$<$:Eigen3::Eigen>
$<$:libscip>
- $<$:XPRESS::XPRESS>
${PROJECT_NAMESPACE}::${PROJECT_NAME}_proto)
#add_library(${PROJECT_NAMESPACE}::linear_solver ALIAS ${NAME})
@@ -68,3 +68,18 @@ elseif(UNIX)
endif()
install(TARGETS solve)
+
+if (BUILD_CXX_EXAMPLES)
+ if (APPLE)
+ set(CMAKE_INSTALL_RPATH
+ "@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path")
+ elseif (UNIX)
+ set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:$ORIGIN:$ORIGIN/../lib:$ORIGIN")
+ endif ()
+
+ add_executable(test_xprs_interface xpress_interface_test.cc)
+ target_compile_features(test_xprs_interface PRIVATE cxx_std_17)
+ target_link_libraries(test_xprs_interface PRIVATE ortools::ortools GTest::gtest_main)
+
+ add_test(NAME cxx_unittests_xpress_interface COMMAND test_xprs_interface)
+endif ()
diff --git a/ortools/linear_solver/java/linear_solver.i b/ortools/linear_solver/java/linear_solver.i
index 0afd3114de..43dd1b78aa 100644
--- a/ortools/linear_solver/java/linear_solver.i
+++ b/ortools/linear_solver/java/linear_solver.i
@@ -390,6 +390,7 @@ PROTO2_RETURN(
%rename (suppressOutput) operations_research::MPSolver::SuppressOutput; // no test
%rename (lookupConstraintOrNull) operations_research::MPSolver::LookupConstraintOrNull; // no test
%rename (lookupVariableOrNull) operations_research::MPSolver::LookupVariableOrNull; // no test
+%rename (write) operations_research::MPSolver::Write;
// Expose very advanced parts of the MPSolver API. For expert users only.
%rename (computeConstraintActivities) operations_research::MPSolver::ComputeConstraintActivities;
diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc
index d4eb2963db..e00b640fd2 100644
--- a/ortools/linear_solver/linear_solver.cc
+++ b/ortools/linear_solver/linear_solver.cc
@@ -403,10 +403,9 @@ extern MPSolverInterface* BuildGurobiInterface(bool mip,
#if defined(USE_CPLEX)
extern MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver);
#endif
-#if defined(USE_XPRESS)
extern MPSolverInterface* BuildXpressInterface(bool mip,
MPSolver* const solver);
-#endif
+
namespace {
MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
@@ -460,12 +459,10 @@ MPSolverInterface* BuildSolverInterface(MPSolver* const solver) {
case MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING:
return BuildCplexInterface(true, solver);
#endif
-#if defined(USE_XPRESS)
case MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING:
return BuildXpressInterface(true, solver);
case MPSolver::XPRESS_LINEAR_PROGRAMMING:
return BuildXpressInterface(false, solver);
-#endif
default:
// TODO(user): Revert to the best *available* interface.
LOG(FATAL) << "Linear solver not recognized.";
@@ -501,6 +498,7 @@ MPSolver::MPSolver(const std::string& name,
MPSolver::~MPSolver() { Clear(); }
extern bool GurobiIsCorrectlyInstalled();
+extern bool XpressIsCorrectlyInstalled();
// static
bool MPSolver::SupportsProblemType(OptimizationProblemType problem_type) {
@@ -545,12 +543,10 @@ bool MPSolver::SupportsProblemType(OptimizationProblemType problem_type) {
return true;
}
#endif
-#ifdef USE_XPRESS
if (problem_type == XPRESS_MIXED_INTEGER_PROGRAMMING ||
problem_type == XPRESS_LINEAR_PROGRAMMING) {
- return true;
+ return XpressIsCorrectlyInstalled();
}
-#endif
return false;
}
diff --git a/ortools/linear_solver/linear_solver.h b/ortools/linear_solver/linear_solver.h
index eb32fcc9e3..625ffe17a6 100644
--- a/ortools/linear_solver/linear_solver.h
+++ b/ortools/linear_solver/linear_solver.h
@@ -1661,7 +1661,7 @@ class MPSolverInterface {
}
// Writes the model using the solver internal write function. Currently only
- // available for GurobiInterface.
+ // available for GurobiInterface and XpressInterface.
virtual void Write(const std::string& filename);
// ----- Model modifications and extraction -----
diff --git a/ortools/linear_solver/python/linear_solver.i b/ortools/linear_solver/python/linear_solver.i
index 83a639a254..98e37728d6 100644
--- a/ortools/linear_solver/python/linear_solver.i
+++ b/ortools/linear_solver/python/linear_solver.i
@@ -366,6 +366,7 @@ PY_CONVERT(MPVariable);
%unignore operations_research::MPSolver::NextSolution;
// ExportModelAsLpFormat() is also visible: it's overridden by an %extend, above.
// ExportModelAsMpsFormat() is also visible: it's overridden by an %extend, above.
+%unignore operations_research::MPSolver::Write;
// Expose very advanced parts of the MPSolver API. For expert users only.
%unignore operations_research::MPSolver::ComputeConstraintActivities;
diff --git a/ortools/linear_solver/xpress_interface.cc b/ortools/linear_solver/xpress_interface.cc
index 11040b2cdf..559338c003 100644
--- a/ortools/linear_solver/xpress_interface.cc
+++ b/ortools/linear_solver/xpress_interface.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 RTE
+// Copyright 2019-2023 RTE
// 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
@@ -13,60 +13,34 @@
// Initial version of this code was provided by RTE
-#if defined(USE_XPRESS)
-
#include
-#include
-#include
+#include
#include
#include
#include
#include
-#include
+#include
#include
#include "absl/strings/str_format.h"
#include "ortools/base/logging.h"
#include "ortools/base/timer.h"
#include "ortools/linear_solver/linear_solver.h"
-
-extern "C" {
-#include "xprs.h"
-}
+#include "ortools/xpress/environment.h"
#define XPRS_INTEGER 'I'
#define XPRS_CONTINUOUS 'C'
-#define STRINGIFY2(X) #X
-#define STRINGIFY(X) STRINGIFY2(X)
-extern "C" {
-static void XPRS_CC cbmessage(XPRSprob, void* cbdata, char const* msg,
- int msglen, int msgtype) {
- if (msgtype < 0) {
- // msgtype < 0 is a request to flush all output.
- LOG(INFO) << std::flush;
- LOG(WARNING) << std::flush;
- LOG(ERROR) << std::flush;
- } else if (msglen > 0 || msg) { // Empty lines have msglen=0, msg!=NULL
- switch (msgtype) {
- case 1: /* info */
- LOG(INFO) << msg << std::endl;
- break;
- case 2: /* unused */
- break;
- case 3: /* warn */
- LOG(WARNING) << msg << std::endl;
- break;
- case 4: /* error */
- LOG(ERROR) << msg << std::endl;
- break;
- }
- }
-}
-}
+// The argument to this macro is the invocation of a XPRS function that
+// returns a status. If the function returns non-zero the macro aborts
+// the program with an appropriate error message.
+#define CHECK_STATUS(s) \
+ do { \
+ int const status_ = s; \
+ CHECK_EQ(0, status_); \
+ } while (0)
-namespace {
-// Get the solver version for prob as string.
+namespace operations_research {
std::string getSolverVersion(XPRSprob const& prob) {
// XPRS_VERSION gives the version number as MAJOR*100 + RELEASE.
// It does not include the build number.
@@ -160,35 +134,74 @@ bool readParameter(XPRSprob const& prob, std::string const& name,
return true;
}
-int XPRSgetnumcols(const XPRSprob& mLp) {
+void printError(const XPRSprob& mLp, int line) {
+ char errmsg[512];
+ XPRSgetlasterror(mLp, errmsg);
+ VLOG(0) << absl::StrFormat("Function line %d did not execute correctly: %s\n",
+ line, errmsg);
+ exit(0);
+}
+
+void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata);
+
+/**********************************************************************************\
+* Name: optimizermsg *
+* Purpose: Display Optimizer error messages and warnings. *
+* Arguments: const char *sMsg Message string *
+* int nLen Message length *
+* int nMsgLvl Message type *
+* Return Value: None *
+\**********************************************************************************/
+void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
+ int nMsgLvl);
+
+int getnumcols(const XPRSprob& mLp) {
int nCols = 0;
- XPRSgetintattrib(mLp, XPRS_ORIGINALCOLS, &nCols);
+ XPRSgetintattrib(mLp, XPRS_COLS, &nCols);
return nCols;
}
-int XPRSgetnumrows(const XPRSprob& mLp) {
+int getnumrows(const XPRSprob& mLp) {
int nRows = 0;
- XPRSgetintattrib(mLp, XPRS_ORIGINALROWS, &nRows);
+ XPRSgetintattrib(mLp, XPRS_ROWS, &nRows);
return nRows;
}
-int XPRSgetitcnt(const XPRSprob& mLp) {
+int getitcnt(const XPRSprob& mLp) {
int nIters = 0;
XPRSgetintattrib(mLp, XPRS_SIMPLEXITER, &nIters);
return nIters;
}
-int XPRSgetnodecnt(const XPRSprob& mLp) {
+int getnodecnt(const XPRSprob& mLp) {
int nNodes = 0;
XPRSgetintattrib(mLp, XPRS_NODES, &nNodes);
return nNodes;
}
-int XPRSsetobjoffset(const XPRSprob& mLp, double value) {
- XPRSsetdblcontrol(mLp, XPRS_OBJRHS, value);
+int setobjoffset(const XPRSprob& mLp, double value) {
+ // TODO detect xpress version
+ static int indexes[1] = {-1};
+ double values[1] = {-value};
+ XPRSchgobj(mLp, 1, indexes, values);
return 0;
}
-} // namespace
+
+void addhint(const XPRSprob& mLp, int length, const double solval[],
+ const int colind[]) {
+ // The OR-Tools API does not allow setting a name for the solution
+ // passing NULL to XPRESS will have it generate a unique ID for the solution
+ if (int status = XPRSaddmipsol(mLp, length, solval, colind, NULL)) {
+ LOG(WARNING) << "Failed to set solution hint.";
+ }
+}
+
+enum CUSTOM_INTERRUPT_REASON { CALLBACK_EXCEPTION = 0 };
+
+void interruptXPRESS(XPRSprob& xprsProb, CUSTOM_INTERRUPT_REASON reason) {
+ // Reason values below 1000 are reserved by XPRESS
+ XPRSinterrupt(xprsProb, 1000 + reason);
+}
enum XPRS_BASIS_STATUS {
XPRS_AT_LOWER = 0,
@@ -203,62 +216,130 @@ enum XPRS_BASIS_STATUS {
#define XPRS_NAN std::numeric_limits::quiet_NaN()
#endif
-// The argument to this macro is the invocation of a XPRS function that
-// returns a status. If the function returns non-zero the macro aborts
-// the program with an appropriate error message.
-#define CHECK_STATUS(s) \
- do { \
- int const status_ = s; \
- CHECK_EQ(0, status_); \
- } while (0)
-
-namespace operations_research {
-
using std::unique_ptr;
+class XpressMPCallbackContext : public MPCallbackContext {
+ friend class XpressInterface;
+
+ public:
+ XpressMPCallbackContext(XPRSprob* xprsprob, MPCallbackEvent event,
+ int num_nodes)
+ : xprsprob_(xprsprob),
+ event_(event),
+ num_nodes_(num_nodes),
+ variable_values_(0){};
+
+ // Implementation of the interface.
+ MPCallbackEvent Event() override { return event_; };
+ bool CanQueryVariableValues() override;
+ double VariableValue(const MPVariable* variable) override;
+ void AddCut(const LinearRange& cutting_plane) override {
+ LOG(WARNING) << "AddCut is not implemented yet in XPRESS interface";
+ };
+ void AddLazyConstraint(const LinearRange& lazy_constraint) override {
+ LOG(WARNING)
+ << "AddLazyConstraint is not implemented yet in XPRESS interface";
+ };
+ double SuggestSolution(
+ const absl::flat_hash_map& solution) override;
+ int64_t NumExploredNodes() override { return num_nodes_; };
+
+ // Call this method to update the internal state of the callback context
+ // before passing it to MPCallback::RunCallback().
+ // Returns true if the internal state has changed.
+ bool UpdateFromXpressState(XPRSprob cbprob);
+
+ private:
+ XPRSprob* xprsprob_;
+ MPCallbackEvent event_;
+ std::vector
+ variable_values_; // same order as MPVariable* elements in MPSolver
+ int num_nodes_;
+};
+
+// Wraps the MPCallback in order to catch and store exceptions
+class MPCallbackWrapper {
+ public:
+ explicit MPCallbackWrapper(MPCallback* callback) : callback_(callback){};
+ MPCallback* GetCallback() const { return callback_; }
+ // Since our (C++) call-back functions are called from the XPRESS (C) code,
+ // exceptions thrown in our call-back code are not caught by XPRESS.
+ // We have to catch them, interrupt XPRESS, and log them after XPRESS is
+ // effectively interrupted (ie after solve).
+ void CatchException(XPRSprob cbprob) {
+ exceptions_mutex_.lock();
+ caught_exceptions_.push_back(std::current_exception());
+ interruptXPRESS(cbprob, CALLBACK_EXCEPTION);
+ exceptions_mutex_.unlock();
+ }
+ void LogCaughtExceptions() {
+ exceptions_mutex_.lock();
+ for (const std::exception_ptr& ex : caught_exceptions_) {
+ try {
+ std::rethrow_exception(ex);
+ } catch (std::exception &ex) {
+ // We don't want the interface to throw exceptions, plus it causes
+ // SWIG issues in Java & Python. Instead, we'll only log them.
+ // (The use cases where the user has to raise an exception inside their
+ // call-back does not seem to be frequent, anyway.)
+ LOG(ERROR) << "Caught exception during user-defined call-back: " << ex.what();
+ }
+ }
+ caught_exceptions_.clear();
+ exceptions_mutex_.unlock();
+ };
+
+ private:
+ MPCallback* callback_;
+ std::vector caught_exceptions_;
+ std::mutex exceptions_mutex_;
+};
+
// For a model that is extracted to an instance of this class there is a
-// 1:1 corresponence between MPVariable instances and XPRESS columns: the
+// 1:1 correspondence between MPVariable instances and XPRESS columns: the
// index of an extracted variable is the column index in the XPRESS model.
// Similar for instances of MPConstraint: the index of the constraint in
// the model is the row index in the XPRESS model.
class XpressInterface : public MPSolverInterface {
public:
// NOTE: 'mip' specifies the type of the problem (either continuous or
- // mixed integer. This type is fixed for the lifetime of the
+ // mixed integer). This type is fixed for the lifetime of the
// instance. There are no dynamic changes to the model type.
- explicit XpressInterface(MPSolver* const solver, bool mip);
- ~XpressInterface();
+ explicit XpressInterface(MPSolver* solver, bool mip);
+ ~XpressInterface() override;
// Sets the optimization direction (min/max).
- virtual void SetOptimizationDirection(bool maximize);
+ void SetOptimizationDirection(bool maximize) override;
// ----- Solve -----
// Solve the problem using the parameter values specified.
- virtual MPSolver::ResultStatus Solve(MPSolverParameters const& param);
+ MPSolver::ResultStatus Solve(MPSolverParameters const& param) override;
+
+ // Writes the model.
+ void Write(const std::string& filename) override;
// ----- Model modifications and extraction -----
// Resets extracted model
- virtual void Reset();
+ void Reset() override;
- virtual void SetVariableBounds(int var_index, double lb, double ub);
- virtual void SetVariableInteger(int var_index, bool integer);
- virtual void SetConstraintBounds(int row_index, double lb, double ub);
+ void SetVariableBounds(int var_index, double lb, double ub) override;
+ void SetVariableInteger(int var_index, bool integer) override;
+ void SetConstraintBounds(int row_index, double lb, double ub) override;
- virtual void AddRowConstraint(MPConstraint* const ct);
- virtual void AddVariable(MPVariable* const var);
- virtual void SetCoefficient(MPConstraint* const constraint,
- MPVariable const* const variable,
- double new_value, double old_value);
+ void AddRowConstraint(MPConstraint* ct) override;
+ void AddVariable(MPVariable* var) override;
+ void SetCoefficient(MPConstraint* constraint, MPVariable const* variable,
+ double new_value, double old_value) override;
// Clear a constraint from all its terms.
- virtual void ClearConstraint(MPConstraint* const constraint);
+ void ClearConstraint(MPConstraint* constraint) override;
// Change a coefficient in the linear objective
- virtual void SetObjectiveCoefficient(MPVariable const* const variable,
- double coefficient);
+ void SetObjectiveCoefficient(MPVariable const* variable,
+ double coefficient) override;
// Change the constant term in the linear objective.
- virtual void SetObjectiveOffset(double value);
+ void SetObjectiveOffset(double value) override;
// Clear the objective from all its terms.
- virtual void ClearObjective();
+ void ClearObjective() override;
// ------ Query statistics on the solution and the solve ------
// Number of simplex iterations
@@ -267,9 +348,9 @@ class XpressInterface : public MPSolverInterface {
virtual int64_t nodes() const;
// Returns the basis status of a row.
- virtual MPSolver::BasisStatus row_status(int constraint_index) const;
+ MPSolver::BasisStatus row_status(int constraint_index) const override;
// Returns the basis status of a column.
- virtual MPSolver::BasisStatus column_status(int variable_index) const;
+ MPSolver::BasisStatus column_status(int variable_index) const override;
// ----- Misc -----
@@ -280,6 +361,10 @@ class XpressInterface : public MPSolverInterface {
bool IsLP() const override { return !mMip; }
bool IsMIP() const override { return mMip; }
+ void SetStartingLpBasis(
+ const std::vector& variable_statuses,
+ const std::vector& constraint_statuses) override;
+
void ExtractNewVariables() override;
void ExtractNewConstraints() override;
void ExtractObjective() override;
@@ -301,8 +386,9 @@ class XpressInterface : public MPSolverInterface {
return 0.0;
}
- bool SetSolverSpecificParametersAsString(
- const std::string& parameters) override;
+ void SetCallback(MPCallback* mp_callback) override;
+ bool SupportsCallbacks() const override { return true; }
+
bool InterruptSolve() override {
if (mLp) XPRSinterrupt(mLp, XPRS_STOP_USER);
return true;
@@ -327,13 +413,14 @@ class XpressInterface : public MPSolverInterface {
// solution information as well. It is the counterpart of
// MPSolverInterface::InvalidateSolutionSynchronization
void InvalidateModelSynchronization() {
- mCstat = 0;
- mRstat = 0;
+ mCstat.clear();
+ mRstat.clear();
sync_status_ = MUST_RELOAD;
}
-
- // Transform XPRESS basis status to MPSolver basis status.
- static MPSolver::BasisStatus xformBasisStatus(int xpress_basis_status);
+ // Adds a new feasible, infeasible or partial MIP solution for the problem to
+ // the Optimizer. The hint is read in the MPSolver where the user set it using
+ // SetHint()
+ void AddSolutionHintToOptimizer();
bool readParameters(std::istream& is, char sep);
@@ -369,131 +456,402 @@ class XpressInterface : public MPSolverInterface {
SlowUpdatesAll = 0xffff
} const slowUpdates;
// XPRESS has no method to query the basis status of a single variable.
- // Hence we query the status only once and cache the array. This is
+ // Hence, we query the status only once and cache the array. This is
// much faster in case the basis status of more than one row/column
// is required.
- unique_ptr mutable mCstat;
- unique_ptr mutable mRstat;
- // Setup the right-hand side of a constraint from its lower and upper bound.
+ // TODO
+ std::vector mutable mCstat;
+ std::vector mutable mRstat;
+
+ std::vector mutable initial_variables_basis_status_;
+ std::vector mutable initial_constraint_basis_status_;
+
+ // Set up the right-hand side of a constraint from its lower and upper bound.
static void MakeRhs(double lb, double ub, double& rhs, char& sense,
double& range);
+
+ std::map& mapStringControls_;
+ std::map& mapDoubleControls_;
+ std::map& mapIntegerControls_;
+ std::map& mapInteger64Controls_;
+
+ bool SetSolverSpecificParametersAsString(
+ const std::string& parameters) override;
+ MPCallback* callback_ = nullptr;
};
-/** init XPRESS environment */
-int init_xpress_env(int xpress_oem_license_key = 0) {
- int code;
+// Transform MPSolver basis status to XPRESS status
+static int MPSolverToXpressBasisStatus(
+ MPSolver::BasisStatus mpsolver_basis_status);
+// Transform XPRESS basis status to MPSolver basis status.
+static MPSolver::BasisStatus XpressToMPSolverBasisStatus(
+ int xpress_basis_status);
- const char* xpress_from_env = getenv("XPRESS");
- std::string xpresspath;
-
- if (xpress_from_env == nullptr) {
-#if defined(XPRESS_PATH)
- std::string path(STRINGIFY(XPRESS_PATH));
- LOG(WARNING)
- << "Environment variable XPRESS undefined. Trying compile path "
- << "'" << path << "'";
-#if defined(_MSC_VER)
- // need to remove the enclosing '\"' from the string itself.
- path.erase(std::remove(path.begin(), path.end(), '\"'), path.end());
- xpresspath = path + "\\bin";
-#else // _MSC_VER
- xpresspath = path + "/bin";
-#endif // _MSC_VER
-#else
- LOG(WARNING)
- << "XpressInterface Error : Environment variable XPRESS undefined.\n";
- return -1;
-#endif
- } else {
- xpresspath = xpress_from_env;
- }
-
- /** if not an OEM key */
- if (xpress_oem_license_key == 0) {
- LOG(WARNING) << "XpressInterface : Initialising xpress-MP with parameter "
- << xpresspath << std::endl;
-
- code = XPRSinit(xpresspath.c_str());
-
- if (!code) {
- /** XPRSbanner informs about Xpress version, options and error messages */
- char banner[1000];
- XPRSgetbanner(banner);
-
- LOG(WARNING) << "XpressInterface : Xpress banner :\n"
- << banner << std::endl;
- return 0;
- } else {
- char errmsg[256];
- XPRSgetlicerrmsg(errmsg, 256);
-
- VLOG(0) << "XpressInterface : License error : " << errmsg << std::endl;
- VLOG(0) << "XpressInterface : XPRSinit returned code : " << code << "\n";
-
- char banner[1000];
- XPRSgetbanner(banner);
-
- LOG(ERROR) << "XpressInterface : Xpress banner :\n" << banner << "\n";
- return -1;
- }
- } else {
- /** if OEM key */
- LOG(WARNING) << "XpressInterface : Initialising xpress-MP with OEM key "
- << xpress_oem_license_key << "\n";
-
- int nvalue = 0;
- int ierr;
- char slicmsg[256] = "";
- char errmsg[256];
-
- XPRSlicense(&nvalue, slicmsg);
- VLOG(0) << "XpressInterface : First message from XPRSLicense : " << slicmsg
- << "\n";
-
- nvalue = xpress_oem_license_key - ((nvalue * nvalue) / 19);
- ierr = XPRSlicense(&nvalue, slicmsg);
-
- VLOG(0) << "XpressInterface : Second message from XPRSLicense : " << slicmsg
- << "\n";
- if (ierr == 16) {
- VLOG(0) << "XpressInterface : Optimizer development software detected\n";
- } else if (ierr != 0) {
- /** get the license error message */
- XPRSgetlicerrmsg(errmsg, 256);
-
- LOG(ERROR) << "XpressInterface : " << errmsg << "\n";
- return -1;
- }
-
- code = XPRSinit(NULL);
-
- if (!code) {
- return 0;
- } else {
- LOG(ERROR) << "XPRSinit returned code : " << code << "\n";
- return -1;
- }
- }
+static std::map& getMapStringControls() {
+ static std::map mapControls = {
+ {"MPSRHSNAME", XPRS_MPSRHSNAME},
+ {"MPSOBJNAME", XPRS_MPSOBJNAME},
+ {"MPSRANGENAME", XPRS_MPSRANGENAME},
+ {"MPSBOUNDNAME", XPRS_MPSBOUNDNAME},
+ {"OUTPUTMASK", XPRS_OUTPUTMASK},
+ {"TUNERMETHODFILE", XPRS_TUNERMETHODFILE},
+ {"TUNEROUTPUTPATH", XPRS_TUNEROUTPUTPATH},
+ {"TUNERSESSIONNAME", XPRS_TUNERSESSIONNAME},
+ {"COMPUTEEXECSERVICE", XPRS_COMPUTEEXECSERVICE},
+ };
+ return mapControls;
}
-// Creates a LP/MIP instance.
+static std::map& getMapDoubleControls() {
+ static std::map mapControls = {
+ {"MAXCUTTIME", XPRS_MAXCUTTIME},
+ {"MAXSTALLTIME", XPRS_MAXSTALLTIME},
+ {"TUNERMAXTIME", XPRS_TUNERMAXTIME},
+ {"MATRIXTOL", XPRS_MATRIXTOL},
+ {"PIVOTTOL", XPRS_PIVOTTOL},
+ {"FEASTOL", XPRS_FEASTOL},
+ {"OUTPUTTOL", XPRS_OUTPUTTOL},
+ {"SOSREFTOL", XPRS_SOSREFTOL},
+ {"OPTIMALITYTOL", XPRS_OPTIMALITYTOL},
+ {"ETATOL", XPRS_ETATOL},
+ {"RELPIVOTTOL", XPRS_RELPIVOTTOL},
+ {"MIPTOL", XPRS_MIPTOL},
+ {"MIPTOLTARGET", XPRS_MIPTOLTARGET},
+ {"BARPERTURB", XPRS_BARPERTURB},
+ {"MIPADDCUTOFF", XPRS_MIPADDCUTOFF},
+ {"MIPABSCUTOFF", XPRS_MIPABSCUTOFF},
+ {"MIPRELCUTOFF", XPRS_MIPRELCUTOFF},
+ {"PSEUDOCOST", XPRS_PSEUDOCOST},
+ {"PENALTY", XPRS_PENALTY},
+ {"BIGM", XPRS_BIGM},
+ {"MIPABSSTOP", XPRS_MIPABSSTOP},
+ {"MIPRELSTOP", XPRS_MIPRELSTOP},
+ {"CROSSOVERACCURACYTOL", XPRS_CROSSOVERACCURACYTOL},
+ {"PRIMALPERTURB", XPRS_PRIMALPERTURB},
+ {"DUALPERTURB", XPRS_DUALPERTURB},
+ {"BAROBJSCALE", XPRS_BAROBJSCALE},
+ {"BARRHSSCALE", XPRS_BARRHSSCALE},
+ {"CHOLESKYTOL", XPRS_CHOLESKYTOL},
+ {"BARGAPSTOP", XPRS_BARGAPSTOP},
+ {"BARDUALSTOP", XPRS_BARDUALSTOP},
+ {"BARPRIMALSTOP", XPRS_BARPRIMALSTOP},
+ {"BARSTEPSTOP", XPRS_BARSTEPSTOP},
+ {"ELIMTOL", XPRS_ELIMTOL},
+ {"MARKOWITZTOL", XPRS_MARKOWITZTOL},
+ {"MIPABSGAPNOTIFY", XPRS_MIPABSGAPNOTIFY},
+ {"MIPRELGAPNOTIFY", XPRS_MIPRELGAPNOTIFY},
+ {"BARLARGEBOUND", XPRS_BARLARGEBOUND},
+ {"PPFACTOR", XPRS_PPFACTOR},
+ {"REPAIRINDEFINITEQMAX", XPRS_REPAIRINDEFINITEQMAX},
+ {"BARGAPTARGET", XPRS_BARGAPTARGET},
+ {"DUMMYCONTROL", XPRS_DUMMYCONTROL},
+ {"BARSTARTWEIGHT", XPRS_BARSTARTWEIGHT},
+ {"BARFREESCALE", XPRS_BARFREESCALE},
+ {"SBEFFORT", XPRS_SBEFFORT},
+ {"HEURDIVERANDOMIZE", XPRS_HEURDIVERANDOMIZE},
+ {"HEURSEARCHEFFORT", XPRS_HEURSEARCHEFFORT},
+ {"CUTFACTOR", XPRS_CUTFACTOR},
+ {"EIGENVALUETOL", XPRS_EIGENVALUETOL},
+ {"INDLINBIGM", XPRS_INDLINBIGM},
+ {"TREEMEMORYSAVINGTARGET", XPRS_TREEMEMORYSAVINGTARGET},
+ {"INDPRELINBIGM", XPRS_INDPRELINBIGM},
+ {"RELAXTREEMEMORYLIMIT", XPRS_RELAXTREEMEMORYLIMIT},
+ {"MIPABSGAPNOTIFYOBJ", XPRS_MIPABSGAPNOTIFYOBJ},
+ {"MIPABSGAPNOTIFYBOUND", XPRS_MIPABSGAPNOTIFYBOUND},
+ {"PRESOLVEMAXGROW", XPRS_PRESOLVEMAXGROW},
+ {"HEURSEARCHTARGETSIZE", XPRS_HEURSEARCHTARGETSIZE},
+ {"CROSSOVERRELPIVOTTOL", XPRS_CROSSOVERRELPIVOTTOL},
+ {"CROSSOVERRELPIVOTTOLSAFE", XPRS_CROSSOVERRELPIVOTTOLSAFE},
+ {"DETLOGFREQ", XPRS_DETLOGFREQ},
+ {"MAXIMPLIEDBOUND", XPRS_MAXIMPLIEDBOUND},
+ {"FEASTOLTARGET", XPRS_FEASTOLTARGET},
+ {"OPTIMALITYTOLTARGET", XPRS_OPTIMALITYTOLTARGET},
+ {"PRECOMPONENTSEFFORT", XPRS_PRECOMPONENTSEFFORT},
+ {"LPLOGDELAY", XPRS_LPLOGDELAY},
+ {"HEURDIVEITERLIMIT", XPRS_HEURDIVEITERLIMIT},
+ {"BARKERNEL", XPRS_BARKERNEL},
+ {"FEASTOLPERTURB", XPRS_FEASTOLPERTURB},
+ {"CROSSOVERFEASWEIGHT", XPRS_CROSSOVERFEASWEIGHT},
+ {"LUPIVOTTOL", XPRS_LUPIVOTTOL},
+ {"MIPRESTARTGAPTHRESHOLD", XPRS_MIPRESTARTGAPTHRESHOLD},
+ {"NODEPROBINGEFFORT", XPRS_NODEPROBINGEFFORT},
+ {"INPUTTOL", XPRS_INPUTTOL},
+ {"MIPRESTARTFACTOR", XPRS_MIPRESTARTFACTOR},
+ {"BAROBJPERTURB", XPRS_BAROBJPERTURB},
+ {"CPIALPHA", XPRS_CPIALPHA},
+ {"GLOBALBOUNDINGBOX", XPRS_GLOBALBOUNDINGBOX},
+ {"TIMELIMIT", XPRS_TIMELIMIT},
+ {"SOLTIMELIMIT", XPRS_SOLTIMELIMIT},
+ {"REPAIRINFEASTIMELIMIT", XPRS_REPAIRINFEASTIMELIMIT},
+ };
+ return mapControls;
+}
+
+static std::map& getMapIntControls() {
+ static std::map mapControls = {
+ {"EXTRAROWS", XPRS_EXTRAROWS},
+ {"EXTRACOLS", XPRS_EXTRACOLS},
+ {"LPITERLIMIT", XPRS_LPITERLIMIT},
+ {"LPLOG", XPRS_LPLOG},
+ {"SCALING", XPRS_SCALING},
+ {"PRESOLVE", XPRS_PRESOLVE},
+ {"CRASH", XPRS_CRASH},
+ {"PRICINGALG", XPRS_PRICINGALG},
+ {"INVERTFREQ", XPRS_INVERTFREQ},
+ {"INVERTMIN", XPRS_INVERTMIN},
+ {"MAXNODE", XPRS_MAXNODE},
+ {"MAXTIME", XPRS_MAXTIME},
+ {"MAXMIPSOL", XPRS_MAXMIPSOL},
+ {"SIFTPASSES", XPRS_SIFTPASSES},
+ {"DEFAULTALG", XPRS_DEFAULTALG},
+ {"VARSELECTION", XPRS_VARSELECTION},
+ {"NODESELECTION", XPRS_NODESELECTION},
+ {"BACKTRACK", XPRS_BACKTRACK},
+ {"MIPLOG", XPRS_MIPLOG},
+ {"KEEPNROWS", XPRS_KEEPNROWS},
+ {"MPSECHO", XPRS_MPSECHO},
+ {"MAXPAGELINES", XPRS_MAXPAGELINES},
+ {"OUTPUTLOG", XPRS_OUTPUTLOG},
+ {"BARSOLUTION", XPRS_BARSOLUTION},
+ {"CACHESIZE", XPRS_CACHESIZE},
+ {"CROSSOVER", XPRS_CROSSOVER},
+ {"BARITERLIMIT", XPRS_BARITERLIMIT},
+ {"CHOLESKYALG", XPRS_CHOLESKYALG},
+ {"BAROUTPUT", XPRS_BAROUTPUT},
+ {"EXTRAMIPENTS", XPRS_EXTRAMIPENTS},
+ {"REFACTOR", XPRS_REFACTOR},
+ {"BARTHREADS", XPRS_BARTHREADS},
+ {"KEEPBASIS", XPRS_KEEPBASIS},
+ {"CROSSOVEROPS", XPRS_CROSSOVEROPS},
+ {"VERSION", XPRS_VERSION},
+ {"CROSSOVERTHREADS", XPRS_CROSSOVERTHREADS},
+ {"BIGMMETHOD", XPRS_BIGMMETHOD},
+ {"MPSNAMELENGTH", XPRS_MPSNAMELENGTH},
+ {"ELIMFILLIN", XPRS_ELIMFILLIN},
+ {"PRESOLVEOPS", XPRS_PRESOLVEOPS},
+ {"MIPPRESOLVE", XPRS_MIPPRESOLVE},
+ {"MIPTHREADS", XPRS_MIPTHREADS},
+ {"BARORDER", XPRS_BARORDER},
+ {"BREADTHFIRST", XPRS_BREADTHFIRST},
+ {"AUTOPERTURB", XPRS_AUTOPERTURB},
+ {"DENSECOLLIMIT", XPRS_DENSECOLLIMIT},
+ {"CALLBACKFROMMASTERTHREAD", XPRS_CALLBACKFROMMASTERTHREAD},
+ {"MAXMCOEFFBUFFERELEMS", XPRS_MAXMCOEFFBUFFERELEMS},
+ {"REFINEOPS", XPRS_REFINEOPS},
+ {"LPREFINEITERLIMIT", XPRS_LPREFINEITERLIMIT},
+ {"MIPREFINEITERLIMIT", XPRS_MIPREFINEITERLIMIT},
+ {"DUALIZEOPS", XPRS_DUALIZEOPS},
+ {"CROSSOVERITERLIMIT", XPRS_CROSSOVERITERLIMIT},
+ {"PREBASISRED", XPRS_PREBASISRED},
+ {"PRESORT", XPRS_PRESORT},
+ {"PREPERMUTE", XPRS_PREPERMUTE},
+ {"PREPERMUTESEED", XPRS_PREPERMUTESEED},
+ {"MAXMEMORYSOFT", XPRS_MAXMEMORYSOFT},
+ {"CUTFREQ", XPRS_CUTFREQ},
+ {"SYMSELECT", XPRS_SYMSELECT},
+ {"SYMMETRY", XPRS_SYMMETRY},
+ {"MAXMEMORYHARD", XPRS_MAXMEMORYHARD},
+ {"MIQCPALG", XPRS_MIQCPALG},
+ {"QCCUTS", XPRS_QCCUTS},
+ {"QCROOTALG", XPRS_QCROOTALG},
+ {"PRECONVERTSEPARABLE", XPRS_PRECONVERTSEPARABLE},
+ {"ALGAFTERNETWORK", XPRS_ALGAFTERNETWORK},
+ {"TRACE", XPRS_TRACE},
+ {"MAXIIS", XPRS_MAXIIS},
+ {"CPUTIME", XPRS_CPUTIME},
+ {"COVERCUTS", XPRS_COVERCUTS},
+ {"GOMCUTS", XPRS_GOMCUTS},
+ {"LPFOLDING", XPRS_LPFOLDING},
+ {"MPSFORMAT", XPRS_MPSFORMAT},
+ {"CUTSTRATEGY", XPRS_CUTSTRATEGY},
+ {"CUTDEPTH", XPRS_CUTDEPTH},
+ {"TREECOVERCUTS", XPRS_TREECOVERCUTS},
+ {"TREEGOMCUTS", XPRS_TREEGOMCUTS},
+ {"CUTSELECT", XPRS_CUTSELECT},
+ {"TREECUTSELECT", XPRS_TREECUTSELECT},
+ {"DUALIZE", XPRS_DUALIZE},
+ {"DUALGRADIENT", XPRS_DUALGRADIENT},
+ {"SBITERLIMIT", XPRS_SBITERLIMIT},
+ {"SBBEST", XPRS_SBBEST},
+ {"BARINDEFLIMIT", XPRS_BARINDEFLIMIT},
+ {"HEURFREQ", XPRS_HEURFREQ},
+ {"HEURDEPTH", XPRS_HEURDEPTH},
+ {"HEURMAXSOL", XPRS_HEURMAXSOL},
+ {"HEURNODES", XPRS_HEURNODES},
+ {"LNPBEST", XPRS_LNPBEST},
+ {"LNPITERLIMIT", XPRS_LNPITERLIMIT},
+ {"BRANCHCHOICE", XPRS_BRANCHCHOICE},
+ {"BARREGULARIZE", XPRS_BARREGULARIZE},
+ {"SBSELECT", XPRS_SBSELECT},
+ {"LOCALCHOICE", XPRS_LOCALCHOICE},
+ {"LOCALBACKTRACK", XPRS_LOCALBACKTRACK},
+ {"DUALSTRATEGY", XPRS_DUALSTRATEGY},
+ {"L1CACHE", XPRS_L1CACHE},
+ {"HEURDIVESTRATEGY", XPRS_HEURDIVESTRATEGY},
+ {"HEURSELECT", XPRS_HEURSELECT},
+ {"BARSTART", XPRS_BARSTART},
+ {"PRESOLVEPASSES", XPRS_PRESOLVEPASSES},
+ {"BARNUMSTABILITY", XPRS_BARNUMSTABILITY},
+ {"BARORDERTHREADS", XPRS_BARORDERTHREADS},
+ {"EXTRASETS", XPRS_EXTRASETS},
+ {"FEASIBILITYPUMP", XPRS_FEASIBILITYPUMP},
+ {"PRECOEFELIM", XPRS_PRECOEFELIM},
+ {"PREDOMCOL", XPRS_PREDOMCOL},
+ {"HEURSEARCHFREQ", XPRS_HEURSEARCHFREQ},
+ {"HEURDIVESPEEDUP", XPRS_HEURDIVESPEEDUP},
+ {"SBESTIMATE", XPRS_SBESTIMATE},
+ {"BARCORES", XPRS_BARCORES},
+ {"MAXCHECKSONMAXTIME", XPRS_MAXCHECKSONMAXTIME},
+ {"MAXCHECKSONMAXCUTTIME", XPRS_MAXCHECKSONMAXCUTTIME},
+ {"HISTORYCOSTS", XPRS_HISTORYCOSTS},
+ {"ALGAFTERCROSSOVER", XPRS_ALGAFTERCROSSOVER},
+ {"MUTEXCALLBACKS", XPRS_MUTEXCALLBACKS},
+ {"BARCRASH", XPRS_BARCRASH},
+ {"HEURDIVESOFTROUNDING", XPRS_HEURDIVESOFTROUNDING},
+ {"HEURSEARCHROOTSELECT", XPRS_HEURSEARCHROOTSELECT},
+ {"HEURSEARCHTREESELECT", XPRS_HEURSEARCHTREESELECT},
+ {"MPS18COMPATIBLE", XPRS_MPS18COMPATIBLE},
+ {"ROOTPRESOLVE", XPRS_ROOTPRESOLVE},
+ {"CROSSOVERDRP", XPRS_CROSSOVERDRP},
+ {"FORCEOUTPUT", XPRS_FORCEOUTPUT},
+ {"PRIMALOPS", XPRS_PRIMALOPS},
+ {"DETERMINISTIC", XPRS_DETERMINISTIC},
+ {"PREPROBING", XPRS_PREPROBING},
+ {"TREEMEMORYLIMIT", XPRS_TREEMEMORYLIMIT},
+ {"TREECOMPRESSION", XPRS_TREECOMPRESSION},
+ {"TREEDIAGNOSTICS", XPRS_TREEDIAGNOSTICS},
+ {"MAXTREEFILESIZE", XPRS_MAXTREEFILESIZE},
+ {"PRECLIQUESTRATEGY", XPRS_PRECLIQUESTRATEGY},
+ {"REPAIRINFEASMAXTIME", XPRS_REPAIRINFEASMAXTIME},
+ {"IFCHECKCONVEXITY", XPRS_IFCHECKCONVEXITY},
+ {"PRIMALUNSHIFT", XPRS_PRIMALUNSHIFT},
+ {"REPAIRINDEFINITEQ", XPRS_REPAIRINDEFINITEQ},
+ {"MIPRAMPUP", XPRS_MIPRAMPUP},
+ {"MAXLOCALBACKTRACK", XPRS_MAXLOCALBACKTRACK},
+ {"USERSOLHEURISTIC", XPRS_USERSOLHEURISTIC},
+ {"FORCEPARALLELDUAL", XPRS_FORCEPARALLELDUAL},
+ {"BACKTRACKTIE", XPRS_BACKTRACKTIE},
+ {"BRANCHDISJ", XPRS_BRANCHDISJ},
+ {"MIPFRACREDUCE", XPRS_MIPFRACREDUCE},
+ {"CONCURRENTTHREADS", XPRS_CONCURRENTTHREADS},
+ {"MAXSCALEFACTOR", XPRS_MAXSCALEFACTOR},
+ {"HEURTHREADS", XPRS_HEURTHREADS},
+ {"THREADS", XPRS_THREADS},
+ {"HEURBEFORELP", XPRS_HEURBEFORELP},
+ {"PREDOMROW", XPRS_PREDOMROW},
+ {"BRANCHSTRUCTURAL", XPRS_BRANCHSTRUCTURAL},
+ {"QUADRATICUNSHIFT", XPRS_QUADRATICUNSHIFT},
+ {"BARPRESOLVEOPS", XPRS_BARPRESOLVEOPS},
+ {"QSIMPLEXOPS", XPRS_QSIMPLEXOPS},
+ {"MIPRESTART", XPRS_MIPRESTART},
+ {"CONFLICTCUTS", XPRS_CONFLICTCUTS},
+ {"PREPROTECTDUAL", XPRS_PREPROTECTDUAL},
+ {"CORESPERCPU", XPRS_CORESPERCPU},
+ {"RESOURCESTRATEGY", XPRS_RESOURCESTRATEGY},
+ {"CLAMPING", XPRS_CLAMPING},
+ {"SLEEPONTHREADWAIT", XPRS_SLEEPONTHREADWAIT},
+ {"PREDUPROW", XPRS_PREDUPROW},
+ {"CPUPLATFORM", XPRS_CPUPLATFORM},
+ {"BARALG", XPRS_BARALG},
+ {"SIFTING", XPRS_SIFTING},
+ {"LPLOGSTYLE", XPRS_LPLOGSTYLE},
+ {"RANDOMSEED", XPRS_RANDOMSEED},
+ {"TREEQCCUTS", XPRS_TREEQCCUTS},
+ {"PRELINDEP", XPRS_PRELINDEP},
+ {"DUALTHREADS", XPRS_DUALTHREADS},
+ {"PREOBJCUTDETECT", XPRS_PREOBJCUTDETECT},
+ {"PREBNDREDQUAD", XPRS_PREBNDREDQUAD},
+ {"PREBNDREDCONE", XPRS_PREBNDREDCONE},
+ {"PRECOMPONENTS", XPRS_PRECOMPONENTS},
+ {"MAXMIPTASKS", XPRS_MAXMIPTASKS},
+ {"MIPTERMINATIONMETHOD", XPRS_MIPTERMINATIONMETHOD},
+ {"PRECONEDECOMP", XPRS_PRECONEDECOMP},
+ {"HEURFORCESPECIALOBJ", XPRS_HEURFORCESPECIALOBJ},
+ {"HEURSEARCHROOTCUTFREQ", XPRS_HEURSEARCHROOTCUTFREQ},
+ {"PREELIMQUAD", XPRS_PREELIMQUAD},
+ {"PREIMPLICATIONS", XPRS_PREIMPLICATIONS},
+ {"TUNERMODE", XPRS_TUNERMODE},
+ {"TUNERMETHOD", XPRS_TUNERMETHOD},
+ {"TUNERTARGET", XPRS_TUNERTARGET},
+ {"TUNERTHREADS", XPRS_TUNERTHREADS},
+ {"TUNERHISTORY", XPRS_TUNERHISTORY},
+ {"TUNERPERMUTE", XPRS_TUNERPERMUTE},
+ {"TUNERVERBOSE", XPRS_TUNERVERBOSE},
+ {"TUNEROUTPUT", XPRS_TUNEROUTPUT},
+ {"PREANALYTICCENTER", XPRS_PREANALYTICCENTER},
+ {"NETCUTS", XPRS_NETCUTS},
+ {"LPFLAGS", XPRS_LPFLAGS},
+ {"MIPKAPPAFREQ", XPRS_MIPKAPPAFREQ},
+ {"OBJSCALEFACTOR", XPRS_OBJSCALEFACTOR},
+ {"TREEFILELOGINTERVAL", XPRS_TREEFILELOGINTERVAL},
+ {"IGNORECONTAINERCPULIMIT", XPRS_IGNORECONTAINERCPULIMIT},
+ {"IGNORECONTAINERMEMORYLIMIT", XPRS_IGNORECONTAINERMEMORYLIMIT},
+ {"MIPDUALREDUCTIONS", XPRS_MIPDUALREDUCTIONS},
+ {"GENCONSDUALREDUCTIONS", XPRS_GENCONSDUALREDUCTIONS},
+ {"PWLDUALREDUCTIONS", XPRS_PWLDUALREDUCTIONS},
+ {"BARFAILITERLIMIT", XPRS_BARFAILITERLIMIT},
+ {"AUTOSCALING", XPRS_AUTOSCALING},
+ {"GENCONSABSTRANSFORMATION", XPRS_GENCONSABSTRANSFORMATION},
+ {"COMPUTEJOBPRIORITY", XPRS_COMPUTEJOBPRIORITY},
+ {"PREFOLDING", XPRS_PREFOLDING},
+ {"NETSTALLLIMIT", XPRS_NETSTALLLIMIT},
+ {"SERIALIZEPREINTSOL", XPRS_SERIALIZEPREINTSOL},
+ {"NUMERICALEMPHASIS", XPRS_NUMERICALEMPHASIS},
+ {"PWLNONCONVEXTRANSFORMATION", XPRS_PWLNONCONVEXTRANSFORMATION},
+ {"MIPCOMPONENTS", XPRS_MIPCOMPONENTS},
+ {"MIPCONCURRENTNODES", XPRS_MIPCONCURRENTNODES},
+ {"MIPCONCURRENTSOLVES", XPRS_MIPCONCURRENTSOLVES},
+ {"OUTPUTCONTROLS", XPRS_OUTPUTCONTROLS},
+ {"SIFTSWITCH", XPRS_SIFTSWITCH},
+ {"HEUREMPHASIS", XPRS_HEUREMPHASIS},
+ {"COMPUTEMATX", XPRS_COMPUTEMATX},
+ {"COMPUTEMATX_IIS", XPRS_COMPUTEMATX_IIS},
+ {"COMPUTEMATX_IISMAXTIME", XPRS_COMPUTEMATX_IISMAXTIME},
+ {"BARREFITER", XPRS_BARREFITER},
+ {"COMPUTELOG", XPRS_COMPUTELOG},
+ {"SIFTPRESOLVEOPS", XPRS_SIFTPRESOLVEOPS},
+ {"CHECKINPUTDATA", XPRS_CHECKINPUTDATA},
+ {"ESCAPENAMES", XPRS_ESCAPENAMES},
+ {"IOTIMEOUT", XPRS_IOTIMEOUT},
+ {"AUTOCUTTING", XPRS_AUTOCUTTING},
+ {"CALLBACKCHECKTIMEDELAY", XPRS_CALLBACKCHECKTIMEDELAY},
+ {"MULTIOBJOPS", XPRS_MULTIOBJOPS},
+ {"MULTIOBJLOG", XPRS_MULTIOBJLOG},
+ {"GLOBALSPATIALBRANCHIFPREFERORIG", XPRS_GLOBALSPATIALBRANCHIFPREFERORIG},
+ {"PRECONFIGURATION", XPRS_PRECONFIGURATION},
+ {"FEASIBILITYJUMP", XPRS_FEASIBILITYJUMP},
+ };
+ return mapControls;
+}
+
+static std::map& getMapInt64Controls() {
+ static std::map mapControls = {
+ {"EXTRAELEMS", XPRS_EXTRAELEMS},
+ {"EXTRASETELEMS", XPRS_EXTRASETELEMS},
+ };
+ return mapControls;
+}
+
+// Creates an LP/MIP instance.
XpressInterface::XpressInterface(MPSolver* const solver, bool mip)
: MPSolverInterface(solver),
- mLp(0),
+ mLp(nullptr),
mMip(mip),
supportIncrementalExtraction(false),
slowUpdates(static_cast(SlowSetObjectiveCoefficient |
SlowClearObjective)),
- mCstat(),
- mRstat() {
- int status = init_xpress_env();
- CHECK_STATUS(status);
- status = XPRScreateprob(&mLp);
+ mapStringControls_(getMapStringControls()),
+ mapDoubleControls_(getMapDoubleControls()),
+ mapIntegerControls_(getMapIntControls()),
+ mapInteger64Controls_(getMapInt64Controls()) {
+ bool correctlyLoaded = initXpressEnv();
+ CHECK(correctlyLoaded);
+ int status = XPRScreateprob(&mLp);
CHECK_STATUS(status);
DCHECK(mLp != nullptr); // should not be NULL if status=0
+ int nReturn = XPRSaddcbmessage(mLp, optimizermsg, (void*)this, 0);
CHECK_STATUS(XPRSloadlp(mLp, "newProb", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
-
CHECK_STATUS(
XPRSchgobjsense(mLp, maximize_ ? XPRS_OBJ_MAXIMIZE : XPRS_OBJ_MINIMIZE));
}
@@ -504,7 +862,21 @@ XpressInterface::~XpressInterface() {
}
std::string XpressInterface::SolverVersion() const {
- return getSolverVersion(mLp);
+ // We prefer XPRSversionnumber() over XPRSversion() since the
+ // former will never pose any encoding issues.
+ int version = 0;
+ CHECK_STATUS(XPRSgetintcontrol(mLp, XPRS_VERSION, &version));
+
+ int const major = version / 1000000;
+ version -= major * 1000000;
+ int const release = version / 10000;
+ version -= release * 10000;
+ int const mod = version / 100;
+ version -= mod * 100;
+ int const fix = version;
+
+ return absl::StrFormat("XPRESS library version %d.%02d.%02d.%02d", major,
+ release, mod, fix);
}
// ------ Model modifications and extraction -----
@@ -518,14 +890,15 @@ void XpressInterface::Reset() {
status = XPRScreateprob(&mLp);
CHECK_STATUS(status);
DCHECK(mLp != nullptr); // should not be NULL if status=0
+ int nReturn = XPRSaddcbmessage(mLp, optimizermsg, (void*)this, 0);
CHECK_STATUS(XPRSloadlp(mLp, "newProb", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
CHECK_STATUS(
XPRSchgobjsense(mLp, maximize_ ? XPRS_OBJ_MAXIMIZE : XPRS_OBJ_MINIMIZE));
ResetExtractionInformation();
- mCstat = 0;
- mRstat = 0;
+ mCstat.clear();
+ mRstat.clear();
}
void XpressInterface::SetOptimizationDirection(bool maximize) {
@@ -540,7 +913,7 @@ void XpressInterface::SetVariableBounds(int var_index, double lb, double ub) {
// many variables may still be slow. So we don't perform the update by
// default. However, if we support incremental extraction
// (supportIncrementalExtraction is true) then we MUST perform the
- // update here or we will lose it.
+ // update here, or we will lose it.
if (!supportIncrementalExtraction && !(slowUpdates & SlowSetVariableBounds)) {
InvalidateModelSynchronization();
@@ -575,8 +948,8 @@ void XpressInterface::SetVariableInteger(int var_index, bool integer) {
// (supportIncrementalExtraction is true) then we MUST change the
// type of extracted variables here.
- if (!supportIncrementalExtraction && !slowUpdates &&
- !SlowSetVariableInteger) {
+ if (!supportIncrementalExtraction &&
+ !(slowUpdates & SlowSetVariableInteger)) {
InvalidateModelSynchronization();
} else {
if (mMip) {
@@ -584,7 +957,7 @@ void XpressInterface::SetVariableInteger(int var_index, bool integer) {
// Variable is extracted. Change the type immediately.
// TODO: Should we check the current type and don't do anything
// in case the type does not change?
- DCHECK_LE(var_index, XPRSgetnumcols(mLp));
+ DCHECK_LE(var_index, getnumcols(mLp));
char const type = integer ? XPRS_INTEGER : XPRS_CONTINUOUS;
CHECK_STATUS(XPRSchgcoltype(mLp, 1, &var_index, &type));
} else {
@@ -597,7 +970,7 @@ void XpressInterface::SetVariableInteger(int var_index, bool integer) {
}
}
-// Setup the right-hand side of a constraint.
+// Set up the right-hand side of a constraint.
void XpressInterface::MakeRhs(double lb, double ub, double& rhs, char& sense,
double& range) {
if (lb == ub) {
@@ -612,6 +985,7 @@ void XpressInterface::MakeRhs(double lb, double ub, double& rhs, char& sense,
// Xpress does not support contradictory bounds. Instead the sign on
// rndval is always ignored.
if (lb > ub) {
+ // TODO check if this is ok for the user
LOG(DFATAL) << "XPRESS does not support contradictory bounds on range "
"constraints! ["
<< lb << ", " << ub << "] will be converted to " << ub << ", "
@@ -667,7 +1041,7 @@ void XpressInterface::SetConstraintBounds(int index, double lb, double ub) {
if (constraint_is_extracted(index)) {
// Constraint is already extracted, so we must update its bounds
// and its type.
- DCHECK(mLp != NULL);
+ DCHECK(mLp != nullptr);
char sense;
double range, rhs;
MakeRhs(lb, ub, rhs, sense, range);
@@ -697,16 +1071,26 @@ void XpressInterface::AddRowConstraint(MPConstraint* const ct) {
// constraint. We could immediately call XPRSaddrows() here but it is
// usually much faster to handle the fully populated constraint in
// ExtractNewConstraints() right before the solve.
+
+ // TODO
+ // Make new constraints basic (rowstat[jrow]=1)
+ // Try not to delete basic variables, or non-basic constraints.
InvalidateModelSynchronization();
}
-void XpressInterface::AddVariable(MPVariable* const ct) {
+void XpressInterface::AddVariable(MPVariable* const var) {
// This is currently only invoked when a new variable is created,
// see MPSolver::MakeVar().
// At this point the variable does not appear in any constraints or
// the objective function. We could invoke XPRSaddcols() to immediately
- // create the variable here but it is usually much faster to handle the
- // fully setup variable in ExtractNewVariables() right before the solve.
+ // create the variable here, but it is usually much faster to handle the
+ // fully set-up variable in ExtractNewVariables() right before the solve.
+
+ // TODO
+ // Make new variables non-basic at their lower bound (colstat[icol]=0), unless
+ // a variable has an infinite lower bound and a finite upper bound, in which
+ // case make the variable non-basic at its upper bound (colstat[icol]=2) Try
+ // not to delete basic variables, or non-basic constraints.
InvalidateModelSynchronization();
}
@@ -720,7 +1104,7 @@ void XpressInterface::SetCoefficient(MPConstraint* const constraint,
// representation. So by default we don't perform this update immediately
// but instead mark the low-level modeling object "out of sync".
// If we want to support incremental extraction then we MUST perform
- // the modification immediately or we will lose it.
+ // the modification immediately, or we will lose it.
if (!supportIncrementalExtraction && !(slowUpdates & SlowSetCoefficient)) {
InvalidateModelSynchronization();
@@ -766,8 +1150,8 @@ void XpressInterface::ClearConstraint(MPConstraint* const constraint) {
unique_ptr val(new double[len]);
int j = 0;
const auto& coeffs = constraint->coefficients_;
- for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
- int const col = it->first->index();
+ for (auto coeff : coeffs) {
+ int const col = coeff.first->index();
if (variable_is_extracted(col)) {
rowind[j] = row;
colind[j] = col;
@@ -806,7 +1190,7 @@ void XpressInterface::SetObjectiveCoefficient(MPVariable const* const variable,
void XpressInterface::SetObjectiveOffset(double value) {
// Changing the objective offset is O(1), so we always do it immediately.
InvalidateSolutionSynchronization();
- CHECK_STATUS(XPRSsetobjoffset(mLp, value));
+ CHECK_STATUS(setobjoffset(mLp, value));
}
void XpressInterface::ClearObjective() {
@@ -818,13 +1202,13 @@ void XpressInterface::ClearObjective() {
// but to perform the update immediately.
if (supportIncrementalExtraction || (slowUpdates & SlowClearObjective)) {
- int const cols = XPRSgetnumcols(mLp);
+ int const cols = getnumcols(mLp);
unique_ptr ind(new int[cols]);
unique_ptr zero(new double[cols]);
int j = 0;
const auto& coeffs = solver_->objective_->coefficients_;
- for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
- int const idx = it->first->index();
+ for (auto coeff : coeffs) {
+ int const idx = coeff.first->index();
// We only need to reset variables that have been extracted.
if (variable_is_extracted(idx)) {
DCHECK_LT(idx, cols);
@@ -833,8 +1217,10 @@ void XpressInterface::ClearObjective() {
++j;
}
}
- if (j > 0) CHECK_STATUS(XPRSchgobj(mLp, j, ind.get(), zero.get()));
- CHECK_STATUS(XPRSsetobjoffset(mLp, 0.0));
+ if (j > 0) {
+ CHECK_STATUS(XPRSchgobj(mLp, j, ind.get(), zero.get()));
+ }
+ CHECK_STATUS(setobjoffset(mLp, 0.0));
} else {
InvalidateModelSynchronization();
}
@@ -844,13 +1230,13 @@ void XpressInterface::ClearObjective() {
int64_t XpressInterface::iterations() const {
if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfIterations;
- return static_cast(XPRSgetitcnt(mLp));
+ return static_cast(getitcnt(mLp));
}
int64_t XpressInterface::nodes() const {
if (mMip) {
if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfNodes;
- return static_cast(XPRSgetnodecnt(mLp));
+ return static_cast(getnodecnt(mLp));
} else {
LOG(DFATAL) << "Number of nodes only available for discrete problems";
return kUnknownNumberOfNodes;
@@ -858,7 +1244,7 @@ int64_t XpressInterface::nodes() const {
}
// Transform a XPRESS basis status to an MPSolver basis status.
-MPSolver::BasisStatus XpressInterface::xformBasisStatus(
+static MPSolver::BasisStatus XpressToMPSolverBasisStatus(
int xpress_basis_status) {
switch (xpress_basis_status) {
case XPRS_AT_LOWER:
@@ -875,6 +1261,24 @@ MPSolver::BasisStatus XpressInterface::xformBasisStatus(
}
}
+static int MPSolverToXpressBasisStatus(MPSolver::BasisStatus mpsolver_basis_status) {
+ switch (mpsolver_basis_status) {
+ case MPSolver::AT_LOWER_BOUND:
+ return XPRS_AT_LOWER;
+ case MPSolver::BASIC:
+ return XPRS_BASIC;
+ case MPSolver::AT_UPPER_BOUND:
+ return XPRS_AT_UPPER;
+ case MPSolver::FREE:
+ return XPRS_FREE_SUPER;
+ case MPSolver::FIXED_VALUE:
+ return XPRS_BASIC;
+ default:
+ LOG(DFATAL) << "Unknown MPSolver basis status";
+ return XPRS_FREE_SUPER;
+ }
+}
+
// Returns the basis status of a row.
MPSolver::BasisStatus XpressInterface::row_status(int constraint_index) const {
if (mMip) {
@@ -883,18 +1287,17 @@ MPSolver::BasisStatus XpressInterface::row_status(int constraint_index) const {
}
if (CheckSolutionIsSynchronized()) {
- if (!mRstat) {
- int const rows = XPRSgetnumrows(mLp);
- unique_ptr data(new int[rows]);
- mRstat.swap(data);
- CHECK_STATUS(XPRSgetbasis(mLp, 0, mRstat.get()));
+ if (mRstat.empty()) {
+ int const rows = getnumrows(mLp);
+ mRstat.resize(rows);
+ CHECK_STATUS(XPRSgetbasis(mLp, mRstat.data(), 0));
}
} else {
- mRstat = 0;
+ mRstat.clear();
}
- if (mRstat) {
- return xformBasisStatus(mRstat[constraint_index]);
+ if (!mRstat.empty()) {
+ return XpressToMPSolverBasisStatus(mRstat[constraint_index]);
} else {
LOG(FATAL) << "Row basis status not available";
return MPSolver::FREE;
@@ -909,18 +1312,17 @@ MPSolver::BasisStatus XpressInterface::column_status(int variable_index) const {
}
if (CheckSolutionIsSynchronized()) {
- if (!mCstat) {
- int const cols = XPRSgetnumcols(mLp);
- unique_ptr data(new int[cols]);
- mCstat.swap(data);
- CHECK_STATUS(XPRSgetbasis(mLp, mCstat.get(), 0));
+ if (mCstat.empty()) {
+ int const cols = getnumcols(mLp);
+ mCstat.resize(cols);
+ CHECK_STATUS(XPRSgetbasis(mLp, 0, mCstat.data()));
}
} else {
- mCstat = 0;
+ mCstat.clear();
}
- if (mCstat) {
- return xformBasisStatus(mCstat[variable_index]);
+ if (!mCstat.empty()) {
+ return XpressToMPSolverBasisStatus(mCstat[variable_index]);
} else {
LOG(FATAL) << "Column basis status not available";
return MPSolver::FREE;
@@ -945,28 +1347,25 @@ void XpressInterface::ExtractNewVariables() {
int const last_extracted = last_variable_index_;
int const var_count = solver_->variables_.size();
- int newcols = var_count - last_extracted;
- if (newcols > 0) {
+ int new_col_count = var_count - last_extracted;
+ if (new_col_count > 0) {
// There are non-extracted variables. Extract them now.
- unique_ptr obj(new double[newcols]);
- unique_ptr lb(new double[newcols]);
- unique_ptr ub(new double[newcols]);
- unique_ptr ctype(new char[newcols]);
- unique_ptr colname(new const char*[newcols]);
+ unique_ptr obj(new double[new_col_count]);
+ unique_ptr lb(new double[new_col_count]);
+ unique_ptr ub(new double[new_col_count]);
+ unique_ptr ctype(new char[new_col_count]);
- bool have_names = false;
- for (int j = 0, varidx = last_extracted; j < newcols; ++j, ++varidx) {
- MPVariable const* const var = solver_->variables_[varidx];
+ for (int j = 0, var_idx = last_extracted; j < new_col_count;
+ ++j, ++var_idx) {
+ MPVariable const* const var = solver_->variables_[var_idx];
lb[j] = var->lb();
ub[j] = var->ub();
ctype[j] = var->integer() ? XPRS_INTEGER : XPRS_CONTINUOUS;
- colname[j] = var->name().empty() ? 0 : var->name().c_str();
- have_names = have_names || var->name().empty();
obj[j] = solver_->objective_->GetCoefficient(var);
}
- // Arrays for modifying the problem are setup. Update the index
+ // Arrays for modifying the problem are set up. Update the index
// of variables that will get extracted now. Updating indices
// _before_ the actual extraction makes things much simpler in
// case we support incremental extraction.
@@ -978,7 +1377,7 @@ void XpressInterface::ExtractNewVariables() {
}
try {
- bool use_newcols = true;
+ bool use_new_cols = true;
if (supportIncrementalExtraction) {
// If we support incremental extraction then we must
@@ -989,8 +1388,8 @@ void XpressInterface::ExtractNewVariables() {
// For each column count the size of the intersection with
// existing constraints.
- unique_ptr collen(new int[newcols]);
- for (int j = 0; j < newcols; ++j) collen[j] = 0;
+ unique_ptr collen(new int[new_col_count]);
+ for (int j = 0; j < new_col_count; ++j) collen[j] = 0;
int nonzeros = 0;
// TODO: Use a bitarray to flag the constraints that actually
// intersect new variables?
@@ -998,8 +1397,8 @@ void XpressInterface::ExtractNewVariables() {
MPConstraint const* const ct = solver_->constraints_[i];
CHECK(constraint_is_extracted(ct->index()));
const auto& coeffs = ct->coefficients_;
- for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
- int const idx = it->first->index();
+ for (auto coeff : coeffs) {
+ int const idx = coeff.first->index();
if (variable_is_extracted(idx) && idx > last_variable_index_) {
collen[idx - last_variable_index_]++;
++nonzeros;
@@ -1011,75 +1410,82 @@ void XpressInterface::ExtractNewVariables() {
// At least one of the new variables did intersect with an
// old constraint. We have to create the new columns via
// XPRSaddcols().
- use_newcols = false;
- unique_ptr begin(new int[newcols + 2]);
+ use_new_cols = false;
+ unique_ptr begin(new int[new_col_count + 2]);
unique_ptr cmatind(new int[nonzeros]);
unique_ptr cmatval(new double[nonzeros]);
- // Here is how cmatbeg[] is setup:
+ // Here is how cmatbeg[] is set up:
// - it is initialized as
// [ 0, 0, collen[0], collen[0]+collen[1], ... ]
// so that cmatbeg[j+1] tells us where in cmatind[] and
// cmatval[] we need to put the next nonzero for column
// j
- // - after nonzeros have been setup the array looks like
+ // - after nonzeros have been set up, the array looks like
// [ 0, collen[0], collen[0]+collen[1], ... ]
// so that it is the correct input argument for XPRSaddcols
int* cmatbeg = begin.get();
cmatbeg[0] = 0;
cmatbeg[1] = 0;
++cmatbeg;
- for (int j = 0; j < newcols; ++j)
+ for (int j = 0; j < new_col_count; ++j)
cmatbeg[j + 1] = cmatbeg[j] + collen[j];
for (int i = 0; i < last_constraint_index_; ++i) {
MPConstraint const* const ct = solver_->constraints_[i];
int const row = ct->index();
const auto& coeffs = ct->coefficients_;
- for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
- int const idx = it->first->index();
+ for (auto coeff : coeffs) {
+ int const idx = coeff.first->index();
if (variable_is_extracted(idx) && idx > last_variable_index_) {
int const nz = cmatbeg[idx]++;
cmatind[nz] = row;
- cmatval[nz] = it->second;
+ cmatval[nz] = coeff.second;
}
}
}
--cmatbeg;
- CHECK_STATUS(XPRSaddcols(mLp, newcols, nonzeros, obj.get(), cmatbeg,
- cmatind.get(), cmatval.get(), lb.get(),
- ub.get()));
+ CHECK_STATUS(XPRSaddcols(mLp, new_col_count, nonzeros, obj.get(),
+ cmatbeg, cmatind.get(), cmatval.get(),
+ lb.get(), ub.get()));
}
}
- if (use_newcols) {
+ if (use_new_cols) {
// Either incremental extraction is not supported or none of
// the new variables did intersect an existing constraint.
// We can just use XPRSnewcols() to create the new variables.
- std::vector collen(newcols, 0);
- std::vector cmatbeg(newcols, 0);
+ std::vector collen(new_col_count, 0);
+ std::vector cmatbeg(new_col_count, 0);
unique_ptr cmatind(new int[1]);
unique_ptr cmatval(new double[1]);
cmatind[0] = 0;
cmatval[0] = 1.0;
- CHECK_STATUS(XPRSaddcols(mLp, newcols, 0, obj.get(), cmatbeg.data(),
- cmatind.get(), cmatval.get(), lb.get(),
- ub.get()));
- int const cols = XPRSgetnumcols(mLp);
- unique_ptr ind(new int[newcols]);
+ CHECK_STATUS(XPRSaddcols(mLp, new_col_count, 0, obj.get(),
+ cmatbeg.data(), cmatind.get(), cmatval.get(),
+ lb.get(), ub.get()));
+ //TODO fixme
+ // Writing all names worsen the performance significantly
+ //if (have_names) {
+ // CHECK_STATUS(XPRSaddnames(mLp, XPRS_NAMES_COLUMN, col_names.data(), 0,
+ // new_col_count - 1));
+ //}
+ int const cols = getnumcols(mLp);
+ unique_ptr ind(new int[new_col_count]);
for (int j = 0; j < cols; ++j) ind[j] = j;
CHECK_STATUS(
XPRSchgcoltype(mLp, cols - last_extracted, ind.get(), ctype.get()));
+
} else {
// Incremental extraction: we must update the ctype of the
// newly created variables (XPRSaddcols() does not allow
// specifying the ctype)
- if (mMip && XPRSgetnumcols(mLp) > 0) {
+ if (mMip && getnumcols(mLp) > 0) {
// Query the actual number of columns in case we did not
// manage to extract all columns.
- int const cols = XPRSgetnumcols(mLp);
- unique_ptr ind(new int[newcols]);
+ int const cols = getnumcols(mLp);
+ unique_ptr ind(new int[new_col_count]);
for (int j = last_extracted; j < cols; ++j)
ind[j - last_extracted] = j;
CHECK_STATUS(XPRSchgcoltype(mLp, cols - last_extracted, ind.get(),
@@ -1088,11 +1494,11 @@ void XpressInterface::ExtractNewVariables() {
}
} catch (...) {
// Undo all changes in case of error.
- int const cols = XPRSgetnumcols(mLp);
+ int const cols = getnumcols(mLp);
if (cols > last_extracted) {
- std::vector colsToDelete;
- for (int i = last_extracted; i < cols; ++i) colsToDelete.push_back(i);
- (void)XPRSdelcols(mLp, colsToDelete.size(), colsToDelete.data());
+ std::vector cols_to_delete;
+ for (int i = last_extracted; i < cols; ++i) cols_to_delete.push_back(i);
+ (void)XPRSdelcols(mLp, cols_to_delete.size(), cols_to_delete.data());
}
std::vector const& variables = solver_->variables();
int const size = variables.size();
@@ -1125,8 +1531,7 @@ void XpressInterface::ExtractNewConstraints() {
InvalidateSolutionSynchronization();
int newCons = total - offset;
- int const cols = XPRSgetnumcols(mLp);
- DCHECK_EQ(last_variable_index_, cols);
+ int const cols = getnumcols(mLp);
int const chunk = newCons; // 10; // max number of rows to add in one shot
// Update indices of new constraints _before_ actually extracting
@@ -1139,8 +1544,9 @@ void XpressInterface::ExtractNewConstraints() {
unique_ptr rmatbeg(new int[chunk]);
unique_ptr sense(new char[chunk]);
unique_ptr rhs(new double[chunk]);
- unique_ptr name(new char const*[chunk]);
unique_ptr rngval(new double[chunk]);
+ unique_ptr rngind(new int[chunk]);
+ bool haveRanges = false;
// Loop over the new constraints, collecting rows for up to
// CHUNK constraints into the arrays so that adding constraints
@@ -1149,7 +1555,6 @@ void XpressInterface::ExtractNewConstraints() {
// Collect up to CHUNK constraints into the arrays.
int nextRow = 0;
int nextNz = 0;
- bool haveRanges = false;
for (/* nothing */; c < newCons && nextRow < chunk; ++c, ++nextRow) {
MPConstraint const* const ct = solver_->constraints_[offset + c];
@@ -1164,37 +1569,40 @@ void XpressInterface::ExtractNewConstraints() {
MakeRhs(ct->lb(), ct->ub(), rhs[nextRow], sense[nextRow],
rngval[nextRow]);
haveRanges = haveRanges || (rngval[nextRow] != 0.0);
+ rngind[nextRow] = offset + c;
// Setup left-hand side of constraint.
rmatbeg[nextRow] = nextNz;
const auto& coeffs = ct->coefficients_;
- for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
- int const idx = it->first->index();
+ for (auto coeff : coeffs) {
+ int const idx = coeff.first->index();
if (variable_is_extracted(idx)) {
DCHECK_LT(nextNz, cols);
DCHECK_LT(idx, cols);
rmatind[nextNz] = idx;
- rmatval[nextNz] = it->second;
+ rmatval[nextNz] = coeff.second;
++nextNz;
}
}
-
- // Finally the name of the constraint.
- name[nextRow] = ct->name().empty() ? 0 : ct->name().c_str();
}
if (nextRow > 0) {
CHECK_STATUS(XPRSaddrows(mLp, nextRow, nextNz, sense.get(), rhs.get(),
- haveRanges ? rngval.get() : 0, rmatbeg.get(),
- rmatind.get(), rmatval.get()));
+ rngval.get(), rmatbeg.get(), rmatind.get(),
+ rmatval.get()));
+
+ if (haveRanges) {
+ CHECK_STATUS(
+ XPRSchgrhsrange(mLp, nextRow, rngind.get(), rngval.get()));
+ }
}
}
} catch (...) {
// Undo all changes in case of error.
- int const rows = XPRSgetnumrows(mLp);
- std::vector rowsToDelete;
- for (int i = offset; i < rows; ++i) rowsToDelete.push_back(i);
+ int const rows = getnumrows(mLp);
+ std::vector rows_to_delete;
+ for (int i = offset; i < rows; ++i) rows_to_delete.push_back(i);
if (rows > offset)
- (void)XPRSdelrows(mLp, rowsToDelete.size(), rowsToDelete.data());
+ (void)XPRSdelrows(mLp, rows_to_delete.size(), rows_to_delete.data());
std::vector const& constraints = solver_->constraints();
int const size = constraints.size();
for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
@@ -1208,8 +1616,8 @@ void XpressInterface::ExtractObjective() {
// NOTE: The code assumes that the objective expression does not contain
// any non-zero duplicates.
- int const cols = XPRSgetnumcols(mLp);
- DCHECK_EQ(last_variable_index_, cols);
+ int const cols = getnumcols(mLp);
+ // DCHECK_EQ(last_variable_index_, cols);
unique_ptr ind(new int[cols]);
unique_ptr val(new double[cols]);
@@ -1219,22 +1627,23 @@ void XpressInterface::ExtractObjective() {
}
const auto& coeffs = solver_->objective_->coefficients_;
- for (auto it = coeffs.begin(); it != coeffs.end(); ++it) {
- int const idx = it->first->index();
+ for (auto coeff : coeffs) {
+ int const idx = coeff.first->index();
if (variable_is_extracted(idx)) {
DCHECK_LT(idx, cols);
- val[idx] = it->second;
+ val[idx] = coeff.second;
}
}
CHECK_STATUS(XPRSchgobj(mLp, cols, ind.get(), val.get()));
- CHECK_STATUS(XPRSsetobjoffset(mLp, solver_->Objective().offset()));
+ CHECK_STATUS(setobjoffset(mLp, solver_->Objective().offset()));
}
// ------ Parameters -----
void XpressInterface::SetParameters(const MPSolverParameters& param) {
SetCommonParameters(param);
+ SetScalingMode(param.GetIntegerParam(MPSolverParameters::SCALING));
if (mMip) SetMIPParameters(param);
}
@@ -1256,8 +1665,7 @@ void XpressInterface::SetDualTolerance(double value) {
}
void XpressInterface::SetPresolveMode(int value) {
- MPSolverParameters::PresolveValues const presolve =
- static_cast(value);
+ auto const presolve = static_cast(value);
switch (presolve) {
case MPSolverParameters::PRESOLVE_OFF:
@@ -1272,8 +1680,7 @@ void XpressInterface::SetPresolveMode(int value) {
// Sets the scaling mode.
void XpressInterface::SetScalingMode(int value) {
- MPSolverParameters::ScalingValues const scaling =
- static_cast(value);
+ auto const scaling = static_cast(value);
switch (scaling) {
case MPSolverParameters::SCALING_OFF:
@@ -1282,9 +1689,9 @@ void XpressInterface::SetScalingMode(int value) {
case MPSolverParameters::SCALING_ON:
CHECK_STATUS(XPRSsetdefaultcontrol(mLp, XPRS_SCALING));
// In Xpress, scaling is not a binary on/off control, but a bit vector
- // control setting it to 1 would only enable bit 1. Instead we reset it to
- // its default (163 for the current version 8.6) Alternatively, we could
- // call CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_SCALING, 163));
+ // control setting it to 1 would only enable bit 1. Instead, we reset it
+ // to its default (163 for the current version 8.6) Alternatively, we
+ // could call CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_SCALING, 163));
break;
}
}
@@ -1292,7 +1699,7 @@ void XpressInterface::SetScalingMode(int value) {
// Sets the LP algorithm : primal, dual or barrier. Note that XPRESS offers
// other LP algorithm (e.g. network) and automatic selection
void XpressInterface::SetLpAlgorithm(int value) {
- MPSolverParameters::LpAlgorithmValues const algorithm =
+ auto const algorithm =
static_cast(value);
int alg = 1;
@@ -1315,6 +1722,25 @@ void XpressInterface::SetLpAlgorithm(int value) {
CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_DEFAULTALG, alg));
}
}
+std::vector XpressBasisStatusesFrom(
+ const std::vector& statuses) {
+ std::vector result;
+ result.reserve(statuses.size());
+ std::transform(statuses.cbegin(), statuses.cend(), result.begin(),
+ MPSolverToXpressBasisStatus);
+ return result;
+}
+
+void XpressInterface::SetStartingLpBasis(
+ const std::vector& variable_statuses,
+ const std::vector& constraint_statuses){
+ if (mMip) {
+ LOG(DFATAL) << __FUNCTION__ << " is only available for LP problems";
+ return;
+ }
+ initial_variables_basis_status_ = XpressBasisStatusesFrom(variable_statuses);
+ initial_constraint_basis_status_ = XpressBasisStatusesFrom(constraint_statuses);
+}
bool XpressInterface::readParameters(std::istream& is, char sep) {
// - parameters must be specified as NAME=VALUE
@@ -1372,29 +1798,19 @@ std::string XpressInterface::ValidFileExtensionForParameterFile() const {
return ".prm";
}
-bool XpressInterface::SetSolverSpecificParametersAsString(
- const std::string& parameters) {
- if (parameters.empty()) {
- return true;
- }
- std::stringstream s(parameters);
- return readParameters(s, ';');
-}
-
MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
int status;
- // Delete chached information
- mCstat = 0;
- mRstat = 0;
+ // Delete cached information
+ mCstat.clear();
+ mRstat.clear();
WallTimer timer;
timer.Start();
// Set incrementality
- MPSolverParameters::IncrementalityValues const inc =
- static_cast(
- param.GetIntegerParam(MPSolverParameters::INCREMENTALITY));
+ auto const inc = static_cast(
+ param.GetIntegerParam(MPSolverParameters::INCREMENTALITY));
switch (inc) {
case MPSolverParameters::INCREMENTALITY_OFF: {
Reset(); // This should not be required but re-extracting everything
@@ -1409,14 +1825,20 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
// Extract the model to be solved.
// If we don't support incremental extraction and the low-level modeling
- // is out of sync then we have to re-extract everything.
+ // is out of sync then we have to re-extract everything. Note that this
+ // will lose MIP starts or advanced basis information from a previous
+ // solve.
if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
ExtractModel();
VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
- // Enable log output.
- if (!quiet()) XPRSaddcbmessage(mLp, cbmessage, nullptr, 0);
+ // Set log level.
+ XPRSsetintcontrol(mLp, XPRS_OUTPUTLOG, quiet() ? 0 : 1);
// Set parameters.
+ // NOTE: We must invoke SetSolverSpecificParametersAsString() _first_.
+ // Its current implementation invokes ReadParameterFile() which in
+ // turn invokes XPRSreadcopyparam(). The latter will _overwrite_
+ // all current parameter settings in the environment.
solver_->SetSolverSpecificParametersAsString(
solver_->solver_specific_parameter_string_);
SetParameters(param);
@@ -1425,30 +1847,54 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
// In Xpress, a time limit should usually have a negative sign. With a
// positive sign, the solver will only stop when a solution has been found.
CHECK_STATUS(XPRSsetintcontrol(mLp, XPRS_MAXTIME,
- -1.0 * solver_->time_limit_in_secs()));
+ -1 * solver_->time_limit_in_secs()));
+ }
+
+ // Load basis if present
+ // TODO : check number of variables / constraints
+ if (!mMip && !initial_variables_basis_status_.empty() && !initial_constraint_basis_status_.empty()) {
+ CHECK_STATUS(XPRSloadbasis(mLp, initial_constraint_basis_status_.data(),
+ initial_variables_basis_status_.data()));
+ }
+
+ // Set the hint (if any)
+ this->AddSolutionHintToOptimizer();
+
+ // Add opt node callback to optimizer. We have to do this here (just before
+ // solve) to make sure the variables are fully initialized
+ MPCallbackWrapper* mp_callback_wrapper = nullptr;
+ if (callback_ != nullptr) {
+ mp_callback_wrapper = new MPCallbackWrapper(callback_);
+ CHECK_STATUS(XPRSaddcbintsol(mLp, XpressIntSolCallbackImpl,
+ static_cast(mp_callback_wrapper), 0));
}
- timer.Restart();
// Solve.
- // Do not CHECK_STATUS here since some errors still allow us to query useful
- // information.
- int xpressstat = 0;
+ // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
+ // still allow us to query useful information.
+ timer.Restart();
+
+ int xpress_stat = 0;
if (mMip) {
- if (this->maximize_)
- status = XPRSmaxim(mLp, "g");
- else
- status = XPRSminim(mLp, "g");
- XPRSgetintattrib(mLp, XPRS_MIPSTATUS, &xpressstat);
+ status = XPRSmipoptimize(mLp,"");
+ XPRSgetintattrib(mLp, XPRS_MIPSTATUS, &xpress_stat);
} else {
- if (this->maximize_)
- status = XPRSmaxim(mLp, "");
- else
- status = XPRSminim(mLp, "");
- XPRSgetintattrib(mLp, XPRS_LPSTATUS, &xpressstat);
+ status = XPRSlpoptimize(mLp,"");
+ XPRSgetintattrib(mLp, XPRS_LPSTATUS, &xpress_stat);
+ }
+
+ if (mp_callback_wrapper != nullptr) {
+ mp_callback_wrapper->LogCaughtExceptions();
+ delete mp_callback_wrapper;
+ }
+
+ if (!(mMip ? (xpress_stat == XPRS_MIP_OPTIMAL)
+ : (xpress_stat == XPRS_LP_OPTIMAL))) {
+ XPRSpostsolve(mLp);
}
// Disable screen output right after solve
- XPRSremovecbmessage(mLp, cbmessage, nullptr);
+ XPRSsetintcontrol(mLp, XPRS_OUTPUTLOG, 0);
if (status) {
VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
@@ -1458,16 +1904,16 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
}
- VLOG(1) << absl::StrFormat("XPRESS solution status %d.", xpressstat);
+ VLOG(1) << absl::StrFormat("XPRESS solution status %d.", xpress_stat);
// Figure out what solution we have.
- bool const feasible = (mMip && (xpressstat == XPRS_MIP_OPTIMAL ||
- xpressstat == XPRS_MIP_SOLUTION)) ||
- (!mMip && xpressstat == XPRS_LP_OPTIMAL);
+ bool const feasible = (mMip ? (xpress_stat == XPRS_MIP_OPTIMAL ||
+ xpress_stat == XPRS_MIP_SOLUTION)
+ : (!mMip && xpress_stat == XPRS_LP_OPTIMAL));
// Get problem dimensions for solution queries below.
- int const rows = XPRSgetnumrows(mLp);
- int const cols = XPRSgetnumcols(mLp);
+ int const rows = getnumrows(mLp);
+ int const cols = getnumcols(mLp);
DCHECK_EQ(rows, solver_->constraints_.size());
DCHECK_EQ(cols, solver_->variables_.size());
@@ -1500,15 +1946,15 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
}
}
} else {
- for (int i = 0; i < solver_->variables_.size(); ++i)
- solver_->variables_[i]->set_solution_value(XPRS_NAN);
+ for (auto& variable : solver_->variables_)
+ variable->set_solution_value(XPRS_NAN);
}
// MIP does not have duals
- for (int i = 0; i < solver_->variables_.size(); ++i)
- solver_->variables_[i]->set_reduced_cost(XPRS_NAN);
- for (int i = 0; i < solver_->constraints_.size(); ++i)
- solver_->constraints_[i]->set_dual_value(XPRS_NAN);
+ for (auto& variable : solver_->variables_)
+ variable->set_reduced_cost(XPRS_NAN);
+ for (auto& constraint : solver_->constraints_)
+ constraint->set_dual_value(XPRS_NAN);
} else {
// Continuous problem.
if (cols > 0) {
@@ -1560,7 +2006,7 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
// Map XPRESS status to more generic solution status in MPSolver
if (mMip) {
- switch (xpressstat) {
+ switch (xpress_stat) {
case XPRS_MIP_OPTIMAL:
result_status_ = MPSolver::OPTIMAL;
break;
@@ -1575,7 +2021,7 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
break;
}
} else {
- switch (xpressstat) {
+ switch (xpress_stat) {
case XPRS_LP_OPTIMAL:
result_status_ = MPSolver::OPTIMAL;
break;
@@ -1595,9 +2041,211 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) {
return result_status_;
}
+void XpressInterface::Write(const std::string& filename) {
+ if (sync_status_ == MUST_RELOAD) {
+ Reset();
+ }
+ ExtractModel();
+ VLOG(1) << "Writing Xpress MPS \"" << filename << "\".";
+ const int status = XPRSwriteprob(mLp, filename.c_str(), "");
+ if (status) {
+ LOG(ERROR) << "Xpress: Failed to write MPS!";
+ }
+}
+
MPSolverInterface* BuildXpressInterface(bool mip, MPSolver* const solver) {
return new XpressInterface(solver, mip);
}
+// TODO useless ?
+template
+void splitMyString(const std::string& str, Container& cont, char delim = ' ') {
+ std::stringstream ss(str);
+ std::string token;
+ while (std::getline(ss, token, delim)) {
+ cont.push_back(token);
+ }
+}
+
+const char* stringToCharPtr(std::string& var) { return var.c_str(); }
+
+// Save the existing locale, use the "C" locale to ensure that
+// string -> double conversion is done ignoring the locale.
+struct ScopedLocale {
+ ScopedLocale() {
+ oldLocale = std::setlocale(LC_NUMERIC, nullptr);
+ auto newLocale = std::setlocale(LC_NUMERIC, "C");
+ CHECK_EQ(std::string(newLocale), "C");
+ }
+ ~ScopedLocale() { std::setlocale(LC_NUMERIC, oldLocale); }
+
+ private:
+ const char* oldLocale;
+};
+
+#define setParamIfPossible_MACRO(target_map, setter, converter) \
+ { \
+ auto matchingParamIter = (target_map).find(paramAndValuePair.first); \
+ if (matchingParamIter != (target_map).end()) { \
+ const auto convertedValue = converter(paramAndValuePair.second); \
+ VLOG(1) << "Setting parameter " << paramAndValuePair.first \
+ << " to value " << convertedValue << std::endl; \
+ setter(mLp, matchingParamIter->second, convertedValue); \
+ continue; \
+ } \
+ }
+
+bool XpressInterface::SetSolverSpecificParametersAsString(
+ const std::string& parameters) {
+ if (parameters.empty()) return true;
+
+ std::vector > paramAndValuePairList;
+
+ std::stringstream ss(parameters);
+ std::string paramName;
+ while (std::getline(ss, paramName, ' ')) {
+ std::string paramValue;
+ if (std::getline(ss, paramValue, ' ')) {
+ paramAndValuePairList.push_back(std::make_pair(paramName, paramValue));
+ } else {
+ LOG(ERROR) << "No value for parameter " << paramName << " : function "
+ << __FUNCTION__ << std::endl;
+ return false;
+ }
+ }
+
+ ScopedLocale locale;
+ for (auto& paramAndValuePair : paramAndValuePairList) {
+ setParamIfPossible_MACRO(mapIntegerControls_, XPRSsetintcontrol, std::stoi);
+ setParamIfPossible_MACRO(mapDoubleControls_, XPRSsetdblcontrol, std::stod);
+ setParamIfPossible_MACRO(mapStringControls_, XPRSsetstrcontrol,
+ stringToCharPtr);
+ setParamIfPossible_MACRO(mapInteger64Controls_, XPRSsetintcontrol64,
+ std::stoll);
+ LOG(ERROR) << "Unknown parameter " << paramName << " : function "
+ << __FUNCTION__ << std::endl;
+ return false;
+ }
+ return true;
+}
+
+/*****************************************************************************\
+* Name: optimizermsg
+* Purpose: Display Optimizer error messages and warnings.
+* Arguments: const char *sMsg Message string
+* int nLen Message length
+* int nMsgLvl Message type
+* Return Value: None
+\*****************************************************************************/
+void XPRS_CC optimizermsg(XPRSprob prob, void* data, const char* sMsg, int nLen,
+ int nMsgLvl) {
+ auto* xprs = reinterpret_cast(data);
+ if (!xprs->quiet()) {
+ switch (nMsgLvl) {
+ /* Print Optimizer error messages and warnings */
+ case 4: /* error */
+ case 3: /* warning */
+ /* Ignore other messages */
+ case 2: /* dialogue */
+ case 1: /* information */
+ printf("%*s\n", nLen, sMsg);
+ break;
+ /* Exit and flush buffers */
+ default:
+ fflush(nullptr);
+ break;
+ }
+ }
+}
+
+void XpressInterface::AddSolutionHintToOptimizer() {
+ // Currently the XPRESS API does not handle clearing out previous hints
+ const std::size_t len = solver_->solution_hint_.size();
+ if (len == 0) {
+ // hint is empty, nothing to do
+ return;
+ }
+ unique_ptr col_ind(new int[len]);
+ unique_ptr val(new double[len]);
+
+ for (std::size_t i = 0; i < len; ++i) {
+ col_ind[i] = solver_->solution_hint_[i].first->index();
+ val[i] = solver_->solution_hint_[i].second;
+ }
+ addhint(mLp, len, val.get(), col_ind.get());
+}
+
+void XpressInterface::SetCallback(MPCallback* mp_callback) {
+ if (callback_ != nullptr) {
+ // replace existing callback by removing it first
+ CHECK_STATUS(XPRSremovecbintsol(mLp, XpressIntSolCallbackImpl, NULL));
+ }
+ callback_ = mp_callback;
+}
+
+// This is the call-back called by XPRESS when it finds a new MIP solution
+// NOTE(user): This function must have this exact API, because we are passing
+// it to XPRESS as a callback.
+void XPRS_CC XpressIntSolCallbackImpl(XPRSprob cbprob, void* cbdata) {
+ auto callback_with_context = static_cast(cbdata);
+ if (callback_with_context == nullptr ||
+ callback_with_context->GetCallback() == nullptr) {
+ // nothing to do
+ return;
+ }
+ try {
+ std::unique_ptr cb_context =
+ std::make_unique(
+ &cbprob, MPCallbackEvent::kMipSolution, getnodecnt(cbprob));
+ callback_with_context->GetCallback()->RunCallback(cb_context.get());
+ } catch (std::exception&) {
+ callback_with_context->CatchException(cbprob);
+ }
+}
+
+bool XpressMPCallbackContext::CanQueryVariableValues() {
+ return Event() == MPCallbackEvent::kMipSolution;
+}
+
+double XpressMPCallbackContext::VariableValue(const MPVariable* variable) {
+ if (variable_values_.empty()) {
+ int num_vars = getnumcols(*xprsprob_);
+ variable_values_.resize(num_vars);
+ CHECK_STATUS(XPRSgetmipsol(*xprsprob_, variable_values_.data(), 0));
+ }
+ return variable_values_[variable->index()];
+}
+
+double XpressMPCallbackContext::SuggestSolution(
+ const absl::flat_hash_map& solution) {
+ // Currently the XPRESS API does not handle clearing out previous hints
+ const std::size_t len = solution.size();
+ if (len == 0) {
+ // hint is empty, do nothing
+ return NAN;
+ }
+ if (Event() == MPCallbackEvent::kMipSolution) {
+ // Currently, XPRESS does not handle adding a new MIP solution inside the
+ // "cbintsol" callback (cb for new MIP solutions that is used here)
+ // So we have to prevent the user from adding a solution
+ // TODO: remove this workaround when it is handled in XPRESS
+ LOG(WARNING)
+ << "XPRESS does not currently allow suggesting MIP solutions after "
+ "a kMipSolution event. Try another call-back.";
+ return NAN;
+ }
+ unique_ptr colind(new int[len]);
+ unique_ptr val(new double[len]);
+ int i = 0;
+ for (const auto& [var, value] : solution) {
+ colind[i] = var->index();
+ val[i] = value;
+ ++i;
+ }
+ addhint(*xprsprob_, len, val.get(), colind.get());
+
+ // XPRESS doesn't guarantee if nor when it will test the suggested solution.
+ // So we return NaN because we can't know the actual objective value.
+ return NAN;
+}
} // namespace operations_research
-#endif // #if defined(USE_XPRESS)
diff --git a/ortools/linear_solver/xpress_interface_test.cc b/ortools/linear_solver/xpress_interface_test.cc
new file mode 100644
index 0000000000..2c251824f6
--- /dev/null
+++ b/ortools/linear_solver/xpress_interface_test.cc
@@ -0,0 +1,1362 @@
+#include
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "ortools/base/init_google.h"
+#include "ortools/linear_solver/linear_solver.h"
+#include "ortools/xpress/environment.h"
+#define XPRS_NAMELENGTH 1028
+
+namespace operations_research {
+
+#define EXPECT_STATUS(s) \
+ do { \
+ int const status_ = s; \
+ EXPECT_EQ(0, status_) << "Nonzero return status"; \
+ } while (0)
+
+class XPRSGetter {
+ public:
+ XPRSGetter(MPSolver* solver) : solver_(solver) {}
+
+ int getNumVariables() {
+ int cols;
+ EXPECT_STATUS(XPRSgetintattrib(prob(), XPRS_COLS, &cols));
+ return cols;
+ }
+
+ int getNumConstraints() {
+ int cols;
+ EXPECT_STATUS(XPRSgetintattrib(prob(), XPRS_ROWS, &cols));
+ return cols;
+ }
+
+ std::string getRowName(int n) {
+ EXPECT_LT(n, getNumConstraints());
+ return getName(n, XPRS_NAMES_ROW);
+ }
+
+ double getLb(int n) {
+ EXPECT_LT(n, getNumVariables());
+ double lb;
+ EXPECT_STATUS(XPRSgetlb(prob(), &lb, n, n));
+ return lb;
+ }
+
+ double getUb(int n) {
+ EXPECT_LT(n, getNumVariables());
+ double ub;
+ EXPECT_STATUS(XPRSgetub(prob(), &ub, n, n));
+ return ub;
+ }
+
+ std::string getColName(int n) {
+ EXPECT_LT(n, getNumVariables());
+ return getName(n, XPRS_NAMES_COLUMN);
+ }
+
+ char getVariableType(int n) {
+ EXPECT_LT(n, getNumVariables());
+ char type;
+ EXPECT_STATUS(XPRSgetcoltype(prob(), &type, n, n));
+ return type;
+ }
+
+ char getConstraintType(int n) {
+ EXPECT_LT(n, getNumConstraints());
+ char type;
+ EXPECT_STATUS(XPRSgetrowtype(prob(), &type, n, n));
+ return type;
+ }
+
+ double getConstraintRhs(int n) {
+ EXPECT_LT(n, getNumConstraints());
+ double rhs;
+ EXPECT_STATUS(XPRSgetrhs(prob(), &rhs, n, n));
+ return rhs;
+ }
+
+ double getConstraintRange(int n) {
+ EXPECT_LT(n, getNumConstraints());
+ double range;
+ EXPECT_STATUS(XPRSgetrhsrange(prob(), &range, n, n));
+ return range;
+ }
+
+ double getConstraintCoef(int row, int col) {
+ EXPECT_LT(col, getNumVariables());
+ EXPECT_LT(row, getNumConstraints());
+ double coef;
+ EXPECT_STATUS(XPRSgetcoef(prob(), row, col, &coef));
+ return coef;
+ }
+
+ double getObjectiveCoef(int n) {
+ EXPECT_LT(n, getNumVariables());
+ double objCoef;
+ EXPECT_STATUS(XPRSgetobj(prob(), &objCoef, n, n));
+ return objCoef;
+ }
+
+ double getObjectiveOffset() {
+ double offset;
+ EXPECT_STATUS(XPRSgetdblattrib(prob(), XPRS_OBJRHS, &offset));
+ return offset;
+ }
+
+ double getObjectiveSense() {
+ double sense;
+ EXPECT_STATUS(XPRSgetdblattrib(prob(), XPRS_OBJSENSE, &sense));
+ return sense;
+ }
+
+ std::string getStringControl(int control) {
+ std::string value(280, '\0');
+ int valueSize;
+ EXPECT_STATUS(XPRSgetstringcontrol(prob(), control, &value[0], value.size(),
+ &valueSize));
+ value.resize(valueSize - 1);
+ return value;
+ }
+
+ double getDoubleControl(int control) {
+ double value;
+ EXPECT_STATUS(XPRSgetdblcontrol(prob(), control, &value));
+ return value;
+ }
+
+ int getIntegerControl(int control) {
+ int value;
+ EXPECT_STATUS(XPRSgetintcontrol(prob(), control, &value));
+ return value;
+ }
+
+ int getInteger64Control(int control) {
+ XPRSint64 value;
+ EXPECT_STATUS(XPRSgetintcontrol64(prob(), control, &value));
+ return value;
+ }
+
+ private:
+ MPSolver* solver_;
+
+ XPRSprob prob() { return (XPRSprob)solver_->underlying_solver(); }
+
+ std::string getName(int n, int type) {
+ int namelength;
+ EXPECT_STATUS(XPRSgetintattrib(prob(), XPRS_NAMELENGTH, &namelength));
+
+ std::string name;
+ name.resize(8 * namelength + 1);
+ EXPECT_STATUS(XPRSgetnames(prob(), type, name.data(), n, n));
+
+ name.erase(std::find_if(name.rbegin(), name.rend(),
+ [](unsigned char ch) {
+ return !std::isspace(ch) && ch != '\0';
+ })
+ .base(),
+ name.end());
+
+ return name;
+ }
+};
+
+#define UNITTEST_INIT_MIP() \
+ MPSolver solver("XPRESS_MIP", MPSolver::XPRESS_MIXED_INTEGER_PROGRAMMING); \
+ XPRSGetter getter(&solver)
+#define UNITTEST_INIT_LP() \
+ MPSolver solver("XPRESS_LP", MPSolver::XPRESS_LINEAR_PROGRAMMING); \
+ XPRSGetter getter(&solver)
+
+void _unittest_verify_var(XPRSGetter* getter, MPVariable* x, char type,
+ double lb, double ub) {
+ EXPECT_EQ(getter->getVariableType(x->index()), type);
+ EXPECT_EQ(getter->getLb(x->index()), lb);
+ EXPECT_EQ(getter->getUb(x->index()), ub);
+}
+
+void _unittest_verify_constraint(XPRSGetter* getter, MPConstraint* c, char type,
+ double lb, double ub) {
+ int idx = c->index();
+ EXPECT_EQ(getter->getConstraintType(idx), type);
+ switch (type) {
+ case 'L':
+ EXPECT_EQ(getter->getConstraintRhs(idx), ub);
+ break;
+ case 'U':
+ EXPECT_EQ(getter->getConstraintRhs(idx), lb);
+ break;
+ case 'E':
+ EXPECT_EQ(getter->getConstraintRhs(idx), ub);
+ EXPECT_EQ(getter->getConstraintRhs(idx), lb);
+ break;
+ case 'R':
+ EXPECT_EQ(getter->getConstraintRhs(idx), ub);
+ EXPECT_EQ(getter->getConstraintRange(idx), ub - lb);
+ break;
+ }
+}
+
+void buildLargeMip(MPSolver& solver, int numVars, int maxTime) {
+ // Build a random but big and complicated MIP with numVars integer variables
+ // And every variable has a coupling constraint with all previous ones
+ srand(123);
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetMaximization();
+ for (int i = 0; i < numVars; ++i) {
+ MPVariable* x = solver.MakeIntVar(-rand() % 200, rand() % 200,
+ "x_" + std::to_string(i));
+ obj->SetCoefficient(x, rand() % 200 - 100);
+ if (i == 0) {
+ continue;
+ }
+ int rand1 = -rand() % 2000;
+ int rand2 = rand() % 2000;
+ int min = std::min(rand1, rand2);
+ int max = std::max(rand1, rand2);
+ MPConstraint* c = solver.MakeRowConstraint(min, max);
+ c->SetCoefficient(x, rand() % 200 - 100);
+ for (int j = 0; j < i; ++j) {
+ c->SetCoefficient(solver.variable(j), rand() % 200 - 100);
+ }
+ }
+ solver.SetSolverSpecificParametersAsString("PRESOLVE 0 MAXTIME " +
+ std::to_string(maxTime));
+ solver.EnableOutput();
+}
+
+void buildLargeLp(MPSolver& solver, int numVars) {
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetMaximization();
+ for (int i = 0; i < numVars; ++i) {
+ MPVariable* x = solver.MakeNumVar(-(i * i) % 21,
+ (i * i) % 55,
+ "x_" + std::to_string(i));
+ obj->SetCoefficient(x, (i * i) % 23);
+ int min = -50;
+ int max = (i * i) % 664 + 55;
+ MPConstraint* c = solver.MakeRowConstraint(min, max);
+ c->SetCoefficient(x, i % 331);
+ for (int j = 0; j < i; ++j) {
+ c->SetCoefficient(solver.variable(j), i + j);
+ }
+ }
+ solver.EnableOutput();
+}
+
+class MyMPCallback : public MPCallback {
+ private:
+ MPSolver* mpSolver_;
+ int nSolutions_ = 0;
+ std::vector last_variable_values_;
+ bool should_throw_;
+
+ public:
+ MyMPCallback(MPSolver* mpSolver, bool should_throw)
+ : MPCallback(false, false),
+ mpSolver_(mpSolver),
+ should_throw_(should_throw){};
+
+ ~MyMPCallback() override{};
+
+ void RunCallback(MPCallbackContext* callback_context) override {
+ if (should_throw_) {
+ throw std::runtime_error("This is a mocked exception in MyMPCallback");
+ }
+ // XpressMPCallbackContext* context_ =
+ // static_cast(callback_context);
+ ++nSolutions_;
+ EXPECT_TRUE(callback_context->CanQueryVariableValues());
+ EXPECT_EQ(callback_context->Event(), MPCallbackEvent::kMipSolution);
+ last_variable_values_.resize(mpSolver_->NumVariables(), 0.0);
+ for (int i = 0; i < mpSolver_->NumVariables(); i++) {
+ last_variable_values_[i] =
+ callback_context->VariableValue(mpSolver_->variable(i));
+ }
+ };
+
+ int getNSolutions() const { return nSolutions_; }
+ double getLastVariableValue(int index) const {
+ return last_variable_values_[index];
+ }
+};
+
+MyMPCallback* buildLargeMipWithCallback(MPSolver& solver, int numVars,
+ int maxTime) {
+ buildLargeMip(solver, numVars, maxTime);
+ MPCallback* mpCallback = new MyMPCallback(&solver, false);
+ solver.SetCallback(nullptr); // just to test that this does not cause failure
+ solver.SetCallback(mpCallback);
+ return static_cast(mpCallback);
+}
+
+TEST(XpressInterface, isMIP) {
+ UNITTEST_INIT_MIP();
+ EXPECT_EQ(solver.IsMIP(), true);
+}
+
+TEST(XpressInterface, isLP) {
+ UNITTEST_INIT_LP();
+ EXPECT_EQ(solver.IsMIP(), false);
+}
+
+TEST(XpressInterface, LpStartingBasis) {
+ UNITTEST_INIT_LP();
+ buildLargeLp(solver, 1000);
+ // First, we record the number of iterations without an initial basis
+ solver.Solve();
+ const auto iterInit = solver.iterations();
+ EXPECT_GE(iterInit, 1000);
+
+ // Here, we retrieve the final basis
+ std::vector varStatus, constrStatus;
+ for (auto* var : solver.variables()) {
+ varStatus.push_back(var->basis_status());
+ }
+ for (auto* constr : solver.constraints()) {
+ constrStatus.push_back(constr->basis_status());
+ }
+
+ // Then we slightly modify the problem...
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetCoefficient(solver.variable(1), 100);
+ // Here, we provide the final basis of the previous (similar) problem
+ solver.SetStartingLpBasis(varStatus, constrStatus);
+ solver.Solve();
+ const auto iterWithBasis = solver.iterations();
+ // ...and check that few iterations have been performed
+ EXPECT_LT(iterWithBasis, 10);
+}
+
+TEST(XpressInterface, NumVariables) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeNumVar(-1., 5.1, "x1");
+ MPVariable* x2 = solver.MakeNumVar(3.14, 5.1, "x2");
+ std::vector xs;
+ solver.MakeBoolVarArray(500, "xs", &xs);
+ solver.Solve();
+ EXPECT_EQ(getter.getNumVariables(), 502);
+}
+
+TEST(XpressInterface, NumConstraints) {
+ UNITTEST_INIT_MIP();
+ solver.MakeRowConstraint(12., 100.0);
+ solver.MakeRowConstraint(13., 13.1);
+ solver.MakeRowConstraint(12.1, 1000.0);
+ solver.Solve();
+ EXPECT_EQ(getter.getNumConstraints(), 3);
+}
+
+TEST(XpressInterface, Reset) {
+ UNITTEST_INIT_MIP();
+ solver.MakeBoolVar("x1");
+ solver.MakeBoolVar("x2");
+ solver.MakeRowConstraint(12., 100.0);
+ solver.Solve();
+ EXPECT_EQ(getter.getNumConstraints(), 1);
+ EXPECT_EQ(getter.getNumVariables(), 2);
+ solver.Reset();
+ EXPECT_EQ(getter.getNumConstraints(), 0);
+ EXPECT_EQ(getter.getNumVariables(), 0);
+}
+
+TEST(XpressInterface, MakeIntVar) {
+ UNITTEST_INIT_MIP();
+ int lb = 0, ub = 10;
+ MPVariable* x = solver.MakeIntVar(lb, ub, "x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, 'I', lb, ub);
+}
+
+TEST(XpressInterface, MakeNumVar) {
+ UNITTEST_INIT_MIP();
+ double lb = 1.5, ub = 158.2;
+ MPVariable* x = solver.MakeNumVar(lb, ub, "x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, 'C', lb, ub);
+}
+
+TEST(XpressInterface, MakeBoolVar) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x = solver.MakeBoolVar("x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, 'B', 0, 1);
+}
+
+TEST(XpressInterface, MakeIntVarArray) {
+ UNITTEST_INIT_MIP();
+ int n1 = 25, lb1 = -7, ub1 = 18;
+ std::vector xs1;
+ solver.MakeIntVarArray(n1, lb1, ub1, "xs1", &xs1);
+ int n2 = 37, lb2 = 19, ub2 = 189;
+ std::vector xs2;
+ solver.MakeIntVarArray(n2, lb2, ub2, "xs2", &xs2);
+ solver.Solve();
+ for (int i = 0; i < n1; ++i) {
+ _unittest_verify_var(&getter, xs1[i], 'I', lb1, ub1);
+ }
+ for (int i = 0; i < n2; ++i) {
+ _unittest_verify_var(&getter, xs2[i], 'I', lb2, ub2);
+ }
+}
+
+TEST(XpressInterface, MakeNumVarArray) {
+ UNITTEST_INIT_MIP();
+ int n1 = 1;
+ double lb1 = 5.1, ub1 = 8.1;
+ std::vector xs1;
+ solver.MakeNumVarArray(n1, lb1, ub1, "xs1", &xs1);
+ int n2 = 13;
+ double lb2 = -11.5, ub2 = 189.9;
+ std::vector xs2;
+ solver.MakeNumVarArray(n2, lb2, ub2, "xs2", &xs2);
+ solver.Solve();
+ for (int i = 0; i < n1; ++i) {
+ _unittest_verify_var(&getter, xs1[i], 'C', lb1, ub1);
+ }
+ for (int i = 0; i < n2; ++i) {
+ _unittest_verify_var(&getter, xs2[i], 'C', lb2, ub2);
+ }
+}
+
+TEST(XpressInterface, MakeBoolVarArray) {
+ UNITTEST_INIT_MIP();
+ double n = 43;
+ std::vector xs;
+ solver.MakeBoolVarArray(n, "xs", &xs);
+ solver.Solve();
+ for (int i = 0; i < n; ++i) {
+ _unittest_verify_var(&getter, xs[i], 'B', 0, 1);
+ }
+}
+
+TEST(XpressInterface, SetVariableBounds) {
+ UNITTEST_INIT_MIP();
+ int lb1 = 3, ub1 = 4;
+ MPVariable* x1 = solver.MakeIntVar(lb1, ub1, "x1");
+ double lb2 = 3.7, ub2 = 4;
+ MPVariable* x2 = solver.MakeNumVar(lb2, ub2, "x2");
+ solver.Solve();
+ _unittest_verify_var(&getter, x1, 'I', lb1, ub1);
+ _unittest_verify_var(&getter, x2, 'C', lb2, ub2);
+ lb1 = 12, ub1 = 15;
+ x1->SetBounds(lb1, ub1);
+ lb2 = -1.1, ub2 = 0;
+ x2->SetBounds(lb2, ub2);
+ solver.Solve();
+ _unittest_verify_var(&getter, x1, 'I', lb1, ub1);
+ _unittest_verify_var(&getter, x2, 'C', lb2, ub2);
+}
+
+TEST(XpressInterface, SetVariableInteger) {
+ UNITTEST_INIT_MIP();
+ int lb = -1, ub = 7;
+ MPVariable* x = solver.MakeIntVar(lb, ub, "x");
+ solver.Solve();
+ _unittest_verify_var(&getter, x, 'I', lb, ub);
+ x->SetInteger(false);
+ solver.Solve();
+ _unittest_verify_var(&getter, x, 'C', lb, ub);
+}
+
+TEST(XpressInterface, ConstraintL) {
+ UNITTEST_INIT_MIP();
+ double lb = -solver.infinity(), ub = 10.;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'L', lb, ub);
+}
+
+TEST(XpressInterface, ConstraintR) {
+ UNITTEST_INIT_MIP();
+ double lb = -2, ub = -1;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'R', lb, ub);
+}
+
+TEST(XpressInterface, ConstraintG) {
+ UNITTEST_INIT_MIP();
+ double lb = 8.1, ub = solver.infinity();
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'G', lb, ub);
+}
+
+TEST(XpressInterface, ConstraintE) {
+ UNITTEST_INIT_MIP();
+ double lb = 18947.3, ub = lb;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'E', lb, ub);
+}
+
+TEST(XpressInterface, SetConstraintBoundsL) {
+ UNITTEST_INIT_MIP();
+ double lb = 18947.3, ub = lb;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'E', lb, ub);
+ lb = -solver.infinity(), ub = 16.6;
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'L', lb, ub);
+}
+
+TEST(XpressInterface, SetConstraintBoundsR) {
+ UNITTEST_INIT_MIP();
+ double lb = -solver.infinity(), ub = 15;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'L', lb, ub);
+ lb = 0, ub = 0.1;
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'R', lb, ub);
+}
+
+TEST(XpressInterface, SetConstraintBoundsG) {
+ UNITTEST_INIT_MIP();
+ double lb = 1, ub = 2;
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'R', lb, ub);
+ lb = 5, ub = solver.infinity();
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'G', lb, ub);
+}
+
+TEST(XpressInterface, SetConstraintBoundsE) {
+ UNITTEST_INIT_MIP();
+ double lb = -1, ub = solver.infinity();
+ MPConstraint* c = solver.MakeRowConstraint(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'G', lb, ub);
+ lb = 128, ub = lb;
+ c->SetBounds(lb, ub);
+ solver.Solve();
+ _unittest_verify_constraint(&getter, c, 'E', lb, ub);
+}
+
+TEST(XpressInterface, ConstraintCoef) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeBoolVar("x1");
+ MPVariable* x2 = solver.MakeBoolVar("x2");
+ MPConstraint* c1 = solver.MakeRowConstraint(4.1, solver.infinity());
+ MPConstraint* c2 = solver.MakeRowConstraint(-solver.infinity(), 0.1);
+ double c11 = -15.6, c12 = 0.4, c21 = -11, c22 = 4.5;
+ c1->SetCoefficient(x1, c11);
+ c1->SetCoefficient(x2, c12);
+ c2->SetCoefficient(x1, c21);
+ c2->SetCoefficient(x2, c22);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), c11);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), c12);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), c21);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), c22);
+ c11 = 0.11, c12 = 0.12, c21 = 0.21, c22 = 0.22;
+ c1->SetCoefficient(x1, c11);
+ c1->SetCoefficient(x2, c12);
+ c2->SetCoefficient(x1, c21);
+ c2->SetCoefficient(x2, c22);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), c11);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), c12);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), c21);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), c22);
+}
+
+TEST(XpressInterface, ClearConstraint) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeBoolVar("x1");
+ MPVariable* x2 = solver.MakeBoolVar("x2");
+ MPConstraint* c1 = solver.MakeRowConstraint(4.1, solver.infinity());
+ MPConstraint* c2 = solver.MakeRowConstraint(-solver.infinity(), 0.1);
+ double c11 = -1533.6, c12 = 3.4, c21 = -11000, c22 = 0.0001;
+ c1->SetCoefficient(x1, c11);
+ c1->SetCoefficient(x2, c12);
+ c2->SetCoefficient(x1, c21);
+ c2->SetCoefficient(x2, c22);
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), c11);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), c12);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), c21);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), c22);
+ c1->Clear();
+ c2->Clear();
+ solver.Solve();
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x1->index()), 0);
+ EXPECT_EQ(getter.getConstraintCoef(c1->index(), x2->index()), 0);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x1->index()), 0);
+ EXPECT_EQ(getter.getConstraintCoef(c2->index(), x2->index()), 0);
+}
+
+TEST(XpressInterface, ObjectiveCoef) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double coef = 3112.4;
+ obj->SetCoefficient(x, coef);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), coef);
+ coef = 0.2;
+ obj->SetCoefficient(x, coef);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), coef);
+}
+
+TEST(XpressInterface, ObjectiveOffset) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double offset = 4.3;
+ obj->SetOffset(offset);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveOffset(), offset);
+ offset = 3.6;
+ obj->SetOffset(offset);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveOffset(), offset);
+}
+
+TEST(XpressInterface, ClearObjective) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x = solver.MakeBoolVar("x");
+ MPObjective* obj = solver.MutableObjective();
+ double coef = -15.6;
+ obj->SetCoefficient(x, coef);
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), coef);
+ obj->Clear();
+ solver.Solve();
+ EXPECT_EQ(getter.getObjectiveCoef(x->index()), 0);
+}
+
+TEST(XpressInterface, ObjectiveSense) {
+ UNITTEST_INIT_MIP();
+ MPObjective* const objective = solver.MutableObjective();
+ objective->SetMinimization();
+ EXPECT_EQ(getter.getObjectiveSense(), XPRS_OBJ_MINIMIZE);
+ objective->SetMaximization();
+ EXPECT_EQ(getter.getObjectiveSense(), XPRS_OBJ_MAXIMIZE);
+}
+
+TEST(XpressInterface, interations) {
+ UNITTEST_INIT_LP();
+ int nc = 100, nv = 100;
+ std::vector cs(nc);
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[ci] = solver.MakeRowConstraint(ci, ci + 1);
+ }
+ MPObjective* const objective = solver.MutableObjective();
+ for (int vi = 0; vi < nv; ++vi) {
+ MPVariable* v = solver.MakeNumVar(0, nv, "x" + std::to_string(vi));
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[ci]->SetCoefficient(v, vi + ci);
+ }
+ objective->SetCoefficient(v, 1);
+ }
+ solver.Solve();
+ EXPECT_GT(solver.iterations(), 0);
+}
+
+TEST(XpressInterface, nodes) {
+ UNITTEST_INIT_MIP();
+ int nc = 100, nv = 100;
+ std::vector cs(nc);
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[ci] = solver.MakeRowConstraint(ci, ci + 1);
+ }
+ MPObjective* const objective = solver.MutableObjective();
+ for (int vi = 0; vi < nv; ++vi) {
+ MPVariable* v = solver.MakeIntVar(0, nv, "x" + std::to_string(vi));
+ for (int ci = 0; ci < nc; ++ci) {
+ cs[ci]->SetCoefficient(v, vi + ci);
+ }
+ objective->SetCoefficient(v, 1);
+ }
+ solver.Solve();
+ EXPECT_GT(solver.nodes(), 0);
+}
+
+TEST(XpressInterface, SolverVersion) {
+ UNITTEST_INIT_MIP();
+ EXPECT_GE(solver.SolverVersion().size(), 30);
+}
+
+TEST(XpressInterface, Write) {
+ UNITTEST_INIT_MIP();
+ MPVariable* x1 = solver.MakeIntVar(-1.2, 9.3, "C1");
+ MPVariable* x2 = solver.MakeNumVar(-1, 5.147593849384714, "C2");
+ MPConstraint* c1 = solver.MakeRowConstraint(-solver.infinity(), 1, "R1");
+ c1->SetCoefficient(x1, 3);
+ c1->SetCoefficient(x2, 1.5);
+ MPConstraint* c2 = solver.MakeRowConstraint(3, 5, "R2");
+ c2->SetCoefficient(x2, -1.1122334455667788);
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetMaximization();
+ obj->SetCoefficient(x1, 1);
+ obj->SetCoefficient(x2, 2);
+
+ const std::filesystem::path temporary_working_dir =
+ std::filesystem::temp_directory_path() / "temporary_working_dir";
+ std::filesystem::create_directories(temporary_working_dir);
+
+ std::string tmpName = (temporary_working_dir / "dummy.mps").string();
+ solver.Write(tmpName);
+
+ std::ifstream tmpFile(tmpName);
+ std::stringstream tmpBuffer;
+ tmpBuffer << tmpFile.rdbuf();
+ tmpFile.close();
+ std::filesystem::remove_all(temporary_working_dir);
+
+ EXPECT_EQ(tmpBuffer.str(), R"(NAME newProb
+OBJSENSE MAXIMIZE
+ROWS
+ N __OBJ___
+ L R1
+ L R2
+COLUMNS
+ C1 __OBJ___ 1
+ C1 R1 3
+ C2 __OBJ___ 2
+ C2 R1 1.5
+ C2 R2 -1.1122334455667788
+RHS
+ RHS00001 R1 1
+ RHS00001 R2 5
+RANGES
+ RNG00001 R2 2
+BOUNDS
+ UI BND00001 C1 9
+ LO BND00001 C1 -1
+ UP BND00001 C2 5.147593849384714
+ LO BND00001 C2 -1
+ENDATA
+)");
+}
+
+TEST(XpressInterface, SetPrimalTolerance) {
+ UNITTEST_INIT_LP();
+ MPSolverParameters params;
+ double tol = 1e-4;
+ params.SetDoubleParam(MPSolverParameters::PRIMAL_TOLERANCE, tol);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getDoubleControl(XPRS_FEASTOL), tol);
+}
+
+TEST(XpressInterface, SetDualTolerance) {
+ UNITTEST_INIT_LP();
+ MPSolverParameters params;
+ double tol = 1e-2;
+ params.SetDoubleParam(MPSolverParameters::DUAL_TOLERANCE, tol);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getDoubleControl(XPRS_OPTIMALITYTOL), tol);
+}
+
+TEST(XpressInterface, SetPresolveMode) {
+ UNITTEST_INIT_MIP();
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::PRESOLVE,
+ MPSolverParameters::PRESOLVE_OFF);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_PRESOLVE), 0);
+ params.SetIntegerParam(MPSolverParameters::PRESOLVE,
+ MPSolverParameters::PRESOLVE_ON);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_PRESOLVE), 1);
+}
+
+TEST(XpressInterface, SetLpAlgorithm) {
+ UNITTEST_INIT_LP();
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM,
+ MPSolverParameters::DUAL);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_DEFAULTALG), 2);
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM,
+ MPSolverParameters::PRIMAL);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_DEFAULTALG), 3);
+ params.SetIntegerParam(MPSolverParameters::LP_ALGORITHM,
+ MPSolverParameters::BARRIER);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_DEFAULTALG), 4);
+}
+
+TEST(XpressInterface, SetScaling) {
+ UNITTEST_INIT_MIP();
+ MPSolverParameters params;
+ params.SetIntegerParam(MPSolverParameters::SCALING,
+ MPSolverParameters::SCALING_OFF);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_SCALING), 0);
+ params.SetIntegerParam(MPSolverParameters::SCALING,
+ MPSolverParameters::SCALING_ON);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getIntegerControl(XPRS_SCALING), 163);
+}
+
+TEST(XpressInterface, SetRelativeMipGap) {
+ UNITTEST_INIT_MIP();
+ MPSolverParameters params;
+ double relativeMipGap = 1e-3;
+ params.SetDoubleParam(MPSolverParameters::RELATIVE_MIP_GAP, relativeMipGap);
+ solver.Solve(params);
+ EXPECT_EQ(getter.getDoubleControl(XPRS_MIPRELSTOP), relativeMipGap);
+}
+
+TEST(XpressInterface, setStringControls) {
+ std::vector> params = {
+ {"MPSRHSNAME", XPRS_MPSRHSNAME, "default_value"},
+ {"MPSOBJNAME", XPRS_MPSOBJNAME, "default_value"},
+ {"MPSRANGENAME", XPRS_MPSRANGENAME, "default_value"},
+ {"MPSBOUNDNAME", XPRS_MPSBOUNDNAME, "default_value"},
+ {"OUTPUTMASK", XPRS_OUTPUTMASK, "default_value"},
+ {"TUNERMETHODFILE", XPRS_TUNERMETHODFILE, "default_value"},
+ {"TUNEROUTPUTPATH", XPRS_TUNEROUTPUTPATH, "default_value"},
+ {"TUNERSESSIONNAME", XPRS_TUNERSESSIONNAME, "default_value"},
+ {"COMPUTEEXECSERVICE", XPRS_COMPUTEEXECSERVICE, "default_value"},
+ };
+ for (const auto& [paramString, control, paramValue] : params) {
+ UNITTEST_INIT_MIP();
+ std::string xpressParamString = paramString + " " + paramValue;
+ solver.SetSolverSpecificParametersAsString(xpressParamString);
+ EXPECT_EQ(paramValue, getter.getStringControl(control));
+ }
+}
+
+TEST(XpressInterface, setDoubleControls) {
+ std::vector> params = {
+ {"MAXCUTTIME", XPRS_MAXCUTTIME, 1.},
+ {"MAXSTALLTIME", XPRS_MAXSTALLTIME, 1.},
+ {"TUNERMAXTIME", XPRS_TUNERMAXTIME, 1.},
+ {"MATRIXTOL", XPRS_MATRIXTOL, 1.},
+ {"PIVOTTOL", XPRS_PIVOTTOL, 1.},
+ {"FEASTOL", XPRS_FEASTOL, 1.},
+ {"OUTPUTTOL", XPRS_OUTPUTTOL, 1.},
+ {"SOSREFTOL", XPRS_SOSREFTOL, 1.},
+ {"OPTIMALITYTOL", XPRS_OPTIMALITYTOL, 1.},
+ {"ETATOL", XPRS_ETATOL, 1.},
+ {"RELPIVOTTOL", XPRS_RELPIVOTTOL, 1.},
+ {"MIPTOL", XPRS_MIPTOL, 1.},
+ {"MIPTOLTARGET", XPRS_MIPTOLTARGET, 1.},
+ {"BARPERTURB", XPRS_BARPERTURB, 1.},
+ {"MIPADDCUTOFF", XPRS_MIPADDCUTOFF, 1.},
+ {"MIPABSCUTOFF", XPRS_MIPABSCUTOFF, 1.},
+ {"MIPRELCUTOFF", XPRS_MIPRELCUTOFF, 1.},
+ {"PSEUDOCOST", XPRS_PSEUDOCOST, 1.},
+ {"PENALTY", XPRS_PENALTY, 1.},
+ {"BIGM", XPRS_BIGM, 1.},
+ {"MIPABSSTOP", XPRS_MIPABSSTOP, 1.},
+ {"MIPRELSTOP", XPRS_MIPRELSTOP, 1.},
+ {"CROSSOVERACCURACYTOL", XPRS_CROSSOVERACCURACYTOL, 1.},
+ {"PRIMALPERTURB", XPRS_PRIMALPERTURB, 1.},
+ {"DUALPERTURB", XPRS_DUALPERTURB, 1.},
+ {"BAROBJSCALE", XPRS_BAROBJSCALE, 1.},
+ {"BARRHSSCALE", XPRS_BARRHSSCALE, 1.},
+ {"CHOLESKYTOL", XPRS_CHOLESKYTOL, 1.},
+ {"BARGAPSTOP", XPRS_BARGAPSTOP, 1.},
+ {"BARDUALSTOP", XPRS_BARDUALSTOP, 1.},
+ {"BARPRIMALSTOP", XPRS_BARPRIMALSTOP, 1.},
+ {"BARSTEPSTOP", XPRS_BARSTEPSTOP, 1.},
+ {"ELIMTOL", XPRS_ELIMTOL, 1.},
+ {"MARKOWITZTOL", XPRS_MARKOWITZTOL, 1.},
+ {"MIPABSGAPNOTIFY", XPRS_MIPABSGAPNOTIFY, 1.},
+ {"MIPRELGAPNOTIFY", XPRS_MIPRELGAPNOTIFY, 1.},
+ {"BARLARGEBOUND", XPRS_BARLARGEBOUND, 1.},
+ {"PPFACTOR", XPRS_PPFACTOR, 1.},
+ {"REPAIRINDEFINITEQMAX", XPRS_REPAIRINDEFINITEQMAX, 1.},
+ {"BARGAPTARGET", XPRS_BARGAPTARGET, 1.},
+ {"DUMMYCONTROL", XPRS_DUMMYCONTROL, 1.},
+ {"BARSTARTWEIGHT", XPRS_BARSTARTWEIGHT, 1.},
+ {"BARFREESCALE", XPRS_BARFREESCALE, 1.},
+ {"SBEFFORT", XPRS_SBEFFORT, 1.},
+ {"HEURDIVERANDOMIZE", XPRS_HEURDIVERANDOMIZE, 1.},
+ {"HEURSEARCHEFFORT", XPRS_HEURSEARCHEFFORT, 1.},
+ {"CUTFACTOR", XPRS_CUTFACTOR, 1.},
+ {"EIGENVALUETOL", XPRS_EIGENVALUETOL, 1.},
+ {"INDLINBIGM", XPRS_INDLINBIGM, 1.},
+ {"TREEMEMORYSAVINGTARGET", XPRS_TREEMEMORYSAVINGTARGET, 1.},
+ {"INDPRELINBIGM", XPRS_INDPRELINBIGM, 1.},
+ {"RELAXTREEMEMORYLIMIT", XPRS_RELAXTREEMEMORYLIMIT, 1.},
+ {"MIPABSGAPNOTIFYOBJ", XPRS_MIPABSGAPNOTIFYOBJ, 1.},
+ {"MIPABSGAPNOTIFYBOUND", XPRS_MIPABSGAPNOTIFYBOUND, 1.},
+ {"PRESOLVEMAXGROW", XPRS_PRESOLVEMAXGROW, 1.},
+ {"HEURSEARCHTARGETSIZE", XPRS_HEURSEARCHTARGETSIZE, 1.},
+ {"CROSSOVERRELPIVOTTOL", XPRS_CROSSOVERRELPIVOTTOL, 1.},
+ {"CROSSOVERRELPIVOTTOLSAFE", XPRS_CROSSOVERRELPIVOTTOLSAFE, 1.},
+ {"DETLOGFREQ", XPRS_DETLOGFREQ, 1.},
+ {"MAXIMPLIEDBOUND", XPRS_MAXIMPLIEDBOUND, 1.},
+ {"FEASTOLTARGET", XPRS_FEASTOLTARGET, 1.},
+ {"OPTIMALITYTOLTARGET", XPRS_OPTIMALITYTOLTARGET, 1.},
+ {"PRECOMPONENTSEFFORT", XPRS_PRECOMPONENTSEFFORT, 1.},
+ {"LPLOGDELAY", XPRS_LPLOGDELAY, 1.},
+ {"HEURDIVEITERLIMIT", XPRS_HEURDIVEITERLIMIT, 1.},
+ {"BARKERNEL", XPRS_BARKERNEL, 1.},
+ {"FEASTOLPERTURB", XPRS_FEASTOLPERTURB, 1.},
+ {"CROSSOVERFEASWEIGHT", XPRS_CROSSOVERFEASWEIGHT, 1.},
+ {"LUPIVOTTOL", XPRS_LUPIVOTTOL, 1.},
+ {"MIPRESTARTGAPTHRESHOLD", XPRS_MIPRESTARTGAPTHRESHOLD, 1.},
+ {"NODEPROBINGEFFORT", XPRS_NODEPROBINGEFFORT, 1.},
+ {"INPUTTOL", XPRS_INPUTTOL, 1.},
+ {"MIPRESTARTFACTOR", XPRS_MIPRESTARTFACTOR, 1.},
+ {"BAROBJPERTURB", XPRS_BAROBJPERTURB, 1.},
+ {"CPIALPHA", XPRS_CPIALPHA, 1.},
+ {"GLOBALBOUNDINGBOX", XPRS_GLOBALBOUNDINGBOX, 1.},
+ {"TIMELIMIT", XPRS_TIMELIMIT, 1.},
+ {"SOLTIMELIMIT", XPRS_SOLTIMELIMIT, 1.},
+ {"REPAIRINFEASTIMELIMIT", XPRS_REPAIRINFEASTIMELIMIT, 1.},
+ };
+ for (const auto& [paramString, control, paramValue] : params) {
+ UNITTEST_INIT_MIP();
+ std::string xpressParamString =
+ paramString + " " + std::to_string(paramValue);
+ solver.SetSolverSpecificParametersAsString(xpressParamString);
+ EXPECT_EQ(paramValue, getter.getDoubleControl(control));
+ }
+}
+
+TEST(XpressInterface, setIntControl) {
+ std::vector> params = {
+ {"EXTRAROWS", XPRS_EXTRAROWS, 1},
+ {"EXTRACOLS", XPRS_EXTRACOLS, 1},
+ {"LPITERLIMIT", XPRS_LPITERLIMIT, 1},
+ {"LPLOG", XPRS_LPLOG, 1},
+ {"SCALING", XPRS_SCALING, 1},
+ {"PRESOLVE", XPRS_PRESOLVE, 1},
+ {"CRASH", XPRS_CRASH, 1},
+ {"PRICINGALG", XPRS_PRICINGALG, 1},
+ {"INVERTFREQ", XPRS_INVERTFREQ, 1},
+ {"INVERTMIN", XPRS_INVERTMIN, 1},
+ {"MAXNODE", XPRS_MAXNODE, 1},
+ {"MAXTIME", XPRS_MAXTIME, 1},
+ {"MAXMIPSOL", XPRS_MAXMIPSOL, 1},
+ {"SIFTPASSES", XPRS_SIFTPASSES, 1},
+ {"DEFAULTALG", XPRS_DEFAULTALG, 1},
+ {"VARSELECTION", XPRS_VARSELECTION, 1},
+ {"NODESELECTION", XPRS_NODESELECTION, 1},
+ {"BACKTRACK", XPRS_BACKTRACK, 1},
+ {"MIPLOG", XPRS_MIPLOG, 1},
+ {"KEEPNROWS", XPRS_KEEPNROWS, 1},
+ {"MPSECHO", XPRS_MPSECHO, 1},
+ {"MAXPAGELINES", XPRS_MAXPAGELINES, 1},
+ {"OUTPUTLOG", XPRS_OUTPUTLOG, 1},
+ {"BARSOLUTION", XPRS_BARSOLUTION, 1},
+ {"CACHESIZE", XPRS_CACHESIZE, 1},
+ {"CROSSOVER", XPRS_CROSSOVER, 1},
+ {"BARITERLIMIT", XPRS_BARITERLIMIT, 1},
+ {"CHOLESKYALG", XPRS_CHOLESKYALG, 1},
+ {"BAROUTPUT", XPRS_BAROUTPUT, 1},
+ {"EXTRAMIPENTS", XPRS_EXTRAMIPENTS, 1},
+ {"REFACTOR", XPRS_REFACTOR, 1},
+ {"BARTHREADS", XPRS_BARTHREADS, 1},
+ {"KEEPBASIS", XPRS_KEEPBASIS, 1},
+ {"CROSSOVEROPS", XPRS_CROSSOVEROPS, 1},
+ {"VERSION", XPRS_VERSION, 1},
+ {"CROSSOVERTHREADS", XPRS_CROSSOVERTHREADS, 1},
+ {"BIGMMETHOD", XPRS_BIGMMETHOD, 1},
+ {"MPSNAMELENGTH", XPRS_MPSNAMELENGTH, 1},
+ {"ELIMFILLIN", XPRS_ELIMFILLIN, 1},
+ {"PRESOLVEOPS", XPRS_PRESOLVEOPS, 1},
+ {"MIPPRESOLVE", XPRS_MIPPRESOLVE, 1},
+ {"MIPTHREADS", XPRS_MIPTHREADS, 1},
+ {"BARORDER", XPRS_BARORDER, 1},
+ {"BREADTHFIRST", XPRS_BREADTHFIRST, 1},
+ {"AUTOPERTURB", XPRS_AUTOPERTURB, 1},
+ {"DENSECOLLIMIT", XPRS_DENSECOLLIMIT, 1},
+ {"CALLBACKFROMMASTERTHREAD", XPRS_CALLBACKFROMMASTERTHREAD, 1},
+ {"MAXMCOEFFBUFFERELEMS", XPRS_MAXMCOEFFBUFFERELEMS, 1},
+ {"REFINEOPS", XPRS_REFINEOPS, 1},
+ {"LPREFINEITERLIMIT", XPRS_LPREFINEITERLIMIT, 1},
+ {"MIPREFINEITERLIMIT", XPRS_MIPREFINEITERLIMIT, 1},
+ {"DUALIZEOPS", XPRS_DUALIZEOPS, 1},
+ {"CROSSOVERITERLIMIT", XPRS_CROSSOVERITERLIMIT, 1},
+ {"PREBASISRED", XPRS_PREBASISRED, 1},
+ {"PRESORT", XPRS_PRESORT, 1},
+ {"PREPERMUTE", XPRS_PREPERMUTE, 1},
+ {"PREPERMUTESEED", XPRS_PREPERMUTESEED, 1},
+ {"MAXMEMORYSOFT", XPRS_MAXMEMORYSOFT, 1},
+ {"CUTFREQ", XPRS_CUTFREQ, 1},
+ {"SYMSELECT", XPRS_SYMSELECT, 1},
+ {"SYMMETRY", XPRS_SYMMETRY, 1},
+ {"MAXMEMORYHARD", XPRS_MAXMEMORYHARD, 1},
+ {"MIQCPALG", XPRS_MIQCPALG, 1},
+ {"QCCUTS", XPRS_QCCUTS, 1},
+ {"QCROOTALG", XPRS_QCROOTALG, 1},
+ {"PRECONVERTSEPARABLE", XPRS_PRECONVERTSEPARABLE, 1},
+ {"ALGAFTERNETWORK", XPRS_ALGAFTERNETWORK, 1},
+ {"TRACE", XPRS_TRACE, 1},
+ {"MAXIIS", XPRS_MAXIIS, 1},
+ {"CPUTIME", XPRS_CPUTIME, 1},
+ {"COVERCUTS", XPRS_COVERCUTS, 1},
+ {"GOMCUTS", XPRS_GOMCUTS, 1},
+ {"LPFOLDING", XPRS_LPFOLDING, 1},
+ {"MPSFORMAT", XPRS_MPSFORMAT, 1},
+ {"CUTSTRATEGY", XPRS_CUTSTRATEGY, 1},
+ {"CUTDEPTH", XPRS_CUTDEPTH, 1},
+ {"TREECOVERCUTS", XPRS_TREECOVERCUTS, 1},
+ {"TREEGOMCUTS", XPRS_TREEGOMCUTS, 1},
+ {"CUTSELECT", XPRS_CUTSELECT, 1},
+ {"TREECUTSELECT", XPRS_TREECUTSELECT, 1},
+ {"DUALIZE", XPRS_DUALIZE, 1},
+ {"DUALGRADIENT", XPRS_DUALGRADIENT, 1},
+ {"SBITERLIMIT", XPRS_SBITERLIMIT, 1},
+ {"SBBEST", XPRS_SBBEST, 1},
+ {"BARINDEFLIMIT", XPRS_BARINDEFLIMIT, 1},
+ {"HEURFREQ", XPRS_HEURFREQ, 1},
+ {"HEURDEPTH", XPRS_HEURDEPTH, 1},
+ {"HEURMAXSOL", XPRS_HEURMAXSOL, 1},
+ {"HEURNODES", XPRS_HEURNODES, 1},
+ {"LNPBEST", XPRS_LNPBEST, 1},
+ {"LNPITERLIMIT", XPRS_LNPITERLIMIT, 1},
+ {"BRANCHCHOICE", XPRS_BRANCHCHOICE, 1},
+ {"BARREGULARIZE", XPRS_BARREGULARIZE, 1},
+ {"SBSELECT", XPRS_SBSELECT, 1},
+ {"LOCALCHOICE", XPRS_LOCALCHOICE, 1},
+ {"LOCALBACKTRACK", XPRS_LOCALBACKTRACK, 1},
+ {"DUALSTRATEGY", XPRS_DUALSTRATEGY, 1},
+ {"L1CACHE", XPRS_L1CACHE, 1},
+ {"HEURDIVESTRATEGY", XPRS_HEURDIVESTRATEGY, 1},
+ {"HEURSELECT", XPRS_HEURSELECT, 1},
+ {"BARSTART", XPRS_BARSTART, 1},
+ {"PRESOLVEPASSES", XPRS_PRESOLVEPASSES, 1},
+ {"BARNUMSTABILITY", XPRS_BARNUMSTABILITY, 1},
+ {"BARORDERTHREADS", XPRS_BARORDERTHREADS, 1},
+ {"EXTRASETS", XPRS_EXTRASETS, 1},
+ {"FEASIBILITYPUMP", XPRS_FEASIBILITYPUMP, 1},
+ {"PRECOEFELIM", XPRS_PRECOEFELIM, 1},
+ {"PREDOMCOL", XPRS_PREDOMCOL, 1},
+ {"HEURSEARCHFREQ", XPRS_HEURSEARCHFREQ, 1},
+ {"HEURDIVESPEEDUP", XPRS_HEURDIVESPEEDUP, 1},
+ {"SBESTIMATE", XPRS_SBESTIMATE, 1},
+ {"BARCORES", XPRS_BARCORES, 1},
+ {"MAXCHECKSONMAXTIME", XPRS_MAXCHECKSONMAXTIME, 1},
+ {"MAXCHECKSONMAXCUTTIME", XPRS_MAXCHECKSONMAXCUTTIME, 1},
+ {"HISTORYCOSTS", XPRS_HISTORYCOSTS, 1},
+ {"ALGAFTERCROSSOVER", XPRS_ALGAFTERCROSSOVER, 1},
+ {"MUTEXCALLBACKS", XPRS_MUTEXCALLBACKS, 1},
+ {"BARCRASH", XPRS_BARCRASH, 1},
+ {"HEURDIVESOFTROUNDING", XPRS_HEURDIVESOFTROUNDING, 1},
+ {"HEURSEARCHROOTSELECT", XPRS_HEURSEARCHROOTSELECT, 1},
+ {"HEURSEARCHTREESELECT", XPRS_HEURSEARCHTREESELECT, 1},
+ {"MPS18COMPATIBLE", XPRS_MPS18COMPATIBLE, 1},
+ {"ROOTPRESOLVE", XPRS_ROOTPRESOLVE, 1},
+ {"CROSSOVERDRP", XPRS_CROSSOVERDRP, 1},
+ {"FORCEOUTPUT", XPRS_FORCEOUTPUT, 1},
+ {"PRIMALOPS", XPRS_PRIMALOPS, 1},
+ {"DETERMINISTIC", XPRS_DETERMINISTIC, 1},
+ {"PREPROBING", XPRS_PREPROBING, 1},
+ {"TREEMEMORYLIMIT", XPRS_TREEMEMORYLIMIT, 1},
+ {"TREECOMPRESSION", XPRS_TREECOMPRESSION, 1},
+ {"TREEDIAGNOSTICS", XPRS_TREEDIAGNOSTICS, 1},
+ {"MAXTREEFILESIZE", XPRS_MAXTREEFILESIZE, 1},
+ {"PRECLIQUESTRATEGY", XPRS_PRECLIQUESTRATEGY, 1},
+ {"REPAIRINFEASMAXTIME", XPRS_REPAIRINFEASMAXTIME, 1},
+ {"IFCHECKCONVEXITY", XPRS_IFCHECKCONVEXITY, 1},
+ {"PRIMALUNSHIFT", XPRS_PRIMALUNSHIFT, 1},
+ {"REPAIRINDEFINITEQ", XPRS_REPAIRINDEFINITEQ, 1},
+ {"MIPRAMPUP", XPRS_MIPRAMPUP, 1},
+ {"MAXLOCALBACKTRACK", XPRS_MAXLOCALBACKTRACK, 1},
+ {"USERSOLHEURISTIC", XPRS_USERSOLHEURISTIC, 1},
+ {"FORCEPARALLELDUAL", XPRS_FORCEPARALLELDUAL, 1},
+ {"BACKTRACKTIE", XPRS_BACKTRACKTIE, 1},
+ {"BRANCHDISJ", XPRS_BRANCHDISJ, 1},
+ {"MIPFRACREDUCE", XPRS_MIPFRACREDUCE, 1},
+ {"CONCURRENTTHREADS", XPRS_CONCURRENTTHREADS, 1},
+ {"MAXSCALEFACTOR", XPRS_MAXSCALEFACTOR, 1},
+ {"HEURTHREADS", XPRS_HEURTHREADS, 1},
+ {"THREADS", XPRS_THREADS, 1},
+ {"HEURBEFORELP", XPRS_HEURBEFORELP, 1},
+ {"PREDOMROW", XPRS_PREDOMROW, 1},
+ {"BRANCHSTRUCTURAL", XPRS_BRANCHSTRUCTURAL, 1},
+ {"QUADRATICUNSHIFT", XPRS_QUADRATICUNSHIFT, 1},
+ {"BARPRESOLVEOPS", XPRS_BARPRESOLVEOPS, 1},
+ {"QSIMPLEXOPS", XPRS_QSIMPLEXOPS, 1},
+ {"MIPRESTART", XPRS_MIPRESTART, 1},
+ {"CONFLICTCUTS", XPRS_CONFLICTCUTS, 1},
+ {"PREPROTECTDUAL", XPRS_PREPROTECTDUAL, 1},
+ {"CORESPERCPU", XPRS_CORESPERCPU, 1},
+ {"RESOURCESTRATEGY", XPRS_RESOURCESTRATEGY, 1},
+ {"CLAMPING", XPRS_CLAMPING, 1},
+ {"SLEEPONTHREADWAIT", XPRS_SLEEPONTHREADWAIT, 1},
+ {"PREDUPROW", XPRS_PREDUPROW, 1},
+ {"CPUPLATFORM", XPRS_CPUPLATFORM, 1},
+ {"BARALG", XPRS_BARALG, 1},
+ {"SIFTING", XPRS_SIFTING, 1},
+ {"LPLOGSTYLE", XPRS_LPLOGSTYLE, 1},
+ {"RANDOMSEED", XPRS_RANDOMSEED, 1},
+ {"TREEQCCUTS", XPRS_TREEQCCUTS, 1},
+ {"PRELINDEP", XPRS_PRELINDEP, 1},
+ {"DUALTHREADS", XPRS_DUALTHREADS, 1},
+ {"PREOBJCUTDETECT", XPRS_PREOBJCUTDETECT, 1},
+ {"PREBNDREDQUAD", XPRS_PREBNDREDQUAD, 1},
+ {"PREBNDREDCONE", XPRS_PREBNDREDCONE, 1},
+ {"PRECOMPONENTS", XPRS_PRECOMPONENTS, 1},
+ {"MAXMIPTASKS", XPRS_MAXMIPTASKS, 1},
+ {"MIPTERMINATIONMETHOD", XPRS_MIPTERMINATIONMETHOD, 1},
+ {"PRECONEDECOMP", XPRS_PRECONEDECOMP, 1},
+ {"HEURFORCESPECIALOBJ", XPRS_HEURFORCESPECIALOBJ, 1},
+ {"HEURSEARCHROOTCUTFREQ", XPRS_HEURSEARCHROOTCUTFREQ, 1},
+ {"PREELIMQUAD", XPRS_PREELIMQUAD, 1},
+ {"PREIMPLICATIONS", XPRS_PREIMPLICATIONS, 1},
+ {"TUNERMODE", XPRS_TUNERMODE, 1},
+ {"TUNERMETHOD", XPRS_TUNERMETHOD, 1},
+ {"TUNERTARGET", XPRS_TUNERTARGET, 1},
+ {"TUNERTHREADS", XPRS_TUNERTHREADS, 1},
+ {"TUNERHISTORY", XPRS_TUNERHISTORY, 1},
+ {"TUNERPERMUTE", XPRS_TUNERPERMUTE, 1},
+ {"TUNERVERBOSE", XPRS_TUNERVERBOSE, 1},
+ {"TUNEROUTPUT", XPRS_TUNEROUTPUT, 1},
+ {"PREANALYTICCENTER", XPRS_PREANALYTICCENTER, 1},
+ {"NETCUTS", XPRS_NETCUTS, 1},
+ {"LPFLAGS", XPRS_LPFLAGS, 1},
+ {"MIPKAPPAFREQ", XPRS_MIPKAPPAFREQ, 1},
+ {"OBJSCALEFACTOR", XPRS_OBJSCALEFACTOR, 1},
+ {"TREEFILELOGINTERVAL", XPRS_TREEFILELOGINTERVAL, 1},
+ {"IGNORECONTAINERCPULIMIT", XPRS_IGNORECONTAINERCPULIMIT, 1},
+ {"IGNORECONTAINERMEMORYLIMIT", XPRS_IGNORECONTAINERMEMORYLIMIT, 1},
+ {"MIPDUALREDUCTIONS", XPRS_MIPDUALREDUCTIONS, 1},
+ {"GENCONSDUALREDUCTIONS", XPRS_GENCONSDUALREDUCTIONS, 1},
+ {"PWLDUALREDUCTIONS", XPRS_PWLDUALREDUCTIONS, 1},
+ {"BARFAILITERLIMIT", XPRS_BARFAILITERLIMIT, 1},
+ {"AUTOSCALING", XPRS_AUTOSCALING, 1},
+ {"GENCONSABSTRANSFORMATION", XPRS_GENCONSABSTRANSFORMATION, 1},
+ {"COMPUTEJOBPRIORITY", XPRS_COMPUTEJOBPRIORITY, 1},
+ {"PREFOLDING", XPRS_PREFOLDING, 1},
+ {"NETSTALLLIMIT", XPRS_NETSTALLLIMIT, 1},
+ {"SERIALIZEPREINTSOL", XPRS_SERIALIZEPREINTSOL, 1},
+ {"NUMERICALEMPHASIS", XPRS_NUMERICALEMPHASIS, 1},
+ {"PWLNONCONVEXTRANSFORMATION", XPRS_PWLNONCONVEXTRANSFORMATION, 1},
+ {"MIPCOMPONENTS", XPRS_MIPCOMPONENTS, 1},
+ {"MIPCONCURRENTNODES", XPRS_MIPCONCURRENTNODES, 1},
+ {"MIPCONCURRENTSOLVES", XPRS_MIPCONCURRENTSOLVES, 1},
+ {"OUTPUTCONTROLS", XPRS_OUTPUTCONTROLS, 1},
+ {"SIFTSWITCH", XPRS_SIFTSWITCH, 1},
+ {"HEUREMPHASIS", XPRS_HEUREMPHASIS, 1},
+ {"COMPUTEMATX", XPRS_COMPUTEMATX, 1},
+ {"COMPUTEMATX_IIS", XPRS_COMPUTEMATX_IIS, 1},
+ {"COMPUTEMATX_IISMAXTIME", XPRS_COMPUTEMATX_IISMAXTIME, 1},
+ {"BARREFITER", XPRS_BARREFITER, 1},
+ {"COMPUTELOG", XPRS_COMPUTELOG, 1},
+ {"SIFTPRESOLVEOPS", XPRS_SIFTPRESOLVEOPS, 1},
+ {"CHECKINPUTDATA", XPRS_CHECKINPUTDATA, 1},
+ {"ESCAPENAMES", XPRS_ESCAPENAMES, 1},
+ {"IOTIMEOUT", XPRS_IOTIMEOUT, 1},
+ {"AUTOCUTTING", XPRS_AUTOCUTTING, 1},
+ {"CALLBACKCHECKTIMEDELAY", XPRS_CALLBACKCHECKTIMEDELAY, 1},
+ {"MULTIOBJOPS", XPRS_MULTIOBJOPS, 1},
+ {"MULTIOBJLOG", XPRS_MULTIOBJLOG, 1},
+ {"GLOBALSPATIALBRANCHIFPREFERORIG", XPRS_GLOBALSPATIALBRANCHIFPREFERORIG,
+ 1},
+ {"PRECONFIGURATION", XPRS_PRECONFIGURATION, 1},
+ {"FEASIBILITYJUMP", XPRS_FEASIBILITYJUMP, 1},
+ };
+ for (const auto& [paramString, control, paramValue] : params) {
+ UNITTEST_INIT_MIP();
+ std::string xpressParamString =
+ paramString + " " + std::to_string(paramValue);
+ solver.SetSolverSpecificParametersAsString(xpressParamString);
+ EXPECT_EQ(paramValue, getter.getIntegerControl(control));
+ }
+}
+
+TEST(XpressInterface, setInt64Control) {
+ std::vector> params = {
+ {"EXTRAELEMS", XPRS_EXTRAELEMS, 1},
+ {"EXTRASETELEMS", XPRS_EXTRASETELEMS, 1},
+ };
+ for (const auto& [paramString, control, paramValue] : params) {
+ UNITTEST_INIT_MIP();
+ std::string xpressParamString =
+ paramString + " " + std::to_string(paramValue);
+ solver.SetSolverSpecificParametersAsString(xpressParamString);
+ EXPECT_EQ(paramValue, getter.getInteger64Control(control));
+ }
+}
+
+TEST(XpressInterface, SolveMIP) {
+ UNITTEST_INIT_MIP();
+
+ // max x + 2y
+ // st. -x + y <= 1
+ // 2x + 3y <= 12
+ // 3x + 2y <= 12
+ // x , y >= 0
+ // x , y \in Z
+
+ double inf = solver.infinity();
+ MPVariable* x = solver.MakeIntVar(0, inf, "x");
+ MPVariable* y = solver.MakeIntVar(0, inf, "y");
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetCoefficient(x, 1);
+ obj->SetCoefficient(y, 2);
+ obj->SetMaximization();
+ MPConstraint* c1 = solver.MakeRowConstraint(-inf, 1);
+ c1->SetCoefficient(x, -1);
+ c1->SetCoefficient(y, 1);
+ MPConstraint* c2 = solver.MakeRowConstraint(-inf, 12);
+ c2->SetCoefficient(x, 3);
+ c2->SetCoefficient(y, 2);
+ MPConstraint* c3 = solver.MakeRowConstraint(-inf, 12);
+ c3->SetCoefficient(x, 2);
+ c3->SetCoefficient(y, 3);
+ solver.Solve();
+
+ EXPECT_EQ(obj->Value(), 6);
+ EXPECT_EQ(obj->BestBound(), 6);
+ EXPECT_EQ(x->solution_value(), 2);
+ EXPECT_EQ(y->solution_value(), 2);
+}
+
+TEST(XpressInterface, SolveLP) {
+ UNITTEST_INIT_LP();
+
+ // max x + 2y
+ // st. -x + y <= 1
+ // 2x + 3y <= 12
+ // 3x + 2y <= 12
+ // x , y \in R+
+
+ double inf = solver.infinity();
+ MPVariable* x = solver.MakeNumVar(0, inf, "x");
+ MPVariable* y = solver.MakeNumVar(0, inf, "y");
+ MPObjective* obj = solver.MutableObjective();
+ obj->SetCoefficient(x, 1);
+ obj->SetCoefficient(y, 2);
+ obj->SetMaximization();
+ MPConstraint* c1 = solver.MakeRowConstraint(-inf, 1);
+ c1->SetCoefficient(x, -1);
+ c1->SetCoefficient(y, 1);
+ MPConstraint* c2 = solver.MakeRowConstraint(-inf, 12);
+ c2->SetCoefficient(x, 3);
+ c2->SetCoefficient(y, 2);
+ MPConstraint* c3 = solver.MakeRowConstraint(-inf, 12);
+ c3->SetCoefficient(x, 2);
+ c3->SetCoefficient(y, 3);
+ solver.Solve();
+
+ EXPECT_NEAR(obj->Value(), 7.4, 1e-8);
+ EXPECT_NEAR(x->solution_value(), 1.8, 1e-8);
+ EXPECT_NEAR(y->solution_value(), 2.8, 1e-8);
+ EXPECT_NEAR(x->reduced_cost(), 0, 1e-8);
+ EXPECT_NEAR(y->reduced_cost(), 0, 1e-8);
+ EXPECT_NEAR(c1->dual_value(), 0.2, 1e-8);
+ EXPECT_NEAR(c2->dual_value(), 0, 1e-8);
+ EXPECT_NEAR(c3->dual_value(), 0.6, 1e-8);
+}
+
+// WARNING fragile test because it uses
+// the random generator is used by
+// buildLargeMip(solver, numVars, maxTime);
+// called by
+// buildLargeMipWithCallback(solver, 60, 2);
+// This tests hints a solution to the solver that is only
+// usable for the test generated under linux
+#if defined(_MSC_VER)
+// Ignore this test because the random generator is different
+// for windows and linux.
+#elif defined(__GNUC__)
+TEST(XpressInterface, SetHint) {
+ UNITTEST_INIT_MIP();
+
+ // Once a solution is added to XPRESS, it is actually impossible to get it
+ // back using the API
+ // In this test we send the (near) optimal solution as a hint (with
+ // obj=56774). Usually XPRESS finds it in ~3000 seconds but in this case it
+ // should be able to retain it in juste a few seconds using the hint. Note
+ // that the logs should mention "User solution (USER_HINT) stored."
+ buildLargeMipWithCallback(solver, 60, 2);
+
+ std::vector hintValues{
+ -2, -3, -19, 8, -1, -1, 7, 9, -20, -17, 7, -7,
+ 9, -27, 13, 14, -6, -3, -25, -9, 15, 13, -10, 16,
+ -34, 51, 39, 4, -54, 19, -76, 1, -17, -18, -46, -10,
+ 0, -36, 9, -29, -6, 4, -16, -45, -12, -45, -25, -70,
+ -43, -63, 54, -148, 79, -2, 64, 92, 61, -121, -174, -85};
+ std::vector> hint;
+ for (int i = 0; i < solver.NumVariables(); ++i) {
+ hint.push_back(std::make_pair(
+ solver.LookupVariableOrNull("x_" + std::to_string(i)), hintValues[i]));
+ }
+ solver.SetHint(hint);
+ solver.Solve();
+
+ // Test that we have at least the near optimal objective function value
+ EXPECT_GE(solver.Objective().Value(), 56774.0);
+}
+#endif
+
+
+TEST(XpressInterface, SetCallBack) {
+ UNITTEST_INIT_MIP();
+
+ auto myMpCallback = buildLargeMipWithCallback(solver, 30, 30);
+ solver.Solve();
+
+ int nSolutions = myMpCallback->getNSolutions();
+
+ // This is a tough MIP, in 30 seconds XPRESS should have found at least 5
+ // solutions (tested with XPRESS v9.0, may change in later versions)
+ EXPECT_GT(nSolutions, 5);
+ // Test variable values for the last solution found
+ for (int i = 0; i < solver.NumVariables(); ++i) {
+ EXPECT_NEAR(
+ myMpCallback->getLastVariableValue(i),
+ solver.LookupVariableOrNull("x_" + std::to_string(i))->solution_value(),
+ 1e-10);
+ }
+}
+
+TEST(XpressInterface, SetAndUnsetCallBack) {
+ // Test that when we unset a callback it is not called
+ UNITTEST_INIT_MIP();
+ auto myMpCallback = buildLargeMipWithCallback(solver, 100, 5);
+ solver.SetCallback(nullptr);
+ solver.Solve();
+ EXPECT_EQ(myMpCallback->getNSolutions(), 0);
+}
+
+TEST(XpressInterface, SetAndResetCallBack) {
+ // Test that when we set a new callback then it is called, and old one is not
+ // called
+ UNITTEST_INIT_MIP();
+ auto oldMpCallback = buildLargeMipWithCallback(solver, 100, 5);
+ auto newMpCallback = new MyMPCallback(&solver, false);
+ solver.SetCallback((MPCallback*)newMpCallback);
+ solver.Solve();
+ EXPECT_EQ(oldMpCallback->getNSolutions(), 0);
+ EXPECT_GT(newMpCallback->getNSolutions(), 1);
+}
+
+TEST(XpressInterface, CallbackThrowsException) {
+ // Test that when the callback throws an exception, it is caught and logged
+ UNITTEST_INIT_MIP();
+ auto oldMpCallback = buildLargeMipWithCallback(solver, 30, 30);
+ auto newMpCallback = new MyMPCallback(&solver, true);
+ solver.SetCallback((MPCallback*)newMpCallback);
+ testing::internal::CaptureStderr();
+ EXPECT_NO_THROW(solver.Solve());
+ std::string errors = testing::internal::GetCapturedStderr();
+ // Test that StdErr contains the following error message
+ std::string expected_error = "Caught exception during user-defined call-back: This is a mocked exception in MyMPCallback";
+ ASSERT_NE(errors.find(expected_error), std::string::npos);
+}
+
+} // namespace operations_research
+
+int main(int argc, char** argv) {
+ absl::SetFlag(&FLAGS_logtostderr, 1);
+ testing::InitGoogleTest(&argc, argv);
+ auto solver = operations_research::MPSolver::CreateSolver("XPRESS_LP");
+ if (solver == nullptr) {
+ LOG(ERROR) << "Xpress solver is not available";
+ return EXIT_SUCCESS;
+ }
+ else{
+ return RUN_ALL_TESTS();
+ }
+}
diff --git a/ortools/xpress/BUILD.bazel b/ortools/xpress/BUILD.bazel
new file mode 100644
index 0000000000..bbdfb3c815
--- /dev/null
+++ b/ortools/xpress/BUILD.bazel
@@ -0,0 +1,22 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "environment",
+ srcs = [
+ "environment.cc",
+ ],
+ hdrs = [
+ "environment.h",
+ ],
+ deps = [
+ "//ortools/base",
+ "//ortools/base:dynamic_library",
+ "//ortools/base:file",
+ "//ortools/base:status_macros",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/status:statusor",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/synchronization",
+ "@com_google_absl//absl/types:optional",
+ ],
+)
diff --git a/ortools/xpress/CMakeLists.txt b/ortools/xpress/CMakeLists.txt
new file mode 100644
index 0000000000..bce9253cec
--- /dev/null
+++ b/ortools/xpress/CMakeLists.txt
@@ -0,0 +1,21 @@
+file(GLOB _SRCS "*.h" "*.cc")
+set(NAME ${PROJECT_NAME}_xpress)
+
+add_library(${NAME} OBJECT ${_SRCS})
+set_target_properties(${NAME} PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ CXX_EXTENSIONS OFF
+ POSITION_INDEPENDENT_CODE ON
+ )
+target_include_directories(${NAME} PRIVATE
+ ${PROJECT_SOURCE_DIR}
+ ${PROJECT_BINARY_DIR})
+target_link_libraries(${NAME} PRIVATE
+ absl::hash
+ absl::meta
+ absl::memory
+ absl::strings
+ absl::str_format
+ protobuf::libprotobuf
+ ${PROJECT_NAMESPACE}::${PROJECT_NAME}_proto)
diff --git a/ortools/xpress/environment.cc b/ortools/xpress/environment.cc
new file mode 100644
index 0000000000..681840b72c
--- /dev/null
+++ b/ortools/xpress/environment.cc
@@ -0,0 +1,362 @@
+// Copyright 2019-2023 RTE
+// 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
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Initial version of this code was provided by RTE
+
+#include "ortools/xpress/environment.h"
+
+#include
+#include
+#include
+
+#include "absl/status/status.h"
+#include "absl/strings/match.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
+#include "absl/synchronization/mutex.h"
+#include "ortools/base/logging.h"
+
+namespace operations_research {
+
+#define STRINGIFY2(X) #X
+#define STRINGIFY(X) STRINGIFY2(X)
+
+// This was generated with the parse_header_xpress.py script.
+// See the comment at the top of the script.
+
+// This is the 'define' section.
+std::function XPRScreateprob = nullptr;
+std::function XPRSdestroyprob = nullptr;
+std::function XPRSinit = nullptr;
+std::function XPRSfree = nullptr;
+std::function XPRSgetlicerrmsg = nullptr;
+std::function XPRSlicense = nullptr;
+std::function XPRSgetbanner = nullptr;
+std::function XPRSgetversion = nullptr;
+std::function XPRSsetdefaultcontrol = nullptr;
+std::function XPRSinterrupt = nullptr;
+std::function XPRSsetintcontrol = nullptr;
+std::function XPRSsetintcontrol64 = nullptr;
+std::function XPRSsetdblcontrol = nullptr;
+std::function XPRSsetstrcontrol = nullptr;
+std::function XPRSgetintcontrol = nullptr;
+std::function XPRSgetintcontrol64 = nullptr;
+std::function XPRSgetdblcontrol = nullptr;
+std::function XPRSgetstringcontrol = nullptr;
+std::function XPRSgetintattrib = nullptr;
+std::function XPRSgetdblattrib = nullptr;
+std::function XPRSgetcontrolinfo = nullptr;
+std::function XPRSloadlp = nullptr;
+std::function XPRSloadlp64 = nullptr;
+std::function XPRSgetobj = nullptr;
+std::function XPRSgetrhs = nullptr;
+std::function XPRSgetrhsrange = nullptr;
+std::function XPRSgetlb = nullptr;
+std::function XPRSgetub = nullptr;
+std::function XPRSgetcoef = nullptr;
+std::function XPRSaddrows = nullptr;
+std::function XPRSdelrows = nullptr;
+std::function XPRSaddcols = nullptr;
+std::function XPRSaddnames = nullptr;
+std::function XPRSgetnames = nullptr;
+std::function XPRSdelcols = nullptr;
+std::function XPRSchgcoltype = nullptr;
+std::function XPRSloadbasis = nullptr;
+std::function XPRSpostsolve = nullptr;
+std::function XPRSchgobjsense = nullptr;
+std::function XPRSgetlasterror = nullptr;
+std::function XPRSgetbasis = nullptr;
+std::function XPRSwriteprob = nullptr;
+std::function XPRSgetrowtype = nullptr;
+std::function XPRSgetcoltype = nullptr;
+std::function XPRSchgbounds = nullptr;
+std::function XPRSaddmipsol = nullptr;
+std::function XPRSgetlpsol = nullptr;
+std::function XPRSgetmipsol = nullptr;
+std::function XPRSchgobj = nullptr;
+std::function XPRSchgcoef = nullptr;
+std::function XPRSchgmcoef = nullptr;
+std::function XPRSchgrhs = nullptr;
+std::function XPRSchgrhsrange = nullptr;
+std::function XPRSchgrowtype = nullptr;
+std::function XPRSaddcbintsol = nullptr;
+std::function XPRSremovecbintsol = nullptr;
+std::function XPRSaddcbmessage = nullptr;
+std::function XPRSlpoptimize = nullptr;
+std::function XPRSmipoptimize = nullptr;
+
+void LoadXpressFunctions(DynamicLibrary* xpress_dynamic_library) {
+ // This was generated with the parse_header_xpress.py script.
+ // See the comment at the top of the script.
+
+ // This is the 'assign' section.
+ xpress_dynamic_library->GetFunction(&XPRScreateprob, "XPRScreateprob");
+ xpress_dynamic_library->GetFunction(&XPRSdestroyprob, "XPRSdestroyprob");
+ xpress_dynamic_library->GetFunction(&XPRSinit, "XPRSinit");
+ xpress_dynamic_library->GetFunction(&XPRSfree, "XPRSfree");
+ xpress_dynamic_library->GetFunction(&XPRSgetlicerrmsg, "XPRSgetlicerrmsg");
+ xpress_dynamic_library->GetFunction(&XPRSlicense, "XPRSlicense");
+ xpress_dynamic_library->GetFunction(&XPRSgetbanner, "XPRSgetbanner");
+ xpress_dynamic_library->GetFunction(&XPRSgetversion, "XPRSgetversion");
+ xpress_dynamic_library->GetFunction(&XPRSsetdefaultcontrol, "XPRSsetdefaultcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSinterrupt, "XPRSinterrupt");
+ xpress_dynamic_library->GetFunction(&XPRSsetintcontrol, "XPRSsetintcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSsetintcontrol64, "XPRSsetintcontrol64");
+ xpress_dynamic_library->GetFunction(&XPRSsetdblcontrol, "XPRSsetdblcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSsetstrcontrol, "XPRSsetstrcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSgetintcontrol, "XPRSgetintcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSgetintcontrol64, "XPRSgetintcontrol64");
+ xpress_dynamic_library->GetFunction(&XPRSgetdblcontrol, "XPRSgetdblcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSgetstringcontrol, "XPRSgetstringcontrol");
+ xpress_dynamic_library->GetFunction(&XPRSgetintattrib, "XPRSgetintattrib");
+ xpress_dynamic_library->GetFunction(&XPRSgetdblattrib, "XPRSgetdblattrib");
+ xpress_dynamic_library->GetFunction(&XPRSloadlp, "XPRSloadlp");
+ xpress_dynamic_library->GetFunction(&XPRSloadlp64, "XPRSloadlp64");
+ xpress_dynamic_library->GetFunction(&XPRSgetobj, "XPRSgetobj");
+ xpress_dynamic_library->GetFunction(&XPRSgetrhs, "XPRSgetrhs");
+ xpress_dynamic_library->GetFunction(&XPRSgetrhsrange, "XPRSgetrhsrange");
+ xpress_dynamic_library->GetFunction(&XPRSgetlb, "XPRSgetlb");
+ xpress_dynamic_library->GetFunction(&XPRSgetub, "XPRSgetub");
+ xpress_dynamic_library->GetFunction(&XPRSgetcoef, "XPRSgetcoef");
+ xpress_dynamic_library->GetFunction(&XPRSaddrows, "XPRSaddrows");
+ xpress_dynamic_library->GetFunction(&XPRSdelrows, "XPRSdelrows");
+ xpress_dynamic_library->GetFunction(&XPRSaddcols, "XPRSaddcols");
+ xpress_dynamic_library->GetFunction(&XPRSaddnames, "XPRSaddnames");
+ xpress_dynamic_library->GetFunction(&XPRSgetnames, "XPRSgetnames");
+ xpress_dynamic_library->GetFunction(&XPRSdelcols, "XPRSdelcols");
+ xpress_dynamic_library->GetFunction(&XPRSchgcoltype, "XPRSchgcoltype");
+ xpress_dynamic_library->GetFunction(&XPRSloadbasis, "XPRSloadbasis");
+ xpress_dynamic_library->GetFunction(&XPRSpostsolve, "XPRSpostsolve");
+ xpress_dynamic_library->GetFunction(&XPRSchgobjsense, "XPRSchgobjsense");
+ xpress_dynamic_library->GetFunction(&XPRSgetlasterror, "XPRSgetlasterror");
+ xpress_dynamic_library->GetFunction(&XPRSgetbasis, "XPRSgetbasis");
+ xpress_dynamic_library->GetFunction(&XPRSwriteprob, "XPRSwriteprob");
+ xpress_dynamic_library->GetFunction(&XPRSgetrowtype, "XPRSgetrowtype");
+ xpress_dynamic_library->GetFunction(&XPRSgetcoltype, "XPRSgetcoltype");
+ xpress_dynamic_library->GetFunction(&XPRSchgbounds, "XPRSchgbounds");
+ xpress_dynamic_library->GetFunction(&XPRSaddmipsol, "XPRSaddmipsol");
+ xpress_dynamic_library->GetFunction(&XPRSgetlpsol, "XPRSgetlpsol");
+ xpress_dynamic_library->GetFunction(&XPRSgetmipsol, "XPRSgetmipsol");
+ xpress_dynamic_library->GetFunction(&XPRSchgobj, "XPRSchgobj");
+ xpress_dynamic_library->GetFunction(&XPRSchgcoef, "XPRSchgcoef");
+ xpress_dynamic_library->GetFunction(&XPRSchgmcoef, "XPRSchgmcoef");
+ xpress_dynamic_library->GetFunction(&XPRSchgrhs, "XPRSchgrhs");
+ xpress_dynamic_library->GetFunction(&XPRSchgrhsrange, "XPRSchgrhsrange");
+ xpress_dynamic_library->GetFunction(&XPRSchgrowtype, "XPRSchgrowtype");
+ xpress_dynamic_library->GetFunction(&XPRSaddcbintsol, "XPRSaddcbintsol");
+ xpress_dynamic_library->GetFunction(&XPRSremovecbintsol, "XPRSremovecbintsol");
+ xpress_dynamic_library->GetFunction(&XPRSaddcbmessage, "XPRSaddcbmessage");
+ xpress_dynamic_library->GetFunction(&XPRSlpoptimize, "XPRSlpoptimize");
+ xpress_dynamic_library->GetFunction(&XPRSmipoptimize, "XPRSmipoptimize");
+}
+
+void printXpressBanner(bool error) {
+ char banner[XPRS_MAXBANNERLENGTH];
+ XPRSgetbanner(banner);
+
+ if (error) {
+ LOG(ERROR) << "XpressInterface : Xpress banner :\n" << banner << "\n";
+ } else {
+ LOG(WARNING) << "XpressInterface : Xpress banner :\n" << banner << "\n";
+ }
+}
+
+std::vector XpressDynamicLibraryPotentialPaths() {
+ std::vector potential_paths;
+
+ // Look for libraries pointed by XPRESSDIR first.
+ const char* xpressdir_from_env = getenv("XPRESSDIR");
+ if (xpressdir_from_env != nullptr) {
+ LOG(INFO) << "Environment variable XPRESSDIR = " << xpressdir_from_env;
+#if defined(_MSC_VER) // Windows
+ potential_paths.push_back(
+ absl::StrCat(xpressdir_from_env, "\\bin\\xprs.dll"));
+#elif defined(__APPLE__) // OS X
+ potential_paths.push_back(
+ absl::StrCat(xpressdir_from_env, "/lib/libxprs.dylib"));
+#elif defined(__GNUC__) // Linux
+ potential_paths.push_back(
+ absl::StrCat(xpressdir_from_env, "/lib/libxprs.so"));
+#else
+ LOG(ERROR) << "OS Not recognized by xpress/environment.cc."
+ << " You won't be able to use Xpress.";
+#endif
+ } else {
+ LOG(WARNING) << "Environment variable XPRESSDIR undefined.";
+ }
+
+ // Search for canonical places.
+#if defined(_MSC_VER) // Windows
+ potential_paths.push_back(absl::StrCat("C:\\xpressmp\\bin\\xprs.dll"));
+ potential_paths.push_back(
+ absl::StrCat("C:\\Program Files\\xpressmp\\bin\\xprs.dll"));
+#elif defined(__APPLE__) // OS X
+ potential_paths.push_back(
+ absl::StrCat("/Library/xpressmp/lib/libxprs.dylib"));
+#elif defined(__GNUC__) // Linux
+ potential_paths.push_back(absl::StrCat("/opt/xpressmp/lib/libxprs.so"));
+#else
+ LOG(ERROR) << "OS Not recognized by xpress/environment.cc."
+ << " You won't be able to use Xpress.";
+#endif
+ return potential_paths;
+}
+
+absl::Status LoadXpressDynamicLibrary(std::string& xpresspath) {
+ static std::string xpress_lib_path;
+ static std::once_flag xpress_loading_done;
+ static absl::Status xpress_load_status;
+ static DynamicLibrary xpress_library;
+ static absl::Mutex mutex;
+
+ absl::MutexLock lock(&mutex);
+
+ std::call_once(xpress_loading_done, []() {
+ const std::vector canonical_paths =
+ XpressDynamicLibraryPotentialPaths();
+ for (const std::string& path : canonical_paths) {
+ if (xpress_library.TryToLoad(path)) {
+ LOG(INFO) << "Found the Xpress library in " << path << ".";
+ xpress_lib_path.clear();
+ std::filesystem::path p(path);
+ p.remove_filename();
+ xpress_lib_path.append(p.string());
+ break;
+ }
+ }
+
+ if (xpress_library.LibraryIsLoaded()) {
+ LOG(INFO) << "Loading all Xpress functions";
+ LoadXpressFunctions(&xpress_library);
+ xpress_load_status = absl::OkStatus();
+ } else {
+ xpress_load_status = absl::NotFoundError(
+ absl::StrCat("Could not find the Xpress shared library. Looked in: [",
+ absl::StrJoin(canonical_paths, "', '"),
+ "]. Please check environment variable XPRESSDIR"));
+ }
+ });
+ xpresspath.clear();
+ xpresspath.append(xpress_lib_path);
+ return xpress_load_status;
+}
+
+void log_message_about_XPRSinit_argument();
+void log_full_license_error(int code, const std::string& xpress_lib_dir);
+/** init XPRESS environment */
+bool initXpressEnv(bool verbose, int xpress_oem_license_key) {
+ std::string xpress_lib_dir;
+ absl::Status status = LoadXpressDynamicLibrary(xpress_lib_dir);
+ if (!status.ok()) {
+ LOG(WARNING) << status << "\n";
+ return false;
+ }
+
+ int code;
+ // if not an OEM key
+ if (xpress_oem_license_key == 0) {
+ if (verbose) {
+ log_message_about_XPRSinit_argument();
+ }
+
+ code = XPRSinit(nullptr);
+
+ if (!code) {
+ // XPRSbanner informs about Xpress version, options and error messages
+ if (verbose) {
+ printXpressBanner(false);
+ char version[16];
+ XPRSgetversion(version);
+ LOG(WARNING) << "Optimizer version: " << version
+ << " (OR-Tools was compiled with version " << XPVERSION
+ << ").\n";
+ }
+ return true;
+ } else {
+ log_full_license_error(code, xpress_lib_dir);
+ return false;
+ }
+ } else {
+ // if OEM key
+ if (verbose) {
+ LOG(WARNING) << "XpressInterface : Initialising xpress-MP with OEM key "
+ << xpress_oem_license_key << "\n";
+ }
+
+ int nvalue = 0;
+ int ierr;
+ char slicmsg[256] = "";
+ char errmsg[256];
+
+ XPRSlicense(&nvalue, slicmsg);
+ if (verbose) {
+ VLOG(0) << "XpressInterface : First message from XPRSLicense : "
+ << slicmsg << "\n";
+ }
+
+ nvalue = xpress_oem_license_key - ((nvalue * nvalue) / 19);
+ ierr = XPRSlicense(&nvalue, slicmsg);
+
+ if (verbose) {
+ VLOG(0) << "XpressInterface : Second message from XPRSLicense : "
+ << slicmsg << "\n";
+ }
+ if (ierr == 16) {
+ if (verbose) {
+ VLOG(0)
+ << "XpressInterface : Optimizer development software detected\n";
+ }
+ } else if (ierr != 0) {
+ // get the license error message
+ XPRSgetlicerrmsg(errmsg, 256);
+
+ LOG(ERROR) << "XpressInterface : " << errmsg << "\n";
+ return false;
+ }
+
+ code = XPRSinit(NULL);
+
+ if (!code) {
+ return true;
+ } else {
+ LOG(ERROR) << "XPRSinit returned code : " << code << "\n";
+ return false;
+ }
+ }
+}
+void log_full_license_error(int code, const std::string& xpress_lib_dir) {
+ LOG(WARNING) << "XpressInterface: Xpress found at " << xpress_lib_dir
+ << "\n";
+ char errmsg[256];
+ XPRSgetlicerrmsg(errmsg, 256);
+
+ LOG(ERROR) << "XpressInterface : License error : " << errmsg
+ << " (XPRSinit returned code " << code << "). \n";
+ LOG(ERROR)
+ << "|_Your Xpress installation should have set the env var XPAUTH_PATH"
+ " to the full path of your licence file\n";
+}
+void log_message_about_XPRSinit_argument() {
+ LOG(WARNING)
+ << "XpressInterface : Initialising xpress-MP with default parameters";
+}
+
+bool XpressIsCorrectlyInstalled() {
+ bool correctlyInstalled = initXpressEnv(false);
+ if (correctlyInstalled) {
+ XPRSfree();
+ }
+ return correctlyInstalled;
+}
+
+} // namespace operations_research
diff --git a/ortools/xpress/environment.h b/ortools/xpress/environment.h
new file mode 100644
index 0000000000..fcbec406c7
--- /dev/null
+++ b/ortools/xpress/environment.h
@@ -0,0 +1,499 @@
+// Copyright 2019-2023 RTE
+// 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
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Initial version of this code was provided by RTE
+
+#ifndef OR_TOOLS_XPRESS_ENVIRONMENT_H
+#define OR_TOOLS_XPRESS_ENVIRONMENT_H
+
+#include "absl/status/status.h"
+#include "absl/status/statusor.h"
+#include "ortools/base/commandlineflags.h"
+#include "ortools/base/dynamic_library.h"
+#include "ortools/base/logging.h"
+
+extern "C" {
+typedef struct xo_prob_struct* XPRSprob;
+}
+
+namespace operations_research {
+
+void printXpressBanner(bool error);
+
+bool initXpressEnv(bool verbose = true, int xpress_oem_license_key = 0);
+
+bool XpressIsCorrectlyInstalled();
+// clang-format off
+// Force the loading of the xpress dynamic library. It returns true if the
+// library was successfully loaded. This method can only be called once.
+// Successive calls are no-op.
+//
+// Note that it does not check if a token license can be grabbed.
+absl::Status LoadXpressDynamicLibrary(std::string &xpresspath);
+
+// The list of #define and extern std::function<> below is generated directly
+// from xprs.h via parse_header_xpress.py
+// See the top comment on the parse_header_xpress.py file.
+// This is the header section
+#if defined(_WIN32)
+#define XPRSint64 __int64
+#elif defined(__LP64__) || defined(_LP64) || defined(__ILP64__) || defined(_ILP64)
+#define XPRSint64 long
+#else
+#define XPRSint64 long long
+#endif
+
+#if defined(_MSC_VER)
+#define XPRS_CC __stdcall
+#else
+#define XPRS_CC
+#endif
+/***************************************************************************\
+ * values related to XPRSinterrupt *
+\***************************************************************************/
+#define XPRS_STOP_NONE 0
+#define XPRS_STOP_TIMELIMIT 1
+#define XPRS_STOP_CTRLC 2
+#define XPRS_STOP_NODELIMIT 3
+#define XPRS_STOP_ITERLIMIT 4
+#define XPRS_STOP_MIPGAP 5
+#define XPRS_STOP_SOLLIMIT 6
+#define XPRS_STOP_GENERICERROR 7
+#define XPRS_STOP_MEMORYERROR 8
+#define XPRS_STOP_USER 9
+#define XPRS_STOP_SOLVECOMPLETE 10
+#define XPRS_STOP_LICENSELOST 11
+#define XPRS_STOP_NUMERICALERROR 13
+/***************************************************************************\
+ * values related to Set/GetControl/Attribinfo *
+\***************************************************************************/
+#define XPRS_TYPE_NOTDEFINED 0
+#define XPRS_TYPE_INT 1
+#define XPRS_TYPE_INT64 2
+#define XPRS_TYPE_DOUBLE 3
+#define XPRS_TYPE_STRING 4
+/***************************************************************************\
+ * values related to NAMESPACES *
+\***************************************************************************/
+#define XPRS_NAMES_ROW 1
+#define XPRS_NAMES_COLUMN 2
+
+
+#define XPRS_PLUSINFINITY 1.0e+20
+#define XPRS_MINUSINFINITY -1.0e+20
+#define XPRS_MAXBANNERLENGTH 512
+#define XPVERSION 41
+#define XPRS_MPSRHSNAME 6001
+#define XPRS_MPSOBJNAME 6002
+#define XPRS_MPSRANGENAME 6003
+#define XPRS_MPSBOUNDNAME 6004
+#define XPRS_OUTPUTMASK 6005
+#define XPRS_TUNERMETHODFILE 6017
+#define XPRS_TUNEROUTPUTPATH 6018
+#define XPRS_TUNERSESSIONNAME 6019
+#define XPRS_COMPUTEEXECSERVICE 6022
+#define XPRS_MAXCUTTIME 8149
+#define XPRS_MAXSTALLTIME 8443
+#define XPRS_TUNERMAXTIME 8364
+#define XPRS_MATRIXTOL 7001
+#define XPRS_PIVOTTOL 7002
+#define XPRS_FEASTOL 7003
+#define XPRS_OUTPUTTOL 7004
+#define XPRS_SOSREFTOL 7005
+#define XPRS_OPTIMALITYTOL 7006
+#define XPRS_ETATOL 7007
+#define XPRS_RELPIVOTTOL 7008
+#define XPRS_MIPTOL 7009
+#define XPRS_MIPTOLTARGET 7010
+#define XPRS_BARPERTURB 7011
+#define XPRS_MIPADDCUTOFF 7012
+#define XPRS_MIPABSCUTOFF 7013
+#define XPRS_MIPRELCUTOFF 7014
+#define XPRS_PSEUDOCOST 7015
+#define XPRS_PENALTY 7016
+#define XPRS_BIGM 7018
+#define XPRS_MIPABSSTOP 7019
+#define XPRS_MIPRELSTOP 7020
+#define XPRS_CROSSOVERACCURACYTOL 7023
+#define XPRS_PRIMALPERTURB 7024
+#define XPRS_DUALPERTURB 7025
+#define XPRS_BAROBJSCALE 7026
+#define XPRS_BARRHSSCALE 7027
+#define XPRS_CHOLESKYTOL 7032
+#define XPRS_BARGAPSTOP 7033
+#define XPRS_BARDUALSTOP 7034
+#define XPRS_BARPRIMALSTOP 7035
+#define XPRS_BARSTEPSTOP 7036
+#define XPRS_ELIMTOL 7042
+#define XPRS_MARKOWITZTOL 7047
+#define XPRS_MIPABSGAPNOTIFY 7064
+#define XPRS_MIPRELGAPNOTIFY 7065
+#define XPRS_BARLARGEBOUND 7067
+#define XPRS_PPFACTOR 7069
+#define XPRS_REPAIRINDEFINITEQMAX 7071
+#define XPRS_BARGAPTARGET 7073
+#define XPRS_DUMMYCONTROL 7075
+#define XPRS_BARSTARTWEIGHT 7076
+#define XPRS_BARFREESCALE 7077
+#define XPRS_SBEFFORT 7086
+#define XPRS_HEURDIVERANDOMIZE 7089
+#define XPRS_HEURSEARCHEFFORT 7090
+#define XPRS_CUTFACTOR 7091
+#define XPRS_EIGENVALUETOL 7097
+#define XPRS_INDLINBIGM 7099
+#define XPRS_TREEMEMORYSAVINGTARGET 7100
+#define XPRS_INDPRELINBIGM 7102
+#define XPRS_RELAXTREEMEMORYLIMIT 7105
+#define XPRS_MIPABSGAPNOTIFYOBJ 7108
+#define XPRS_MIPABSGAPNOTIFYBOUND 7109
+#define XPRS_PRESOLVEMAXGROW 7110
+#define XPRS_HEURSEARCHTARGETSIZE 7112
+#define XPRS_CROSSOVERRELPIVOTTOL 7113
+#define XPRS_CROSSOVERRELPIVOTTOLSAFE 7114
+#define XPRS_DETLOGFREQ 7116
+#define XPRS_MAXIMPLIEDBOUND 7120
+#define XPRS_FEASTOLTARGET 7121
+#define XPRS_OPTIMALITYTOLTARGET 7122
+#define XPRS_PRECOMPONENTSEFFORT 7124
+#define XPRS_LPLOGDELAY 7127
+#define XPRS_HEURDIVEITERLIMIT 7128
+#define XPRS_BARKERNEL 7130
+#define XPRS_FEASTOLPERTURB 7132
+#define XPRS_CROSSOVERFEASWEIGHT 7133
+#define XPRS_LUPIVOTTOL 7139
+#define XPRS_MIPRESTARTGAPTHRESHOLD 7140
+#define XPRS_NODEPROBINGEFFORT 7141
+#define XPRS_INPUTTOL 7143
+#define XPRS_MIPRESTARTFACTOR 7145
+#define XPRS_BAROBJPERTURB 7146
+#define XPRS_CPIALPHA 7149
+#define XPRS_GLOBALBOUNDINGBOX 7154
+#define XPRS_TIMELIMIT 7158
+#define XPRS_SOLTIMELIMIT 7159
+#define XPRS_REPAIRINFEASTIMELIMIT 7160
+#define XPRS_EXTRAROWS 8004
+#define XPRS_EXTRACOLS 8005
+#define XPRS_LPITERLIMIT 8007
+#define XPRS_LPLOG 8009
+#define XPRS_SCALING 8010
+#define XPRS_PRESOLVE 8011
+#define XPRS_CRASH 8012
+#define XPRS_PRICINGALG 8013
+#define XPRS_INVERTFREQ 8014
+#define XPRS_INVERTMIN 8015
+#define XPRS_MAXNODE 8018
+#define XPRS_MAXTIME 8020
+#define XPRS_MAXMIPSOL 8021
+#define XPRS_SIFTPASSES 8022
+#define XPRS_DEFAULTALG 8023
+#define XPRS_VARSELECTION 8025
+#define XPRS_NODESELECTION 8026
+#define XPRS_BACKTRACK 8027
+#define XPRS_MIPLOG 8028
+#define XPRS_KEEPNROWS 8030
+#define XPRS_MPSECHO 8032
+#define XPRS_MAXPAGELINES 8034
+#define XPRS_OUTPUTLOG 8035
+#define XPRS_BARSOLUTION 8038
+#define XPRS_CACHESIZE 8043
+#define XPRS_CROSSOVER 8044
+#define XPRS_BARITERLIMIT 8045
+#define XPRS_CHOLESKYALG 8046
+#define XPRS_BAROUTPUT 8047
+#define XPRS_EXTRAMIPENTS 8051
+#define XPRS_REFACTOR 8052
+#define XPRS_BARTHREADS 8053
+#define XPRS_KEEPBASIS 8054
+#define XPRS_CROSSOVEROPS 8060
+#define XPRS_VERSION 8061
+#define XPRS_CROSSOVERTHREADS 8065
+#define XPRS_BIGMMETHOD 8068
+#define XPRS_MPSNAMELENGTH 8071
+#define XPRS_ELIMFILLIN 8073
+#define XPRS_PRESOLVEOPS 8077
+#define XPRS_MIPPRESOLVE 8078
+#define XPRS_MIPTHREADS 8079
+#define XPRS_BARORDER 8080
+#define XPRS_BREADTHFIRST 8082
+#define XPRS_AUTOPERTURB 8084
+#define XPRS_DENSECOLLIMIT 8086
+#define XPRS_CALLBACKFROMMASTERTHREAD 8090
+#define XPRS_MAXMCOEFFBUFFERELEMS 8091
+#define XPRS_REFINEOPS 8093
+#define XPRS_LPREFINEITERLIMIT 8094
+#define XPRS_MIPREFINEITERLIMIT 8095
+#define XPRS_DUALIZEOPS 8097
+#define XPRS_CROSSOVERITERLIMIT 8104
+#define XPRS_PREBASISRED 8106
+#define XPRS_PRESORT 8107
+#define XPRS_PREPERMUTE 8108
+#define XPRS_PREPERMUTESEED 8109
+#define XPRS_MAXMEMORYSOFT 8112
+#define XPRS_CUTFREQ 8116
+#define XPRS_SYMSELECT 8117
+#define XPRS_SYMMETRY 8118
+#define XPRS_MAXMEMORYHARD 8119
+#define XPRS_MIQCPALG 8125
+#define XPRS_QCCUTS 8126
+#define XPRS_QCROOTALG 8127
+#define XPRS_PRECONVERTSEPARABLE 8128
+#define XPRS_ALGAFTERNETWORK 8129
+#define XPRS_TRACE 8130
+#define XPRS_MAXIIS 8131
+#define XPRS_CPUTIME 8133
+#define XPRS_COVERCUTS 8134
+#define XPRS_GOMCUTS 8135
+#define XPRS_LPFOLDING 8136
+#define XPRS_MPSFORMAT 8137
+#define XPRS_CUTSTRATEGY 8138
+#define XPRS_CUTDEPTH 8139
+#define XPRS_TREECOVERCUTS 8140
+#define XPRS_TREEGOMCUTS 8141
+#define XPRS_CUTSELECT 8142
+#define XPRS_TREECUTSELECT 8143
+#define XPRS_DUALIZE 8144
+#define XPRS_DUALGRADIENT 8145
+#define XPRS_SBITERLIMIT 8146
+#define XPRS_SBBEST 8147
+#define XPRS_BARINDEFLIMIT 8153
+#define XPRS_HEURFREQ 8155
+#define XPRS_HEURDEPTH 8156
+#define XPRS_HEURMAXSOL 8157
+#define XPRS_HEURNODES 8158
+#define XPRS_LNPBEST 8160
+#define XPRS_LNPITERLIMIT 8161
+#define XPRS_BRANCHCHOICE 8162
+#define XPRS_BARREGULARIZE 8163
+#define XPRS_SBSELECT 8164
+#define XPRS_LOCALCHOICE 8170
+#define XPRS_LOCALBACKTRACK 8171
+#define XPRS_DUALSTRATEGY 8174
+#define XPRS_L1CACHE 8175
+#define XPRS_HEURDIVESTRATEGY 8177
+#define XPRS_HEURSELECT 8178
+#define XPRS_BARSTART 8180
+#define XPRS_PRESOLVEPASSES 8183
+#define XPRS_BARNUMSTABILITY 8186
+#define XPRS_BARORDERTHREADS 8187
+#define XPRS_EXTRASETS 8190
+#define XPRS_FEASIBILITYPUMP 8193
+#define XPRS_PRECOEFELIM 8194
+#define XPRS_PREDOMCOL 8195
+#define XPRS_HEURSEARCHFREQ 8196
+#define XPRS_HEURDIVESPEEDUP 8197
+#define XPRS_SBESTIMATE 8198
+#define XPRS_BARCORES 8202
+#define XPRS_MAXCHECKSONMAXTIME 8203
+#define XPRS_MAXCHECKSONMAXCUTTIME 8204
+#define XPRS_HISTORYCOSTS 8206
+#define XPRS_ALGAFTERCROSSOVER 8208
+#define XPRS_MUTEXCALLBACKS 8210
+#define XPRS_BARCRASH 8211
+#define XPRS_HEURDIVESOFTROUNDING 8215
+#define XPRS_HEURSEARCHROOTSELECT 8216
+#define XPRS_HEURSEARCHTREESELECT 8217
+#define XPRS_MPS18COMPATIBLE 8223
+#define XPRS_ROOTPRESOLVE 8224
+#define XPRS_CROSSOVERDRP 8227
+#define XPRS_FORCEOUTPUT 8229
+#define XPRS_PRIMALOPS 8231
+#define XPRS_DETERMINISTIC 8232
+#define XPRS_PREPROBING 8238
+#define XPRS_TREEMEMORYLIMIT 8242
+#define XPRS_TREECOMPRESSION 8243
+#define XPRS_TREEDIAGNOSTICS 8244
+#define XPRS_MAXTREEFILESIZE 8245
+#define XPRS_PRECLIQUESTRATEGY 8247
+#define XPRS_REPAIRINFEASMAXTIME 8250
+#define XPRS_IFCHECKCONVEXITY 8251
+#define XPRS_PRIMALUNSHIFT 8252
+#define XPRS_REPAIRINDEFINITEQ 8254
+#define XPRS_MIPRAMPUP 8255
+#define XPRS_MAXLOCALBACKTRACK 8257
+#define XPRS_USERSOLHEURISTIC 8258
+#define XPRS_FORCEPARALLELDUAL 8265
+#define XPRS_BACKTRACKTIE 8266
+#define XPRS_BRANCHDISJ 8267
+#define XPRS_MIPFRACREDUCE 8270
+#define XPRS_CONCURRENTTHREADS 8274
+#define XPRS_MAXSCALEFACTOR 8275
+#define XPRS_HEURTHREADS 8276
+#define XPRS_THREADS 8278
+#define XPRS_HEURBEFORELP 8280
+#define XPRS_PREDOMROW 8281
+#define XPRS_BRANCHSTRUCTURAL 8282
+#define XPRS_QUADRATICUNSHIFT 8284
+#define XPRS_BARPRESOLVEOPS 8286
+#define XPRS_QSIMPLEXOPS 8288
+#define XPRS_MIPRESTART 8290
+#define XPRS_CONFLICTCUTS 8292
+#define XPRS_PREPROTECTDUAL 8293
+#define XPRS_CORESPERCPU 8296
+#define XPRS_RESOURCESTRATEGY 8297
+#define XPRS_CLAMPING 8301
+#define XPRS_SLEEPONTHREADWAIT 8302
+#define XPRS_PREDUPROW 8307
+#define XPRS_CPUPLATFORM 8312
+#define XPRS_BARALG 8315
+#define XPRS_SIFTING 8319
+#define XPRS_LPLOGSTYLE 8326
+#define XPRS_RANDOMSEED 8328
+#define XPRS_TREEQCCUTS 8331
+#define XPRS_PRELINDEP 8333
+#define XPRS_DUALTHREADS 8334
+#define XPRS_PREOBJCUTDETECT 8336
+#define XPRS_PREBNDREDQUAD 8337
+#define XPRS_PREBNDREDCONE 8338
+#define XPRS_PRECOMPONENTS 8339
+#define XPRS_MAXMIPTASKS 8347
+#define XPRS_MIPTERMINATIONMETHOD 8348
+#define XPRS_PRECONEDECOMP 8349
+#define XPRS_HEURFORCESPECIALOBJ 8350
+#define XPRS_HEURSEARCHROOTCUTFREQ 8351
+#define XPRS_PREELIMQUAD 8353
+#define XPRS_PREIMPLICATIONS 8356
+#define XPRS_TUNERMODE 8359
+#define XPRS_TUNERMETHOD 8360
+#define XPRS_TUNERTARGET 8362
+#define XPRS_TUNERTHREADS 8363
+#define XPRS_TUNERHISTORY 8365
+#define XPRS_TUNERPERMUTE 8366
+#define XPRS_TUNERVERBOSE 8370
+#define XPRS_TUNEROUTPUT 8372
+#define XPRS_PREANALYTICCENTER 8374
+#define XPRS_NETCUTS 8382
+#define XPRS_LPFLAGS 8385
+#define XPRS_MIPKAPPAFREQ 8386
+#define XPRS_OBJSCALEFACTOR 8387
+#define XPRS_TREEFILELOGINTERVAL 8389
+#define XPRS_IGNORECONTAINERCPULIMIT 8390
+#define XPRS_IGNORECONTAINERMEMORYLIMIT 8391
+#define XPRS_MIPDUALREDUCTIONS 8392
+#define XPRS_GENCONSDUALREDUCTIONS 8395
+#define XPRS_PWLDUALREDUCTIONS 8396
+#define XPRS_BARFAILITERLIMIT 8398
+#define XPRS_AUTOSCALING 8406
+#define XPRS_GENCONSABSTRANSFORMATION 8408
+#define XPRS_COMPUTEJOBPRIORITY 8409
+#define XPRS_PREFOLDING 8410
+#define XPRS_NETSTALLLIMIT 8412
+#define XPRS_SERIALIZEPREINTSOL 8413
+#define XPRS_NUMERICALEMPHASIS 8416
+#define XPRS_PWLNONCONVEXTRANSFORMATION 8420
+#define XPRS_MIPCOMPONENTS 8421
+#define XPRS_MIPCONCURRENTNODES 8422
+#define XPRS_MIPCONCURRENTSOLVES 8423
+#define XPRS_OUTPUTCONTROLS 8424
+#define XPRS_SIFTSWITCH 8425
+#define XPRS_HEUREMPHASIS 8427
+#define XPRS_COMPUTEMATX 8428
+#define XPRS_COMPUTEMATX_IIS 8429
+#define XPRS_COMPUTEMATX_IISMAXTIME 8430
+#define XPRS_BARREFITER 8431
+#define XPRS_COMPUTELOG 8434
+#define XPRS_SIFTPRESOLVEOPS 8435
+#define XPRS_CHECKINPUTDATA 8436
+#define XPRS_ESCAPENAMES 8440
+#define XPRS_IOTIMEOUT 8442
+#define XPRS_AUTOCUTTING 8446
+#define XPRS_CALLBACKCHECKTIMEDELAY 8451
+#define XPRS_MULTIOBJOPS 8457
+#define XPRS_MULTIOBJLOG 8458
+#define XPRS_GLOBALSPATIALBRANCHIFPREFERORIG 8465
+#define XPRS_PRECONFIGURATION 8470
+#define XPRS_FEASIBILITYJUMP 8471
+#define XPRS_EXTRAELEMS 8006
+#define XPRS_EXTRASETELEMS 8191
+#define XPRS_LPOBJVAL 2001
+#define XPRS_MIPOBJVAL 2003
+#define XPRS_BESTBOUND 2004
+#define XPRS_OBJRHS 2005
+#define XPRS_OBJSENSE 2008
+#define XPRS_ROWS 1001
+#define XPRS_SIMPLEXITER 1009
+#define XPRS_LPSTATUS 1010
+#define XPRS_MIPSTATUS 1011
+#define XPRS_NODES 1013
+#define XPRS_COLS 1018
+#define XPRS_LP_OPTIMAL 1
+#define XPRS_LP_INFEAS 2
+#define XPRS_LP_UNBOUNDED 5
+#define XPRS_MIP_SOLUTION 4
+#define XPRS_MIP_INFEAS 5
+#define XPRS_MIP_OPTIMAL 6
+#define XPRS_MIP_UNBOUNDED 7
+#define XPRS_OBJ_MINIMIZE 1
+#define XPRS_OBJ_MAXIMIZE -1
+extern std::function XPRScreateprob;
+extern std::function XPRSdestroyprob;
+extern std::function XPRSinit;
+extern std::function XPRSfree;
+extern std::function XPRSgetlicerrmsg;
+extern std::function XPRSlicense;
+extern std::function XPRSgetbanner;
+extern std::function XPRSgetversion;
+extern std::function XPRSsetdefaultcontrol;
+extern std::function XPRSinterrupt;
+extern std::function XPRSsetintcontrol;
+extern std::function XPRSsetintcontrol64;
+extern std::function XPRSsetdblcontrol;
+extern std::function XPRSsetstrcontrol;
+extern std::function XPRSgetintcontrol;
+extern std::function XPRSgetintcontrol64;
+extern std::function XPRSgetdblcontrol;
+extern std::function XPRSgetstringcontrol;
+extern std::function XPRSgetintattrib;
+extern std::function XPRSgetdblattrib;
+extern std::function XPRSgetcontrolinfo;
+extern std::function XPRSloadlp;
+extern std::function XPRSloadlp64;
+extern std::function XPRSgetobj;
+extern std::function XPRSgetrhs;
+extern std::function XPRSgetrhsrange;
+extern std::function XPRSgetlb;
+extern std::function XPRSgetub;
+extern std::function XPRSgetcoef;
+extern std::function XPRSaddrows;
+extern std::function XPRSdelrows;
+extern std::function XPRSaddcols;
+extern std::function XPRSaddnames;
+extern std::function XPRSgetnames;
+extern std::function XPRSdelcols;
+extern std::function XPRSchgcoltype;
+extern std::function XPRSloadbasis;
+extern std::function XPRSpostsolve;
+extern std::function XPRSchgobjsense;
+extern std::function XPRSgetlasterror;
+extern std::function XPRSgetbasis;
+extern std::function XPRSwriteprob;
+extern std::function XPRSgetrowtype;
+extern std::function XPRSgetcoltype;
+extern std::function XPRSchgbounds;
+extern std::function XPRSaddmipsol;
+extern std::function XPRSgetlpsol;
+extern std::function XPRSgetmipsol;
+extern std::function XPRSchgobj;
+extern std::function XPRSchgcoef;
+extern std::function