21#include "absl/status/status.h"
22#include "absl/strings/string_view.h"
23#include "gtest/gtest.h"
45 <<
", supports_soc_constraints: "
47 <<
", supports_incremental_add_and_deletes: "
54using ::testing::AnyOf;
55using ::testing::HasSubstr;
56using ::testing::status::IsOkAndHolds;
57using ::testing::status::StatusIs;
62constexpr absl::string_view kNoSocSupportMessage =
63 "This test is disabled as the solver does not support second-order cone "
65constexpr absl::string_view kNoIncrementalAddAndDeletes =
66 "This test is disabled as the solver does not support incremental add and "
74TEST_P(SimpleSecondOrderConeTest, CanBuildSecondOrderConeModel) {
81 <<
"This test is disabled as Xpress rejects duplicate Q entries";
84 const Variable x = model.AddContinuousVariable(0.0, 1.0,
"x");
85 model.AddSecondOrderConeConstraint({
x}, 2 *
x);
86 if (GetParam().supports_soc_constraints) {
90 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
91 absl::StatusCode::kUnimplemented),
92 HasSubstr(
"second-order cone constraints")));
105 if (!GetParam().supports_soc_constraints) {
106 GTEST_SKIP() << kNoSocSupportMessage;
109 GTEST_SKIP() <<
"This test is disabled as Xpress only supports second "
110 "order cone constraints on singletons";
113 const Variable x = model.AddContinuousVariable(0.0, 1.0,
"x");
114 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
115 const Variable z = model.AddContinuousVariable(0.0, 1.0,
"z");
117 model.AddSecondOrderConeConstraint({
x, 2.0 * y, 3.0 * z}, 1.0);
120 7.0 / 6.0, {{
x, 6.0 / 7.0}, {y, 3.0 / 14.0}, {z, 2.0 / 21.0}},
132 if (!GetParam().supports_soc_constraints) {
133 GTEST_SKIP() << kNoSocSupportMessage;
136 GTEST_SKIP() <<
"This test is disabled as Xpress only supports second "
137 "order cone constraints on singletons";
140 const Variable x = model.AddContinuousVariable(0.0, 1.0,
"x");
141 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
143 model.AddSecondOrderConeConstraint({
x, 2.0 * y}, 2.0 *
x + 3.0);
144 model.AddSecondOrderConeConstraint({2.0 *
x, y}, 2.0 * y + 3.0);
158 if (!GetParam().supports_soc_constraints) {
159 GTEST_SKIP() << kNoSocSupportMessage;
165 model.AddLinearConstraint(
x - y <= 1.0);
166 model.AddSecondOrderConeConstraint({
x, y}, 2.0);
167 const double sqrt_of_seven = std::sqrt(7.0);
171 (sqrt_of_seven + 1.0) / 2.0,
172 {{
x, (sqrt_of_seven + 1.0) / 2.0}, {y, (sqrt_of_seven - 1.0) / 2.0}},
191 if (!GetParam().supports_incremental_add_and_deletes) {
192 GTEST_SKIP() << kNoIncrementalAddAndDeletes;
195 const Variable x = model.AddContinuousVariable(0.0, 1.0,
"x");
196 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
197 model.AddLinearConstraint(
x + 0.5 * y <= 1.0);
203 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
206 model.AddSecondOrderConeConstraint({
x, y}, std::sqrt(0.5));
208 if (!GetParam().supports_soc_constraints) {
217 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
218 absl::StatusCode::kUnimplemented),
219 AllOf(HasSubstr(
"second-order cone constraint"),
221 Not(HasSubstr(
"update failed")),
223 HasSubstr(
"solver re-creation failed"))));
227 ASSERT_THAT(solver->Update(),
228 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
231 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
254 if (!GetParam().supports_soc_constraints) {
255 GTEST_SKIP() << kNoSocSupportMessage;
257 if (!GetParam().supports_incremental_add_and_deletes) {
258 GTEST_SKIP() << kNoIncrementalAddAndDeletes;
261 const Variable x = model.AddContinuousVariable(0.0, 1.0,
"x");
262 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
263 model.AddLinearConstraint(
x + 0.5 * y <= 1.0);
265 model.AddSecondOrderConeConstraint({
x, y}, std::sqrt(0.5));
271 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
275 model.DeleteSecondOrderConeConstraint(
c);
277 ASSERT_THAT(solver->Update(),
278 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
281 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
301 if (!GetParam().supports_soc_constraints) {
302 GTEST_SKIP() << kNoSocSupportMessage;
305 const Variable x = model.AddContinuousVariable(0.0, 1.0,
"x");
306 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
307 model.AddSecondOrderConeConstraint({
x}, y);
313 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
316 model.DeleteVariable(y);
318 ASSERT_THAT(solver->Update(),
319 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
322 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
343 UpdateDeletesVariableInUpperBoundingExpression) {
344 if (!GetParam().supports_soc_constraints) {
345 GTEST_SKIP() << kNoSocSupportMessage;
347 if (!GetParam().supports_incremental_add_and_deletes) {
348 GTEST_SKIP() << kNoIncrementalAddAndDeletes;
351 const Variable x = model.AddContinuousVariable(0.0, 2.0,
"x");
352 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
353 model.AddSecondOrderConeConstraint({
x}, y + 1.0);
359 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
362 model.DeleteVariable(y);
364 ASSERT_THAT(solver->Update(),
365 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
368 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
389 if (!GetParam().supports_soc_constraints) {
390 GTEST_SKIP() << kNoSocSupportMessage;
392 if (!GetParam().supports_incremental_add_and_deletes) {
393 GTEST_SKIP() << kNoIncrementalAddAndDeletes;
396 const Variable x = model.AddContinuousVariable(1.0, 1.0,
"x");
397 const Variable y = model.AddContinuousVariable(0.0, 1.0,
"y");
398 model.AddSecondOrderConeConstraint({
x}, y);
404 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
407 model.DeleteVariable(
x);
409 ASSERT_THAT(solver->Update(),
410 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
413 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
434 if (!GetParam().supports_soc_constraints) {
435 GTEST_SKIP() << kNoSocSupportMessage;
437 if (!GetParam().supports_incremental_add_and_deletes) {
438 GTEST_SKIP() << kNoIncrementalAddAndDeletes;
441 const Variable x = model.AddContinuousVariable(1.0, 1.0,
"x");
442 const Variable y = model.AddContinuousVariable(0.0, 2.0,
"y");
443 model.AddSecondOrderConeConstraint({
x + 1.0}, y);
449 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
452 model.DeleteVariable(
x);
454 ASSERT_THAT(solver->Update(),
455 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
458 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined, bool set_is_fixed=false)
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
#define EXPECT_OK(expression)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
Matcher< SolveResult > IsOptimalWithSolution(const double expected_objective, const VariableMap< double > expected_variable_values, const double tolerance)
EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type), IsOkAndHolds(IsInfeasible(true, ModelSubset{ .variable_bounds={{x, ModelSubset::Bounds{.lower=false,.upper=true}}},.linear_constraints={ {c, ModelSubset::Bounds{.lower=true,.upper=false}}}})))
TEST_P(InfeasibleSubsystemTest, CanComputeInfeasibleSubsystem)
<=x<=1 IncrementalMipTest::IncrementalMipTest() :model_("incremental_solve_test"), x_(model_.AddContinuousVariable(0.0, 1.0, "x")), y_(model_.AddIntegerVariable(0.0, 2.0, "y")), c_(model_.AddLinearConstraint(0<=x_+y_<=1.5, "c")) { model_.Maximize(3.0 *x_+2.0 *y_+0.1);solver_=NewIncrementalSolver(&model_, TestedSolver()).value();const SolveResult first_solve=solver_->Solve().value();CHECK(first_solve.has_primal_feasible_solution());CHECK_LE(std::abs(first_solve.objective_value() - 3.6), kTolerance)<< first_solve.objective_value();} namespace { TEST_P(SimpleMipTest, OneVarMax) { Model model;const Variable x=model.AddVariable(0.0, 4.0, false, "x");model.Maximize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(8.0));EXPECT_THAT(result.variable_values(), IsNear({{x, 4.0}}));} TEST_P(SimpleMipTest, OneVarMin) { Model model;const Variable x=model.AddVariable(-2.4, 4.0, false, "x");model.Minimize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(-4.8));EXPECT_THAT(result.variable_values(), IsNear({{x, -2.4}}));} TEST_P(SimpleMipTest, OneIntegerVar) { Model model;const Variable x=model.AddVariable(0.0, 4.5, true, "x");model.Maximize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(8.0));EXPECT_THAT(result.variable_values(), IsNear({{x, 4.0}}));} TEST_P(SimpleMipTest, SimpleLinearConstraint) { Model model;const Variable x=model.AddBinaryVariable("x");const Variable y=model.AddBinaryVariable("y");model.Maximize(2.0 *x+y);model.AddLinearConstraint(0.0<=x+y<=1.5, "c");ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(2.0));EXPECT_THAT(result.variable_values(), IsNear({{x, 1}, {y, 0}}));} TEST_P(SimpleMipTest, Unbounded) { Model model;const Variable x=model.AddVariable(0.0, kInf, true, "x");model.Maximize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));if(GetParam().report_unboundness_correctly) { ASSERT_THAT(result, TerminatesWithOneOf({TerminationReason::kUnbounded, TerminationReason::kInfeasibleOrUnbounded}));} else { ASSERT_THAT(result, TerminatesWith(TerminationReason::kOtherError));} } TEST_P(SimpleMipTest, Infeasible) { Model model;const Variable x=model.AddVariable(0.0, 3.0, true, "x");model.Maximize(2.0 *x);model.AddLinearConstraint(x >=4.0);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, TerminatesWith(TerminationReason::kInfeasible));} TEST_P(SimpleMipTest, FractionalBoundsContainNoInteger) { if(GetParam().solver_type==SolverType::kGurobi) { GTEST_SKIP()<< "TODO(b/272298816): Gurobi bindings are broken here.";} if(GetParam().solver_type==SolverType::kXpress) { GTEST_SKIP()<< "Xpress does not support contradictory bounds.";} Model model;const Variable x=model.AddIntegerVariable(0.5, 0.6, "x");model.Maximize(x);EXPECT_THAT(Solve(model, GetParam().solver_type), IsOkAndHolds(TerminatesWith(TerminationReason::kInfeasible)));} TEST_P(IncrementalMipTest, EmptyUpdate) { ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());ASSERT_THAT(result, IsOptimal(3.6));EXPECT_THAT(result.variable_values(), IsNear({{x_, 0.5}, {y_, 1.0}}));} TEST_P(IncrementalMipTest, MakeContinuous) { model_.set_continuous(y_);ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());ASSERT_THAT(result, IsOptimal(4.1));EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.5}}));} TEST_P(IncrementalMipTest, DISABLED_MakeContinuousWithNonIntegralBounds) { solver_.reset();Model model("bounds");const Variable x=model.AddIntegerVariable(0.5, 1.5, "x");model.Maximize(x);ASSERT_OK_AND_ASSIGN(const auto solver, NewIncrementalSolver(&model, TestedSolver()));ASSERT_THAT(solver->Solve(), IsOkAndHolds(IsOptimal(1.0)));model.set_continuous(x);ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));ASSERT_THAT(solver->SolveWithoutUpdate(), IsOkAndHolds(IsOptimal(1.5)));model.Minimize(x);ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));ASSERT_THAT(solver-> IsOkAndHolds(IsOptimal(0.5)))
absl::StatusOr< std::unique_ptr< IncrementalSolver > > NewIncrementalSolver(Model *model, SolverType solver_type, SolverInitArguments arguments)
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
Matcher< UpdateResult > DidUpdate()
constexpr double kTolerance
std::string ProtobufShortDebugString(const P &message)
bool supports_incremental_add_and_deletes
SecondOrderConeTestParameters(SolverType solver_type, SolveParameters parameters, bool supports_soc_constraints, bool supports_incremental_add_and_deletes)
SolveParameters parameters
bool supports_soc_constraints
SolveParametersProto Proto() const