27#include "absl/log/check.h"
28#include "absl/status/statusor.h"
29#include "gtest/gtest.h"
34#include "ortools/math_opt/solution.pb.h"
40using ::testing::AnyOf;
41using ::testing::status::IsOkAndHolds;
43constexpr double kInf = std::numeric_limits<double>::infinity();
59 <<
" disallows_infeasible_or_unbounded: "
72 : model_(
"incremental_solve_test"),
73 zero_(model_.AddContinuousVariable(0, 0,
"zero")),
74 x_1_(model_.AddContinuousVariable(0, 1,
"x_1")),
75 y_1_(model_.AddContinuousVariable(0, 1,
"y_1")),
76 c_1_(model_.AddLinearConstraint(x_1_ + y_1_ <= 1.5,
"c_1")),
77 x_2_(model_.AddContinuousVariable(0, 1,
"x_2")),
78 y_2_(model_.AddContinuousVariable(0, 1,
"y_2")),
79 c_2_(model_.AddLinearConstraint(x_2_ + y_2_ <= 1.5,
"c_2")),
80 x_3_(model_.AddContinuousVariable(0, 1,
"x_3")),
81 y_3_(model_.AddContinuousVariable(0, 1,
"y_3")),
82 c_3_(model_.AddLinearConstraint(x_3_ + y_3_ <= 1.5,
"c_3")) {
83 model_.Maximize(0.1 + 3 * (x_1_ + x_2_ + x_3_) + 2 * (y_1_ + y_2_ + y_3_));
85 const SolveResult first_solve = solver_->Solve().value();
86 CHECK_OK(first_solve.termination.EnsureIsOptimal());
87 CHECK_LE(std::abs(first_solve.objective_value() - 12.1),
kTolerance);
92TEST_P(SimpleLpTest, ProtoNonIncrementalSolve) {
94 const Variable
x =
model.AddContinuousVariable(0, 1,
"x");
98 const SolveResultProto result,
102 {.parameters = GetParam().parameters.Proto()}));
103 ASSERT_EQ(result.termination().reason(), TERMINATION_REASON_OPTIMAL)
105 ASSERT_GE(result.solutions_size(), 1);
106 ASSERT_TRUE(result.solutions(0).has_primal_solution());
107 EXPECT_NEAR(result.solutions(0).primal_solution().objective_value(), 2.0,
109 EXPECT_EQ(result.solutions(0).primal_solution().feasibility_status(),
110 SOLUTION_STATUS_FEASIBLE);
111 if (GetParam().supports_duals) {
112 ASSERT_TRUE(result.solutions(0).has_dual_solution());
113 ASSERT_TRUE(result.solutions(0).dual_solution().has_objective_value());
114 EXPECT_NEAR(result.solutions(0).dual_solution().objective_value(), 2.0,
116 EXPECT_EQ(result.solutions(0).dual_solution().feasibility_status(),
117 SOLUTION_STATUS_FEASIBLE);
140TEST_P(SimpleLpTest, OneVarMax) {
142 const Variable
x =
model.AddContinuousVariable(0.0, 4.0,
"x");
147 if (GetParam().supports_duals) {
169TEST_P(SimpleLpTest, OneVarMin) {
171 const Variable
x =
model.AddContinuousVariable(-2.4, 4.0,
"x");
175 if (GetParam().supports_duals) {
201SolvedModel SimpleLinearConstraint(
double p) {
202 auto model = std::make_unique<Model>();
203 const Variable x_1 =
model->AddVariable(0.0, 1.0,
false,
"x_1");
204 const Variable x_2 =
model->AddVariable(0.0, 1.0,
false,
"x_2");
205 model->Maximize(2 * x_1 + x_2);
206 const LinearConstraint
y =
207 model->AddLinearConstraint(p <= x_1 + x_2 <= 1.5,
"y");
208 SolveResult result{Termination::Optimal(2.5)};
209 result.solutions.push_back(Solution{
211 PrimalSolution{.variable_values = {{x_1, 1.0}, {x_2, 0.5}},
212 .objective_value = 2.5,
215 DualSolution{.dual_values = {{
y, 1.0}},
216 .reduced_costs = {{x_1, 1.0}, {x_2, 0.0}},
217 .objective_value = 2.5,
219 return {.model = std::move(
model), .expected_result = result};
222TEST_P(SimpleLpTest, SimpleLinearConstraintRanged) {
223 const SolvedModel solved_model = SimpleLinearConstraint(0.0);
227 {.check_dual = GetParam().supports_duals})));
230TEST_P(SimpleLpTest, SimpleLinearConstraintNonRanged) {
231 const SolvedModel solved_model = SimpleLinearConstraint(-
kInf);
235 {.check_dual = GetParam().supports_duals})));
260SolvedModel SimpleLinearConstraintDualMin(
double p) {
261 auto model = std::make_unique<Model>();
262 const Variable x_1 =
model->AddVariable(0.0, 1.0,
false,
"x_1");
263 const Variable x_2 =
model->AddVariable(0.0, 1.0,
false,
"x_2");
264 model->Minimize(2 * x_1 + x_2);
265 const LinearConstraint
y =
266 model->AddLinearConstraint(0.5 <= x_1 + x_2 <= p,
"y");
267 SolveResult result{Termination::Optimal(0.5)};
268 result.solutions.push_back(Solution{
270 PrimalSolution{.variable_values = {{x_1, 0.0}, {x_2, 0.5}},
271 .objective_value = 0.5,
274 DualSolution{.dual_values = {{
y, 1.0}},
275 .reduced_costs = {{x_1, 1.0}, {x_2, 0.0}},
276 .objective_value = 0.5,
278 return {std::move(
model), result};
281TEST_P(SimpleLpTest, SimpleLinearConstraintDualMinRanged) {
282 const SolvedModel solved_model = SimpleLinearConstraintDualMin(1.5);
286 {.check_dual = GetParam().supports_duals})));
289TEST_P(SimpleLpTest, SimpleLinearConstraintDualMinNonRanged) {
290 const SolvedModel solved_model = SimpleLinearConstraintDualMin(
kInf);
294 {.check_dual = GetParam().supports_duals})));
319SolvedModel SimpleLinearConstraintDualLowerBounds(
double p) {
320 auto model = std::make_unique<Model>();
321 const Variable x_1 =
model->AddVariable(0.0, 1.0,
false,
"x_1");
322 const Variable x_2 =
model->AddVariable(0.0, 1.0,
false,
"x_2");
323 model->Maximize(-2 * x_1 - x_2);
324 const LinearConstraint
y =
325 model->AddLinearConstraint(0.5 <= x_1 + x_2 <= p,
"y");
326 SolveResult result{Termination::Optimal(-0.5)};
327 result.solutions.push_back(Solution{
329 PrimalSolution{.variable_values = {{x_1, 0.0}, {x_2, 0.5}},
330 .objective_value = -0.5,
333 DualSolution{.dual_values = {{
y, -1.0}},
334 .reduced_costs = {{x_1, -1.0}, {x_2, 0.0}},
335 .objective_value = -0.5,
337 return {std::move(
model), result};
340TEST_P(SimpleLpTest, SimpleLinearConstraintDualLowerBoundsRanged) {
341 const SolvedModel solved_model = SimpleLinearConstraintDualLowerBounds(1.5);
345 {.check_dual = GetParam().supports_duals})));
348TEST_P(SimpleLpTest, SimpleLinearConstraintDualLowerBoundsNonRanged) {
349 const SolvedModel solved_model = SimpleLinearConstraintDualLowerBounds(
kInf);
353 {.check_dual = GetParam().supports_duals})));
364SolvedModel SimpleUnboundedLP(
bool ranged) {
365 auto model = std::make_unique<Model>();
366 const Variable x_1 =
model->AddContinuousVariable(0.0,
kInf,
"x_1");
367 const Variable x_2 =
model->AddContinuousVariable(0.0,
kInf,
"x_2");
368 model->Maximize(2 * x_1 + x_2);
370 model->AddLinearConstraint(-1 <= x_1 - x_2 <= 1,
"y");
372 model->AddLinearConstraint(-1 <= x_1 - x_2,
"y_1");
373 model->AddLinearConstraint(x_1 - x_2 <= 1,
"y_2");
375 SolveResult result{Termination::Unbounded(
true)};
376 result.primal_rays.emplace_back().variable_values = {{x_1, 1.0}, {x_2, 1.0}};
377 return {std::move(
model), result};
380SolveResultMatcherOptions PrimalRayMatchOptions(
381 const SimpleLpTestParameters& test_params,
const SolveResult& actual) {
382 SolveResultMatcherOptions matcher_options;
383 matcher_options.inf_or_unb_soft_match =
384 !test_params.disallows_infeasible_or_unbounded;
385 matcher_options.check_rays =
386 test_params.ensures_primal_ray || actual.has_ray();
387 return matcher_options;
390TEST_P(SimpleLpTest, SimpleRangedRay) {
391 const SolvedModel solved_model = SimpleUnboundedLP(
true);
393 SimpleSolve(*solved_model.model));
396 PrimalRayMatchOptions(GetParam(), actual)));
399TEST_P(SimpleLpTest, SimpleNonRangedRay) {
400 const SolvedModel solved_model = SimpleUnboundedLP(
false);
402 SimpleSolve(*solved_model.model));
405 PrimalRayMatchOptions(GetParam(), actual)));
433SolvedModel SimpleInfeasibleLPMin(
double p) {
434 auto model = std::make_unique<Model>();
435 const Variable x_1 =
model->AddContinuousVariable(0, 3,
"x_1");
436 const Variable x_2 =
model->AddContinuousVariable(0, 3,
"x_2");
437 model->Minimize(2 * x_1 + x_2);
438 const LinearConstraint
y =
439 model->AddLinearConstraint(p <= x_1 + x_2 <= -1,
"y");
440 SolveResult result{Termination::Infeasible(
443 result.dual_rays.push_back(
444 {.dual_values = {{
y, -1.0}}, .reduced_costs = {{x_1, 1.0}, {x_2, 1.0}}});
445 return {std::move(
model), result};
448SolveResultMatcherOptions DualUnboundedMatchOptions(
449 const SimpleLpTestParameters& test_params,
const SolveResult& actual) {
450 SolveResultMatcherOptions matcher_options;
453 matcher_options.inf_or_unb_soft_match =
false;
457 matcher_options.inf_or_unb_soft_match =
true;
459 matcher_options.check_rays =
460 test_params.ensures_dual_ray || actual.has_dual_ray();
461 return matcher_options;
464TEST_P(SimpleLpTest, SimpleRangedInfeasibleMin) {
465 const SolvedModel solved_model = SimpleInfeasibleLPMin(-2.0);
467 SimpleSolve(*solved_model.model));
470 DualUnboundedMatchOptions(GetParam(), actual)));
473TEST_P(SimpleLpTest, SimpleNonRangedInfeasibleMin) {
474 const SolvedModel solved_model = SimpleInfeasibleLPMin(-
kInf);
476 SimpleSolve(*solved_model.model));
479 DualUnboundedMatchOptions(GetParam(), actual)));
506SolvedModel SimpleInfeasibleLPMax(
double p) {
507 auto model = std::make_unique<Model>();
508 const Variable x_1 =
model->AddContinuousVariable(0, 3,
"x_1");
509 const Variable x_2 =
model->AddContinuousVariable(0, 3,
"x_2");
510 model->Maximize(2 * x_1 + x_2);
511 const LinearConstraint
y =
512 model->AddLinearConstraint(p <= x_1 + x_2 <= -1,
"y");
513 SolveResult result{Termination::Infeasible(
516 result.dual_rays.push_back(
517 {.dual_values = {{
y, 1.0}}, .reduced_costs = {{x_1, -1.0}, {x_2, -1.0}}});
518 return {std::move(
model), result};
521TEST_P(SimpleLpTest, SimpleRangedInfeasibleMax) {
522 const SolvedModel solved_model = SimpleInfeasibleLPMax(-2.0);
524 SimpleSolve(*solved_model.model));
527 DualUnboundedMatchOptions(GetParam(), actual)));
530TEST_P(SimpleLpTest, SimpleNonRangedInfeasibleMax) {
531 const SolvedModel solved_model = SimpleInfeasibleLPMax(-
kInf);
533 SimpleSolve(*solved_model.model));
536 DualUnboundedMatchOptions(GetParam(), actual)));
578SolvedModel ConstraintDefinedBasisLP(
const double p) {
579 auto model = std::make_unique<Model>();
580 const Variable x_1 =
model->AddContinuousVariable(-1, 1,
"x_1");
581 const Variable x_2 =
model->AddContinuousVariable(0,
kInf,
"x_2");
582 model->Maximize(x_2);
583 const LinearConstraint y_1 =
584 model->AddLinearConstraint(-p <= x_1 + x_2 <= 2,
"y_1");
585 const LinearConstraint y_2 =
586 model->AddLinearConstraint(-2 <= x_1 - x_2 <= p,
"y_2");
587 SolveResult result{Termination::Optimal(2.0)};
588 result.solutions.push_back(Solution{
590 PrimalSolution{.variable_values = {{x_1, 0.0}, {x_2, 2.0}},
591 .objective_value = 2.0,
594 DualSolution{.dual_values = {{y_1, 0.5}, {y_2, -0.5}},
595 .reduced_costs = {{x_1, 0.0}, {x_2, 0.0}},
596 .objective_value = 2.0,
603 return {std::move(
model), result};
606TEST_P(SimpleLpTest, ConstraintDefinedBasisLPRanged) {
607 if (!GetParam().supports_basis) {
609 <<
"Getting the basis is not supported for this config, skipping test.";
611 const SolvedModel solved_model = ConstraintDefinedBasisLP(2.0);
614 {.check_basis = true})));
617TEST_P(SimpleLpTest, ConstraintDefinedBasisLPNonRanged) {
618 if (!GetParam().supports_basis) {
620 <<
"Getting the basis is not supported for this config, skipping test.";
622 const SolvedModel solved_model = ConstraintDefinedBasisLP(
kInf);
625 {.check_basis = true})));
666SolvedModel ConstraintVariableDefinedBasisLP(
const double p) {
667 auto model = std::make_unique<Model>();
668 model->set_is_maximize(
true);
669 const Variable x_1 =
model->AddContinuousVariable(-1.0, 1.0,
"x_1");
670 const Variable x_2 =
model->AddContinuousVariable(0.0,
kInf,
"x_2");
671 model->Maximize(2 * x_1 + x_2);
672 const LinearConstraint y_1 =
673 model->AddLinearConstraint(-p <= x_1 + x_2 <= 2.0,
"y_1");
674 const LinearConstraint y_2 =
675 model->AddLinearConstraint(-2.0 <= x_1 - x_2 <= p,
"y_2");
677 SolveResult result{Termination::Optimal(3.0)};
678 result.solutions.push_back(Solution{
680 PrimalSolution{.variable_values = {{x_1, 1.0}, {x_2, 1.0}},
681 .objective_value = 3,
684 DualSolution{.dual_values = {{y_1, 1.0}, {y_2, 0.0}},
685 .reduced_costs = {{x_1, 1.0}, {x_2, 0.0}},
686 .objective_value = 3.0,
694 return {std::move(
model), result};
697TEST_P(SimpleLpTest, ConstraintVariableDefinedBasisLPRanged) {
698 if (!GetParam().supports_basis) {
700 <<
"Getting the basis is not supported for this config, skipping test.";
702 const SolvedModel solved_model = ConstraintVariableDefinedBasisLP(2.0);
705 {.check_basis = true})));
708TEST_P(SimpleLpTest, ConstraintVariableDefinedBasisLPNonRanged) {
709 if (!GetParam().supports_basis) {
711 <<
"Getting the basis is not supported for this config, skipping test.";
713 const SolvedModel solved_model = ConstraintVariableDefinedBasisLP(
kInf);
716 {.check_basis = true})));
755SolvedModel VariableDefinedBasisLP(
const double p) {
756 auto model = std::make_unique<Model>();
757 const Variable x_1 =
model->AddContinuousVariable(-1.0, 1.0,
"x_1");
758 const Variable x_2 =
model->AddContinuousVariable(0.0,
kInf,
"x_2");
759 model->Minimize(x_1 + x_2);
760 const LinearConstraint y_1 =
761 model->AddLinearConstraint(-p <= x_1 + x_2 <= 2.0,
"y_1");
762 const LinearConstraint y_2 =
763 model->AddLinearConstraint(-2.0 <= x_1 - x_2 <= p,
"y_2");
765 SolveResult result{Termination::Optimal(-1.0)};
766 result.solutions.push_back(Solution{
768 PrimalSolution{.variable_values = {{x_1, -1.0}, {x_2, 0.0}},
769 .objective_value = -1,
772 DualSolution{.dual_values = {{y_1, 0.0}, {y_2, 0.0}},
773 .reduced_costs = {{x_1, 1.0}, {x_2, 1.0}},
774 .objective_value = -1,
781 return {std::move(
model), result};
784TEST_P(SimpleLpTest, VariableDefinedBasisLPRanged) {
785 if (!GetParam().supports_basis) {
787 <<
"Getting the basis is not supported for this config, skipping test.";
789 const SolvedModel solved_model = VariableDefinedBasisLP(2.0);
792 {.check_basis = true})));
795TEST_P(SimpleLpTest, VariableDefinedBasisLPNonRanged) {
796 if (!GetParam().supports_basis) {
798 <<
"Getting the basis is not supported for this config, skipping test.";
800 const SolvedModel solved_model = VariableDefinedBasisLP(
kInf);
803 {.check_basis = true})));
834TEST_P(SimpleLpTest, FixedBasis) {
835 if (!GetParam().supports_basis) {
837 <<
"Getting the basis is not supported for this config, skipping test.";
841 const Variable x_1 =
model.AddContinuousVariable(0.0, 1.0,
"x_1");
842 const Variable x_2 =
model.AddContinuousVariable(-
kInf,
kInf,
"x_2");
843 model.Maximize(x_1 + x_2);
844 const LinearConstraint y_1 =
845 model.AddLinearConstraint(-x_1 + x_2 == 0.0,
"y_1");
849 {{x_1, 2.0}, {x_2, 0.0}}));
850 const Basis expected_basis_alternative_one = {
855 const Basis expected_basis_alternative_two = {
861 ASSERT_TRUE(result.has_basis());
863 AnyOf(
BasisIs(expected_basis_alternative_one),
864 BasisIs(expected_basis_alternative_two)));
905TEST_P(SimpleLpTest, FreeBasis) {
906 if (!GetParam().supports_basis) {
908 <<
"Getting the basis is not supported for this config, skipping test.";
913 const Variable x_1 =
model.AddContinuousVariable(-
kInf,
kInf,
"x_1");
914 const Variable x_2 =
model.AddContinuousVariable(-
kInf,
kInf,
"x_2");
915 const LinearConstraint y_1 =
920 {{x_1, 0.0}, {x_2, 0.0}}));
922 const Basis expected_basis_alternative_one = {
927 const Basis expected_basis_alternative_two = {
932 ASSERT_TRUE(result.has_basis());
934 AnyOf(
BasisIs(expected_basis_alternative_one),
935 BasisIs(expected_basis_alternative_two)));
942TEST_P(SimpleLpTest, OptimalAfterInfeasible) {
946 GTEST_SKIP() <<
"Glpk returns [GLP_EFAIL] for the first solve.";
949 const Variable
x =
model.AddContinuousVariable(0, 1,
"x");
951 model.AddLinearConstraint(
x >= 2);
953 const SolveArguments arguments{.parameters = GetParam().parameters};
961 model.set_upper_bound(
x, 3);
965TEST_P(SimpleLpTest, OptimalAfterUnbounded) {
969 GTEST_SKIP() <<
"Glpk returns [GLP_EFAIL] for the first solve.";
972 const Variable
x =
model.AddContinuousVariable(-
kInf, 1,
"x");
975 const SolveArguments arguments{.parameters = GetParam().parameters};
983 model.set_lower_bound(
x, 0);
987TEST_P(IncrementalLpTest, EmptyUpdate) {
992TEST_P(IncrementalLpTest, ObjDir) {
993 model_.set_minimize();
998TEST_P(IncrementalLpTest, ObjOffset) {
999 model_.set_objective_offset(1.1);
1004TEST_P(IncrementalLpTest, LinearObjCoef) {
1005 model_.set_objective_coefficient(x_1_, 5.0);
1006 model_.set_objective_coefficient(x_2_, 5.0);
1007 model_.set_objective_coefficient(x_3_, 5.0);
1013TEST_P(IncrementalLpTest, LinearObjCoefAndRemove) {
1014 model_.DeleteVariable(zero_);
1015 model_.set_objective_coefficient(x_1_, 5.0);
1016 model_.set_objective_coefficient(x_2_, 5.0);
1017 model_.set_objective_coefficient(x_3_, 5.0);
1023TEST_P(IncrementalLpTest, LinearObjCoefAfterRemove) {
1024 model_.DeleteVariable(zero_);
1027 model_.set_objective_coefficient(x_1_, 5.0);
1028 model_.set_objective_coefficient(x_2_, 5.0);
1029 model_.set_objective_coefficient(x_3_, 5.0);
1043TEST_P(IncrementalLpTest, VariableLb) {
1044 model_.set_lower_bound(y_1_, 0.75);
1045 model_.set_lower_bound(y_2_, 0.75);
1046 model_.set_lower_bound(y_3_, 0.75);
1052TEST_P(IncrementalLpTest, VariableLbAndRemove) {
1053 model_.DeleteVariable(zero_);
1054 model_.set_lower_bound(y_1_, 0.75);
1055 model_.set_lower_bound(y_2_, 0.75);
1056 model_.set_lower_bound(y_3_, 0.75);
1062TEST_P(IncrementalLpTest, VariableLbAfterRemove) {
1063 model_.DeleteVariable(zero_);
1066 model_.set_lower_bound(y_1_, 0.75);
1067 model_.set_lower_bound(y_2_, 0.75);
1068 model_.set_lower_bound(y_3_, 0.75);
1074TEST_P(IncrementalLpTest, VariableUb) {
1075 model_.set_upper_bound(x_1_, 0.5);
1076 model_.set_upper_bound(x_2_, 0.5);
1077 model_.set_upper_bound(x_3_, 0.5);
1083TEST_P(IncrementalLpTest, LinearConstraintLb) {
1084 model_.set_lower_bound(c_1_, 1.0);
1085 model_.set_lower_bound(c_2_, 1.0);
1086 model_.set_lower_bound(c_3_, 1.0);
1092 EXPECT_EQ(result.solve_stats.simplex_iterations, 0);
1093 EXPECT_EQ(result.solve_stats.barrier_iterations, 0);
1094 EXPECT_EQ(result.solve_stats.first_order_iterations, 0);
1098TEST_P(IncrementalLpTest, ConstraintTypeSwitch) {
1104 model_.set_lower_bound(c_1_, 1.0);
1107 solver_->SolveWithoutUpdate());
1111 EXPECT_EQ(first_result.solve_stats.simplex_iterations, 0);
1112 EXPECT_EQ(first_result.solve_stats.barrier_iterations, 0);
1113 EXPECT_EQ(first_result.solve_stats.first_order_iterations, 0);
1118 model_.set_lower_bound(c_1_, -
kInf);
1119 model_.set_lower_bound(c_2_, 1.0);
1122 solver_->SolveWithoutUpdate());
1126 EXPECT_EQ(second_result.solve_stats.simplex_iterations, 0);
1127 EXPECT_EQ(second_result.solve_stats.barrier_iterations, 0);
1128 EXPECT_EQ(second_result.solve_stats.first_order_iterations, 0);
1131 model_.set_lower_bound(c_2_, -
kInf);
1134 solver_->SolveWithoutUpdate());
1138 EXPECT_EQ(third_result.solve_stats.simplex_iterations, 0);
1139 EXPECT_EQ(third_result.solve_stats.barrier_iterations, 0);
1140 EXPECT_EQ(third_result.solve_stats.first_order_iterations, 0);
1143TEST_P(IncrementalLpTest, LinearConstraintUb) {
1144 model_.set_upper_bound(c_1_, 2.0);
1145 model_.set_upper_bound(c_2_, 2.0);
1146 model_.set_upper_bound(c_3_, 2.0);
1152TEST_P(IncrementalLpTest, LinearConstraintCoefficient) {
1153 model_.set_coefficient(c_1_, y_1_, 0.5);
1154 model_.set_coefficient(c_2_, y_2_, 0.5);
1155 model_.set_coefficient(c_3_, y_3_, 0.5);
1161TEST_P(IncrementalLpTest, AddVariable) {
1162 const Variable z_1 = model_.AddContinuousVariable(0.0, 1.0,
"z_1");
1163 model_.set_objective_coefficient(z_1, 10.0);
1164 model_.set_coefficient(c_1_, z_1, 1.0);
1165 const Variable z_2 = model_.AddContinuousVariable(0.0, 1.0,
"z_2");
1166 model_.set_objective_coefficient(z_2, 10.0);
1167 model_.set_coefficient(c_2_, z_2, 1.0);
1168 const Variable z_3 = model_.AddContinuousVariable(0.0, 1.0,
"z_3");
1169 model_.set_objective_coefficient(z_3, 10.0);
1170 model_.set_coefficient(c_3_, z_3, 1.0);
1176TEST_P(IncrementalLpTest, AddLinearConstraint) {
1177 const LinearConstraint d_1 = model_.AddLinearConstraint(0.0, 2.0,
"d_1");
1178 model_.set_coefficient(d_1, x_1_, 1.0);
1179 model_.set_coefficient(d_1, y_1_, 2.0);
1180 const LinearConstraint d_2 = model_.AddLinearConstraint(0.0, 2.0,
"d_2");
1181 model_.set_coefficient(d_2, x_2_, 1.0);
1182 model_.set_coefficient(d_2, y_2_, 2.0);
1183 const LinearConstraint d_3 = model_.AddLinearConstraint(0.0, 2.0,
"d_3");
1184 model_.set_coefficient(d_3, x_3_, 1.0);
1185 model_.set_coefficient(d_3, y_3_, 2.0);
1191TEST_P(IncrementalLpTest, DeleteVariable) {
1192 model_.DeleteVariable(x_1_);
1193 model_.DeleteVariable(x_2_);
1194 model_.DeleteVariable(x_3_);
1200TEST_P(IncrementalLpTest, DeleteLinearConstraint) {
1201 model_.DeleteLinearConstraint(c_1_);
1202 model_.DeleteLinearConstraint(c_2_);
1203 model_.DeleteLinearConstraint(c_3_);
1209TEST_P(IncrementalLpTest, ChangeBoundsWithTemporaryInversion) {
1210 model_.set_lower_bound(x_1_, 3.0);
1214 model_.set_upper_bound(x_1_, 5.0);
1222 model_.set_lower_bound(c_1_, 4.0);
1227 model_.set_upper_bound(c_1_, 5.5);
1231 solver_->SolveWithoutUpdate(),
static absl::StatusOr< SolveResultProto > NonIncrementalSolve(const ModelProto &model, SolverTypeProto solver_type, const InitArgs &init_args, const SolveArgs &solve_args)
A shortcut for calling Solver::New() and then Solver::Solve().
absl::StatusOr< bool > Update(ModelUpdateProto model_update) override
CpModelProto proto
The output proto.
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)
@ kInfeasible
The primal problem has no feasible solutions.
<=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.";} 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-> IsOkAndHolds(IsOptimal(1.5)))
ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()))
Matcher< SolveResult > TerminatesWithOneOf(const std::vector< TerminationReason > &allowed)
Checks that the result has one of the allowed termination reasons.
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
Matcher< SolveResult > IsConsistentWith(const SolveResult &expected, const SolveResultMatcherOptions &options)
absl::StatusOr< std::unique_ptr< IncrementalSolver > > NewIncrementalSolver(Model *model, SolverType solver_type, SolverInitArguments arguments)
@ kFeasible
Solver claims the solution is feasible.
@ kFixedValue
The variable/constraint has identical finite lower and upper bounds.
@ kBasic
The variable/constraint is basic.
@ kFree
The variable/constraint is free (it has no finite bounds).
@ kAtLowerBound
The variable/constraint is at its lower bound (which must be finite).
@ kAtUpperBound
The variable/constraint is at its upper bound (which must be finite).
Enum< E >::Proto EnumToProto(std::optional< E > value)
Matcher< UpdateResult > DidUpdate()
Actual UpdateResult.did_update is true.
constexpr double kTolerance
Matcher< SolveResult > IsOptimalWithDualSolution(const double expected_objective, const LinearConstraintMap< double > expected_dual_values, const VariableMap< double > expected_reduced_costs, const double tolerance)
Matcher< Basis > BasisIs(const Basis &expected)
Matcher< SolveResult > IsOptimal(const std::optional< double > expected_primal_objective, const double tolerance)
@ kFeasible
Solver claims the problem is feasible.
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtobufShortDebugString(const P &message)
std::string ProtobufDebugString(const P &message)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
bool supports_basis
True if the solver produces a basis.
SolverType solver_type
The tested solver.
bool supports_duals
True if a dual solution is returned.
bool disallows_infeasible_or_unbounded
SolveParameters parameters
SolveParametersProto Proto() const
std::unique_ptr< Model > model
SolveResult expected_result