Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
6e9ce9f
Support the message callback in Xpress.
djunglas Oct 1, 2025
672be76
Remove the "always on" message handler.
djunglas Oct 1, 2025
a396c97
Mark message callback as supported in test suite.
djunglas Oct 1, 2025
17d842f
Prepare for more callbacks being added and handle exceptions.
djunglas Oct 2, 2025
cdf7055
Small code optimization.
djunglas Oct 2, 2025
54bb914
Simplify handling errors in callback registration.
djunglas Oct 2, 2025
242dd6b
Apply `clang-format`.
djunglas Oct 2, 2025
a56f7f3
Rewrite callback handling.
djunglas Oct 2, 2025
058892a
Rewrite callback handling again.
djunglas Oct 6, 2025
f60b696
Add support for interrupters.
djunglas Oct 6, 2025
19b3f5c
Formatting.
djunglas Oct 6, 2025
be01567
Fix compilation problems.
djunglas Oct 7, 2025
1c32a13
clang-format.
djunglas Oct 7, 2025
cea9f0f
Review refactor, needs comments
XPRSc4v4 Oct 10, 2025
6e1c8d1
clang-format.
djunglas Oct 13, 2025
75e8cd5
Add missing control handling functions.
djunglas Oct 7, 2025
ecd3a2d
Support temporary setting of controls.
djunglas Oct 7, 2025
a08695f
Switch to `XPRSoptimize()` .
djunglas Oct 7, 2025
79b84c2
Fix setting of cut strategy.
djunglas Oct 7, 2025
797f515
Finish conversion to `XPRSoptimize()`.
djunglas Oct 7, 2025
f63af03
Enable support for integer variables.
djunglas Oct 7, 2025
720602f
Fix compilation issues.
djunglas Oct 7, 2025
acbf398
Fix extraction of free ranged rows.
djunglas Oct 8, 2025
dd48423
Enable more tests.
djunglas Oct 8, 2025
5d89668
Fix infeasibility status.
djunglas Oct 8, 2025
83f5181
Complete and fix `SolveStats` reporting.
djunglas Oct 8, 2025
e037d0b
Skip random seed tests.
djunglas Oct 8, 2025
6ca6015
Fix handling of presolve emphasis.
djunglas Oct 8, 2025
0a43864
Fix some parameter handling.
djunglas Oct 8, 2025
7e8b5f9
Do not return non-zero from checktime() callback.
djunglas Oct 8, 2025
8100885
Fix handling of cutoff return status.
djunglas Oct 8, 2025
b0744a0
Fix copy/paste error.
djunglas Oct 8, 2025
94ad842
Do not support `objective_limit`.
djunglas Oct 8, 2025
15d4ca8
Fix some test failures.
djunglas Oct 8, 2025
d4029ba
Add tests for unsupported parameters.
djunglas Oct 8, 2025
83e9e03
Fix expected value for test.
djunglas Oct 8, 2025
69efba4
Add status tests with integer variables.
djunglas Oct 9, 2025
7475b5e
Fix handling of memory limit.
djunglas Oct 9, 2025
0983ea9
Extend all tests to run with and without integer variables.
djunglas Oct 9, 2025
285d1d4
Propagate variable types to low-level Xpress.
djunglas Oct 9, 2025
360969c
Do not attach empty solutions for MIPs.
djunglas Oct 9, 2025
53498c0
Fix expected valus for MIPs.
djunglas Oct 9, 2025
424d179
Bail out if library has bad version.
djunglas Oct 9, 2025
dee7da4
Fix iteration limit for hybrid gradient.
djunglas Oct 10, 2025
7881ad6
Support arbitrary Xpress parameters.
djunglas Oct 10, 2025
2a76892
Implement model specific parameters.
djunglas Oct 10, 2025
e90135b
Fixes after rebase.
djunglas Oct 13, 2025
3a9b1ad
Add support for multi-objective.
djunglas Oct 13, 2025
c8b32d9
Add support for special ordered sets.
djunglas Oct 14, 2025
09fe47e
Add support for indicator constraints.
djunglas Oct 14, 2025
d47383e
Add support for quadratic constraints.
djunglas Oct 15, 2025
2b9db82
Factor out checking for singleton variables.
djunglas Oct 15, 2025
af80e2b
Support `EXPORT_MODEL` control to dump model.
djunglas Oct 15, 2025
b0a3fdb
Add support for second order cone constraints.
djunglas Oct 15, 2025
712a291
Only query "additional" solution for LPs.
djunglas Oct 17, 2025
cdbbe6d
Rename functions.
djunglas Oct 17, 2025
40bf758
Use `absl::Span<>` rather than raw arrays.
djunglas Oct 17, 2025
7841179
Cleanup and comments.
djunglas Oct 20, 2025
e35ff0b
Pass small types by value
djunglas Oct 21, 2025
a306b21
Pass small types by value
djunglas Oct 21, 2025
3b71120
Pass small types by value
djunglas Oct 21, 2025
ea4e172
Always initialize value
djunglas Oct 21, 2025
7353184
Always initialize
djunglas Oct 21, 2025
8b0618f
Always initialize
djunglas Oct 21, 2025
8398eda
Always initialize
djunglas Oct 21, 2025
abef7c6
Always initialize
djunglas Oct 21, 2025
20bf0ae
Use newline character instead of `std::endl` to avoid excessive flushing
djunglas Oct 21, 2025
3c9d27f
Get rid of redundant field.
djunglas Oct 21, 2025
62f2a86
Use enum class rather than plain enum.
djunglas Oct 21, 2025
440c4b8
Update reference documentation.
djunglas Oct 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ortools/math_opt/cpp/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,8 @@ class Model {
// The `weights` are an implementation detail in the solver used to order the
// `expressions`; see the Gurobi documentation for more detail:
// https://www.gurobi.com/documentation/9.5/refman/constraints.html#subsubsection:SOSConstraints
// For Xpress see
// https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/HTML/XPRSaddsets.html
//
// These `weights` must either be empty or the same length as `expressions`.
// If it is empty, default weights of 1, 2, ... will be used.
Expand Down Expand Up @@ -540,6 +542,8 @@ class Model {
// The `weights` are an implementation detail in the solver used to order the
// `expressions`; see the Gurobi documentation for more detail:
// https://www.gurobi.com/documentation/9.5/refman/constraints.html#subsubsection:SOSConstraints
// For Xpress see
// https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/HTML/XPRSaddsets.html
//
// These `weights` must either be empty or the same length as `expressions`.
// If it is empty, default weights of 1, 2, ... will be used.
Expand Down
22 changes: 22 additions & 0 deletions ortools/math_opt/cpp/parameters.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "ortools/math_opt/solvers/glpk.pb.h"
#include "ortools/math_opt/solvers/gurobi.pb.h"
#include "ortools/math_opt/solvers/highs.pb.h"
#include "ortools/math_opt/solvers/xpress.pb.h"
#include "ortools/port/proto_utils.h"
#include "ortools/util/status_macros.h"

Expand Down Expand Up @@ -213,6 +214,25 @@ GlpkParameters GlpkParameters::FromProto(const GlpkParametersProto& proto) {
return result;
}

XpressParametersProto XpressParameters::Proto() const {
XpressParametersProto result;
for (const auto& [key, val] : param_values) {
XpressParametersProto::Parameter& p = *result.add_parameters();
p.set_name(key);
p.set_value(val);
}
return result;
}

XpressParameters XpressParameters::FromProto(
const XpressParametersProto& proto) {
XpressParameters result;
for (const XpressParametersProto::Parameter& p : proto.parameters()) {
result.param_values[p.name()] = p.value();
}
return result;
}

SolveParametersProto SolveParameters::Proto() const {
SolveParametersProto result;
result.set_enable_output(enable_output);
Expand Down Expand Up @@ -265,6 +285,7 @@ SolveParametersProto SolveParameters::Proto() const {
*result.mutable_pdlp() = pdlp;
*result.mutable_glpk() = glpk.Proto();
*result.mutable_highs() = highs;
*result.mutable_xpress() = xpress.Proto();
return result;
}

Expand Down Expand Up @@ -324,6 +345,7 @@ absl::StatusOr<SolveParameters> SolveParameters::FromProto(
result.pdlp = proto.pdlp();
result.glpk = GlpkParameters::FromProto(proto.glpk());
result.highs = proto.highs();
result.xpress = XpressParameters::FromProto(proto.xpress());
return result;
}

Expand Down
26 changes: 25 additions & 1 deletion ortools/math_opt/cpp/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,27 @@ struct GlpkParameters {
static GlpkParameters FromProto(const GlpkParametersProto& proto);
};

// Xpress specific parameters for solving. See
// https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/HTML/chapter7.html
// for a list of possible parameters (called "controls" in Xpress).
//
// Example use:
// XpressParameters xpress;
// xpress.param_values["BarIterLimit"] = "10";
//
// Parameters are applied in the following order:
// * Any parameters derived from ortools parameters (like LP algorithm).
// * param_values in iteration order (insertion order).
struct XpressParameters {
// Parameter name-value pairs to set in insertion order.
gtl::linked_hash_map<std::string, std::string> param_values;

XpressParametersProto Proto() const;
static XpressParameters FromProto(const XpressParametersProto& proto);

bool empty() const { return param_values.empty(); }
};

// Parameters to control a single solve.
//
// Contains both parameters common to all solvers, e.g. time_limit, and
Expand Down Expand Up @@ -330,7 +351,8 @@ struct SolveParameters {
// Solvers will typically not return more solutions than the solution limit,
// but this is not enforced by MathOpt, see also b/214041169.
//
// Currently supported for Gurobi and SCIP, and for CP-SAT only with value 1.
// Currently supported for Gurobi, Xpress and SCIP, and for CP-SAT only with
// value 1.
std::optional<int32_t> solution_limit;

// If unset, use the solver default. If set, it must be >= 1.
Expand All @@ -347,6 +369,7 @@ struct SolveParameters {
// - Gurobi: [0:GRB_MAXINT] (which as of Gurobi 9.0 is 2x10^9).
// - GSCIP: [0:2147483647] (which is MAX_INT or kint32max or 2^31-1).
// - GLOP: [0:2147483647] (same as above)
// - Xpress: Any 32bit signed integer is allowed
// In all cases, the solver will receive a value equal to:
// MAX(0, MIN(MAX_VALID_VALUE_FOR_SOLVER, random_seed)).
std::optional<int32_t> random_seed;
Expand Down Expand Up @@ -425,6 +448,7 @@ struct SolveParameters {

GlpkParameters glpk;
HighsOptionsProto highs;
XpressParameters xpress;

SolveParametersProto Proto() const;
static absl::StatusOr<SolveParameters> FromProto(
Expand Down
3 changes: 3 additions & 0 deletions ortools/math_opt/parameters.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import "ortools/math_opt/solvers/gscip/gscip.proto";
import "ortools/math_opt/solvers/gurobi.proto";
import "ortools/math_opt/solvers/highs.proto";
import "ortools/math_opt/solvers/osqp.proto";
import "ortools/math_opt/solvers/xpress.proto";
import "ortools/sat/sat_parameters.proto";

option java_package = "com.google.ortools.mathopt";
Expand Down Expand Up @@ -376,5 +377,7 @@ message SolveParametersProto {

HighsOptionsProto highs = 27;

XpressParametersProto xpress = 28;

reserved 11; // Deleted
}
48 changes: 48 additions & 0 deletions ortools/math_opt/solver_tests/logical_constraint_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,22 @@ constexpr absl::string_view no_sos2_support_message =
constexpr absl::string_view no_indicator_support_message =
"This test is disabled as the solver does not support indicator "
"constraints";
constexpr absl::string_view no_updating_binary_variables_message =
"This test is disabled as the solver does not support updating "
"binary variables";
constexpr absl::string_view no_deleting_indicator_variables_message =
"This test is disabled as the solver does not support deleting "
"indicator variables";
constexpr absl::string_view no_incremental_add_and_deletes_message =
"This test is disabled as the solver does not support incremental "
"add/delete";

// We test SOS1 constraints with both explicit weights and default weights.
TEST_P(SimpleLogicalConstraintTest, CanBuildSos1Model) {
if (GetParam().solver_type == SolverType::kXpress) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest adding test suite parameters for these skipped tests (like supports_integer_variables, supports_indicator_constraints, etc).
maybe we can get feedback from someone on the OR-Tools team on this.

GTEST_SKIP() << "skipped since Xpress only supports SOS on variables (not "
"expressions)";
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
model.AddSos1Constraint({3.0 * x + 2.0}, {3.0});
Expand All @@ -105,6 +118,10 @@ TEST_P(SimpleLogicalConstraintTest, CanBuildSos1Model) {

// We test SOS2 constraints with both explicit weights and default weights.
TEST_P(SimpleLogicalConstraintTest, CanBuildSos2Model) {
if (GetParam().solver_type == SolverType::kXpress) {
GTEST_SKIP() << "skipped since Xpress only supports SOS on variables (not "
"expressions)";
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
model.AddSos2Constraint({3.0 * x + 2.0}, {3.0});
Expand Down Expand Up @@ -284,6 +301,9 @@ TEST_P(SimpleLogicalConstraintTest, Sos2VariableInMultipleTerms) {
// The optimal solution for the modified problem is (x*, y*) = (0, 1) with
// objective value 2.
TEST_P(IncrementalLogicalConstraintTest, LinearToSos1Update) {
if (!GetParam().supports_incremental_add_and_deletes) {
GTEST_SKIP() << no_incremental_add_and_deletes_message;
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -335,6 +355,9 @@ TEST_P(IncrementalLogicalConstraintTest, LinearToSos1Update) {
// The optimal solution for the modified problem is (x*, y*, z*) = (0, 1, 1)
// with objective value 4.
TEST_P(IncrementalLogicalConstraintTest, LinearToSos2Update) {
if (!GetParam().supports_incremental_add_and_deletes) {
GTEST_SKIP() << no_incremental_add_and_deletes_message;
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -760,6 +783,13 @@ TEST_P(SimpleLogicalConstraintTest, IndicatorsWithOddButValidBounds) {
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (GetParam().solver_type == SolverType::kXpress) {
// This test does not work with Xpress because the Xpress library will
// refuse to create an indicator constraint if the indicator variable is
// not a binary variable (it will raise error 1029);
GTEST_SKIP() << "This test is disabled as Xpress does not support "
"indicator constraints on non-binary variables";
}
Model model;
const Variable x = model.AddIntegerVariable(0.0, 0.0, "x");
const Variable y = model.AddIntegerVariable(1.0, 1.0, "y");
Expand Down Expand Up @@ -855,6 +885,9 @@ TEST_P(IncrementalLogicalConstraintTest, UpdateDeletesIndicatorConstraint) {
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (!GetParam().supports_incremental_add_and_deletes) {
GTEST_SKIP() << no_incremental_add_and_deletes_message;
}
Model model;
const Variable x = model.AddBinaryVariable("x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -898,6 +931,9 @@ TEST_P(IncrementalLogicalConstraintTest,
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (!GetParam().supports_incremental_add_and_deletes) {
GTEST_SKIP() << no_incremental_add_and_deletes_message;
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
const Variable indicator = model.AddBinaryVariable("indicator");
Expand Down Expand Up @@ -942,6 +978,9 @@ TEST_P(IncrementalLogicalConstraintTest, UpdateDeletesIndicatorVariable) {
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (!GetParam().supports_deleting_indicator_variables) {
GTEST_SKIP() << no_deleting_indicator_variables_message;
}
Model model;
const Variable x = model.AddBinaryVariable("x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -1022,6 +1061,9 @@ TEST_P(IncrementalLogicalConstraintTest,
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (!GetParam().supports_updating_binary_variables) {
GTEST_SKIP() << no_updating_binary_variables_message;
}
Model model;
const Variable x = model.AddBinaryVariable("x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -1069,6 +1111,9 @@ TEST_P(IncrementalLogicalConstraintTest, UpdateChangesIndicatorVariableBound) {
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (!GetParam().supports_updating_binary_variables) {
GTEST_SKIP() << no_updating_binary_variables_message;
}
Model model;
const Variable x = model.AddIntegerVariable(0.0, 1.0, "x");
const Variable y = model.AddIntegerVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -1133,6 +1178,9 @@ TEST_P(IncrementalLogicalConstraintTest,
if (!GetParam().supports_indicator_constraints) {
GTEST_SKIP() << no_indicator_support_message;
}
if (!GetParam().supports_updating_binary_variables) {
GTEST_SKIP() << no_updating_binary_variables_message;
}
Model model;
const Variable x = model.AddIntegerVariable(0.0, 1.0, "x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down
20 changes: 20 additions & 0 deletions ortools/math_opt/solver_tests/multi_objective_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ TEST_P(SimpleMultiObjectiveTest,
if (!GetParam().supports_integer_variables) {
GTEST_SKIP() << kNoIntegerVariableSupportMessage;
}
if (GetParam().solver_type == SolverType::kXpress) {
GTEST_SKIP() << "Ignoring this test because Xpress does not support per "
"objective time limits at the moment";
}
ASSERT_OK_AND_ASSIGN(const std::unique_ptr<Model> model,
Load23588MiplibInstance());
const Objective aux_obj = model->AddMaximizationObjective(
Expand Down Expand Up @@ -402,6 +406,10 @@ TEST_P(SimpleMultiObjectiveTest,
if (!GetParam().supports_integer_variables) {
GTEST_SKIP() << kNoIntegerVariableSupportMessage;
}
if (GetParam().solver_type == SolverType::kXpress) {
GTEST_SKIP() << "Ignoring this test because Xpress does not support per "
"objective time limits at the moment";
}
ASSERT_OK_AND_ASSIGN(const std::unique_ptr<Model> model,
Load23588MiplibInstance());
model->AddMaximizationObjective(0, /*priority=*/1);
Expand Down Expand Up @@ -463,6 +471,10 @@ TEST_P(SimpleMultiObjectiveTest,
if (!GetParam().supports_integer_variables) {
GTEST_SKIP() << kNoIntegerVariableSupportMessage;
}
if (GetParam().solver_type == SolverType::kXpress) {
GTEST_SKIP() << "Ignoring this test because Xpress does not support per "
"objective time limits at the moment";
}
ASSERT_OK_AND_ASSIGN(const std::unique_ptr<Model> model,
Load23588MiplibInstance());
SolveArguments args = {
Expand Down Expand Up @@ -583,6 +595,10 @@ TEST_P(IncrementalMultiObjectiveTest, AddObjectiveToMultiObjectiveModel) {
if (!GetParam().supports_auxiliary_objectives) {
GTEST_SKIP() << kNoMultiObjectiveSupportMessage;
}
if (!GetParam().supports_incremental_objective_add_and_delete) {
GTEST_SKIP()
<< "Ignoring this test as it requires support for incremental solve";
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down Expand Up @@ -636,6 +652,10 @@ TEST_P(IncrementalMultiObjectiveTest, DeleteObjectiveFromMultiObjectiveModel) {
if (!GetParam().supports_auxiliary_objectives) {
GTEST_SKIP() << kNoMultiObjectiveSupportMessage;
}
if (!GetParam().supports_incremental_objective_add_and_delete) {
GTEST_SKIP()
<< "Ignoring this test as it requires support for incremental solve";
}
Model model;
const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
Expand Down
Loading
Loading