20#include "absl/status/status.h"
21#include "absl/strings/string_view.h"
22#include "gtest/gtest.h"
26#include "ortools/math_opt/model_update.pb.h"
27#include "ortools/math_opt/result.pb.h"
34 const bool supports_integer_variables,
const bool supports_sos1,
35 const bool supports_sos2,
const bool supports_indicator_constraints,
36 const bool supports_incremental_add_and_deletes,
37 const bool supports_incremental_variable_deletions,
38 const bool supports_deleting_indicator_variables,
39 const bool supports_updating_binary_variables)
40 : solver_type(solver_type),
42 supports_integer_variables(supports_integer_variables),
43 supports_sos1(supports_sos1),
44 supports_sos2(supports_sos2),
45 supports_indicator_constraints(supports_indicator_constraints),
46 supports_incremental_add_and_deletes(
47 supports_incremental_add_and_deletes),
48 supports_incremental_variable_deletions(
49 supports_incremental_variable_deletions),
50 supports_deleting_indicator_variables(
51 supports_deleting_indicator_variables),
52 supports_updating_binary_variables(supports_updating_binary_variables) {}
58 <<
", supports_integer_variables: "
60 <<
", supports_sos1: " << (params.
supports_sos1 ?
"true" :
"false")
61 <<
", supports_sos2: " << (params.
supports_sos2 ?
"true" :
"false")
62 <<
", supports_indicator_constraints: "
64 <<
", supports_incremental_add_and_deletes: "
66 <<
", supports_incremental_variable_deletions: "
68 <<
", supports_deleting_indicator_variables: "
70 <<
", supports_updating_binary_variables: "
77using ::testing::AnyOf;
78using ::testing::HasSubstr;
79using ::testing::status::IsOkAndHolds;
80using ::testing::status::StatusIs;
82constexpr absl::string_view no_sos1_support_message =
83 "This test is disabled as the solver does not support sos1 constraints";
84constexpr absl::string_view no_sos2_support_message =
85 "This test is disabled as the solver does not support sos2 constraints";
86constexpr absl::string_view no_indicator_support_message =
87 "This test is disabled as the solver does not support indicator "
91TEST_P(SimpleLogicalConstraintTest, CanBuildSos1Model) {
93 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
94 model.AddSos1Constraint({3.0 *
x + 2.0}, {3.0});
95 model.AddSos1Constraint({2.0 *
x + 1.0}, {});
96 if (GetParam().supports_sos1) {
100 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
101 absl::StatusCode::kUnimplemented),
102 HasSubstr(
"sos1 constraints")));
107TEST_P(SimpleLogicalConstraintTest, CanBuildSos2Model) {
109 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
110 model.AddSos2Constraint({3.0 *
x + 2.0}, {3.0});
111 model.AddSos2Constraint({2.0 *
x + 1.0}, {});
112 if (GetParam().supports_sos2) {
116 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
117 absl::StatusCode::kUnimplemented),
118 HasSubstr(
"sos2 constraints")));
129TEST_P(SimpleLogicalConstraintTest, SimpleSos1Instance) {
130 if (!GetParam().supports_sos1) {
131 GTEST_SKIP() << no_sos1_support_message;
134 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
135 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
137 model.AddSos1Constraint({
x,
y}, {});
150TEST_P(SimpleLogicalConstraintTest, SimpleSos2Instance) {
151 if (!GetParam().supports_sos2) {
152 GTEST_SKIP() << no_sos2_support_message;
155 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
156 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
157 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
158 model.Maximize(2.0 *
x + 1.0 *
y + 3.0 * z);
159 model.AddSos2Constraint({
x,
y, z}, {});
162 4.0, {{
x, 0.0}, {
y, 1.0}, {z, 1.0}})));
173TEST_P(SimpleLogicalConstraintTest, InstanceWithSos1AndSos2) {
174 if (!GetParam().supports_sos1) {
175 GTEST_SKIP() << no_sos1_support_message;
177 if (!GetParam().supports_sos2) {
178 GTEST_SKIP() << no_sos2_support_message;
181 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
182 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
183 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
184 model.Maximize(2.0 *
x + 1.5 *
y + 3.0 * z);
185 model.AddSos1Constraint({
y, z}, {});
186 model.AddSos2Constraint({
x,
y, z}, {});
189 3.5, {{
x, 1.0}, {
y, 1.0}, {z, 0.0}})));
199TEST_P(SimpleLogicalConstraintTest, Sos1WithExpressions) {
200 if (!GetParam().supports_sos1) {
201 GTEST_SKIP() << no_sos1_support_message;
204 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
205 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
207 model.AddSos1Constraint({2 *
x - 1,
y - 0.75}, {});
220TEST_P(SimpleLogicalConstraintTest, Sos2WithExpressions) {
221 if (!GetParam().supports_sos2) {
222 GTEST_SKIP() << no_sos2_support_message;
225 const Variable
x =
model.AddContinuousVariable(-1.0, 1.0,
"x");
226 const Variable
y =
model.AddContinuousVariable(-1.0, 1.0,
"y");
227 const Variable z =
model.AddContinuousVariable(-1.0, 1.0,
"z");
229 model.AddSos2Constraint({2 *
x + 1, 8 *
y + 1, 4 * z + 1}, {});
232 1.75, {{
x, 1.0}, {
y, 1.0}, {z, -0.25}})));
242TEST_P(SimpleLogicalConstraintTest, Sos1VariableInMultipleTerms) {
243 if (!GetParam().supports_sos1) {
244 GTEST_SKIP() << no_sos2_support_message;
247 const Variable
x =
model.AddContinuousVariable(-1.0, 1.0,
"x");
249 model.AddSos1Constraint({
x,
x});
262TEST_P(SimpleLogicalConstraintTest, Sos2VariableInMultipleTerms) {
263 if (!GetParam().supports_sos2) {
264 GTEST_SKIP() << no_sos2_support_message;
267 const Variable
x =
model.AddContinuousVariable(-1.0, 1.0,
"x");
269 model.AddSos2Constraint({
x, 0.0,
x});
286TEST_P(IncrementalLogicalConstraintTest, LinearToSos1Update) {
288 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
289 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
295 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
298 model.AddSos1Constraint({
x,
y}, {});
300 if (!GetParam().supports_sos1) {
309 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
310 absl::StatusCode::kUnimplemented),
311 AllOf(HasSubstr(
"sos1 constraint"),
313 Not(HasSubstr(
"update failed")),
315 HasSubstr(
"solver re-creation failed"))));
319 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
322 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
337TEST_P(IncrementalLogicalConstraintTest, LinearToSos2Update) {
339 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
340 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
341 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
342 model.Maximize(2.0 *
x + 1.0 *
y + 3.0 * z);
348 solver->Solve({.parameters = GetParam().parameters}),
351 model.AddSos2Constraint({
x,
y, z}, {});
353 if (!GetParam().supports_sos2) {
362 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
363 absl::StatusCode::kUnimplemented),
364 AllOf(HasSubstr(
"sos2 constraint"),
366 Not(HasSubstr(
"update failed")),
368 HasSubstr(
"solver re-creation failed"))));
372 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
376 solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
396TEST_P(IncrementalLogicalConstraintTest, UpdateDeletesSos1Constraint) {
397 if (!GetParam().supports_sos1) {
398 GTEST_SKIP() << no_sos1_support_message;
401 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
402 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
404 model.AddLinearConstraint(
x +
y <= 1);
405 const Sos1Constraint
c =
model.AddSos1Constraint({2 *
x - 1, 4 *
y - 3}, {});
410 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
413 model.DeleteSos1Constraint(
c);
416 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
419 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
440TEST_P(IncrementalLogicalConstraintTest, UpdateDeletesSos2Constraint) {
441 if (!GetParam().supports_sos2) {
442 GTEST_SKIP() << no_sos1_support_message;
445 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
446 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
447 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
448 model.Maximize(
x + 3 *
y + 2 * z);
449 model.AddLinearConstraint(
x +
y + z <= 2);
450 const Sos2Constraint
c =
451 model.AddSos2Constraint({2 *
x - 1, 8 *
y - 1, 4 * z - 1}, {});
457 solver->Solve({.parameters = GetParam().parameters}),
460 model.DeleteSos2Constraint(
c);
463 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
467 solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
488TEST_P(IncrementalLogicalConstraintTest,
489 UpdateDeletesVariableInSos1Constraint) {
490 if (!GetParam().supports_sos1) {
491 GTEST_SKIP() << no_sos1_support_message;
494 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
495 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
496 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
497 const Variable
w =
model.AddContinuousVariable(0.0, 1.0,
"w");
498 model.Maximize(2.0 *
x + 2.0 *
y + z +
w);
499 model.AddSos1Constraint({
x,
y +
w, z}, {});
504 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
506 3.0, {{
x, 0.0}, {
y, 1.0}, {z, 0.0}, {
w, 1.0}})));
511 IsOkAndHolds(GetParam().supports_incremental_variable_deletions
515 solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
536TEST_P(IncrementalLogicalConstraintTest,
537 UpdateDeletesVariableInSos2Constraint) {
538 if (!GetParam().supports_sos2) {
539 GTEST_SKIP() << no_sos2_support_message;
542 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
543 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
544 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
545 const Variable
w =
model.AddContinuousVariable(0.0, 1.0,
"w");
546 model.Maximize(2.0 *
x + 2.0 *
y + 2.0 * z +
w);
547 model.AddSos2Constraint({
x,
y, z +
w}, {});
552 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
554 5.0, {{
x, 0.0}, {
y, 1.0}, {z, 1.0}, {
w, 1.0}})));
556 model.DeleteVariable(z);
559 IsOkAndHolds(GetParam().supports_incremental_variable_deletions
563 solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
583TEST_P(IncrementalLogicalConstraintTest, InstanceWithSos1AndSos2AndDeletion) {
584 if (!GetParam().supports_sos1) {
585 GTEST_SKIP() << no_sos1_support_message;
587 if (!GetParam().supports_sos2) {
588 GTEST_SKIP() << no_sos2_support_message;
591 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
592 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
593 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
594 model.Maximize(2.0 *
x + 1.5 *
y + 3.0 * z);
595 const Sos1Constraint
c =
model.AddSos1Constraint({
y, z}, {});
596 model.AddSos2Constraint({
x,
y, z}, {});
602 solver->Solve({.parameters = GetParam().parameters}),
605 model.DeleteSos1Constraint(
c);
608 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
612 solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
616TEST_P(SimpleLogicalConstraintTest, CanBuildIndicatorModel) {
624 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
625 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
626 model.AddIndicatorConstraint(
x,
y <= 0.5);
628 if (GetParam().supports_indicator_constraints) {
632 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
633 absl::StatusCode::kUnimplemented),
634 HasSubstr(
"indicator constraints")));
641TEST_P(SimpleLogicalConstraintTest, SolveFailsWithNonBinaryIndicatorVariable) {
642 if (!GetParam().supports_indicator_constraints) {
643 GTEST_SKIP() << no_indicator_support_message;
646 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
647 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
648 model.AddIndicatorConstraint(
x,
y >= 0.5);
651 StatusIs(absl::StatusCode::kInvalidArgument,
652 HasSubstr(
"indicator variable is not binary")));
663TEST_P(SimpleLogicalConstraintTest, SimpleIndicatorInstance) {
664 if (!GetParam().supports_indicator_constraints) {
665 GTEST_SKIP() << no_indicator_support_message;
668 const Variable
x =
model.AddBinaryVariable(
"x");
669 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
671 model.AddIndicatorConstraint(
x,
y >= 0.5);
685TEST_P(SimpleLogicalConstraintTest, ActivationOnZero) {
686 if (!GetParam().supports_indicator_constraints) {
687 GTEST_SKIP() << no_indicator_support_message;
690 const Variable
x =
model.AddBinaryVariable(
"x");
691 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
693 model.AddIndicatorConstraint(
x,
y >= 0.5,
true,
"c");
703TEST_P(SimpleLogicalConstraintTest, IndicatorWithRangedImpliedConstraint) {
704 if (!GetParam().supports_indicator_constraints) {
705 GTEST_SKIP() << no_indicator_support_message;
708 const Variable
x =
model.AddBinaryVariable(
"x");
709 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
710 model.AddIndicatorConstraint(
x, 0.25 <=
y <= 0.75);
713 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
714 absl::StatusCode::kUnimplemented),
715 HasSubstr(
"ranged")));
729TEST_P(SimpleLogicalConstraintTest, UnsetIndicatorVariable) {
730 if (!GetParam().supports_indicator_constraints) {
731 GTEST_SKIP() << no_indicator_support_message;
734 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
735 const Variable indicator =
model.AddBinaryVariable(
"indicator");
737 model.AddIndicatorConstraint(indicator,
x == 0.5);
738 model.DeleteVariable(indicator);
759TEST_P(SimpleLogicalConstraintTest, IndicatorsWithOddButValidBounds) {
760 if (!GetParam().supports_indicator_constraints) {
761 GTEST_SKIP() << no_indicator_support_message;
764 const Variable
x =
model.AddIntegerVariable(0.0, 0.0,
"x");
765 const Variable
y =
model.AddIntegerVariable(1.0, 1.0,
"y");
766 const Variable z =
model.AddIntegerVariable(0.5, 1.0,
"z");
767 const Variable v =
model.AddContinuousVariable(0.0, 1.0,
"v");
768 const Variable
w =
model.AddContinuousVariable(0.0, 1.0,
"w");
770 model.AddIndicatorConstraint(
x,
w >= 1.5);
771 model.AddIndicatorConstraint(
y, v <= 0.6);
772 model.AddIndicatorConstraint(z,
w <= 0.4);
776 1.0, {{
x, 0.0}, {
y, 1.0}, {z, 1.0}, {v, 0.6}, {
w, 0.4}})));
793TEST_P(IncrementalLogicalConstraintTest, LinearToIndicatorUpdate) {
797 const Variable
x = GetParam().supports_integer_variables
798 ?
model.AddBinaryVariable(
"x")
799 :
model.AddContinuousVariable(0.0, 1.0,
"x");
800 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
806 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
809 model.AddIndicatorConstraint(
x,
y <= 0.5);
811 if (!GetParam().supports_indicator_constraints) {
820 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
821 absl::StatusCode::kUnimplemented),
822 AllOf(HasSubstr(
"indicator constraint"),
824 Not(HasSubstr(
"update failed")),
826 HasSubstr(
"solver re-creation failed"))));
830 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
833 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
854TEST_P(IncrementalLogicalConstraintTest, UpdateDeletesIndicatorConstraint) {
855 if (!GetParam().supports_indicator_constraints) {
856 GTEST_SKIP() << no_indicator_support_message;
859 const Variable
x =
model.AddBinaryVariable(
"x");
860 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
862 const IndicatorConstraint
c =
model.AddIndicatorConstraint(
x,
y <= 0.5);
866 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
869 model.DeleteIndicatorConstraint(
c);
872 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
875 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
896TEST_P(IncrementalLogicalConstraintTest,
897 UpdateDeletesIndicatorConstraintWithUnsetIndicatorVariable) {
898 if (!GetParam().supports_indicator_constraints) {
899 GTEST_SKIP() << no_indicator_support_message;
902 const Variable
x =
model.AddContinuousVariable(0.0, 1.0,
"x");
903 const Variable indicator =
model.AddBinaryVariable(
"indicator");
905 const IndicatorConstraint
c =
906 model.AddIndicatorConstraint(indicator,
x <= 0.5);
907 model.DeleteVariable(indicator);
912 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
915 model.DeleteIndicatorConstraint(
c);
918 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
921 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
941TEST_P(IncrementalLogicalConstraintTest, UpdateDeletesIndicatorVariable) {
942 if (!GetParam().supports_indicator_constraints) {
943 GTEST_SKIP() << no_indicator_support_message;
946 const Variable
x =
model.AddBinaryVariable(
"x");
947 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
949 model.AddIndicatorConstraint(
x,
y <= 0.5);
954 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
960 IsOkAndHolds(GetParam().supports_deleting_indicator_variables
963 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
987TEST_P(IncrementalLogicalConstraintTest,
988 UpdateDeletesVariableInImpliedExpression) {
989 if (!GetParam().supports_indicator_constraints) {
990 GTEST_SKIP() << no_indicator_support_message;
993 const Variable
x =
model.AddBinaryVariable(
"x");
994 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
995 const Variable z =
model.AddContinuousVariable(0.0, 1.0,
"z");
996 model.Maximize(
x + 2.0 *
y + z);
997 model.AddIndicatorConstraint(
x,
y <= 0.5);
998 model.AddIndicatorConstraint(
x, z <= 0.5);
1004 solver->Solve({.parameters = GetParam().parameters}),
1010 IsOkAndHolds(GetParam().supports_incremental_variable_deletions
1013 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
1020TEST_P(IncrementalLogicalConstraintTest,
1021 UpdateMakesIndicatorVariableTypeInvalid) {
1022 if (!GetParam().supports_indicator_constraints) {
1023 GTEST_SKIP() << no_indicator_support_message;
1026 const Variable
x =
model.AddBinaryVariable(
"x");
1027 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
1028 model.AddIndicatorConstraint(
x,
y <= 0.5);
1033 ASSERT_OK(solver->Solve({.parameters = GetParam().parameters}));
1038 IsOkAndHolds(GetParam().supports_updating_binary_variables
1041 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
1042 StatusIs(absl::StatusCode::kInvalidArgument,
1043 HasSubstr(
"indicator variable is not binary")));
1068TEST_P(IncrementalLogicalConstraintTest, UpdateChangesIndicatorVariableBound) {
1069 if (!GetParam().supports_indicator_constraints) {
1070 GTEST_SKIP() << no_indicator_support_message;
1073 const Variable
x =
model.AddIntegerVariable(0.0, 1.0,
"x");
1074 const Variable
y =
model.AddIntegerVariable(0.0, 1.0,
"y");
1075 const Variable u =
model.AddContinuousVariable(0.0, 1.0,
"u");
1076 const Variable v =
model.AddContinuousVariable(0.0, 1.0,
"z");
1077 model.Maximize(u + v);
1078 model.AddLinearConstraint(
x +
y == 1.0);
1079 model.AddIndicatorConstraint(
x, u <= 0.6);
1080 model.AddIndicatorConstraint(
y, v <= 0.4);
1085 EXPECT_THAT(solver->Solve({.parameters = GetParam().parameters}),
1087 1.6, {{
x, 1.0}, {
y, 0.0}, {u, 0.6}, {v, 1.0}})));
1089 model.set_lower_bound(
x, 0.0);
1090 model.set_upper_bound(
x, 0.0);
1093 IsOkAndHolds(GetParam().supports_updating_binary_variables
1096 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
1098 1.4, {{
x, 0.0}, {
y, 1.0}, {u, 1.0}, {v, 0.4}})));
1100 model.set_lower_bound(
x, 0.5);
1101 model.set_upper_bound(
x, 1.0);
1102 model.set_lower_bound(
y, 0.0);
1103 model.set_upper_bound(
y, 0.5);
1106 IsOkAndHolds(GetParam().supports_updating_binary_variables
1109 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
1111 1.6, {{
x, 1.0}, {
y, 0.0}, {u, 0.6}, {v, 1.0}})));
1113 model.set_lower_bound(
x, 0.0);
1114 model.set_upper_bound(
x, 1.0);
1115 model.set_lower_bound(
y, 1.0);
1116 model.set_upper_bound(
y, 1.0);
1119 IsOkAndHolds(GetParam().supports_updating_binary_variables
1122 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
1124 1.4, {{
x, 0.0}, {
y, 1.0}, {u, 1.0}, {v, 0.4}})));
1131TEST_P(IncrementalLogicalConstraintTest,
1132 UpdateMakesIndicatorVariableBoundsInvalid) {
1133 if (!GetParam().supports_indicator_constraints) {
1134 GTEST_SKIP() << no_indicator_support_message;
1137 const Variable
x =
model.AddIntegerVariable(0.0, 1.0,
"x");
1138 const Variable
y =
model.AddContinuousVariable(0.0, 1.0,
"y");
1139 model.AddIndicatorConstraint(
x,
y <= 0.5);
1144 ASSERT_OK(solver->Solve({.parameters = GetParam().parameters}));
1146 model.set_upper_bound(
x, 2.0);
1149 IsOkAndHolds(GetParam().supports_updating_binary_variables
1152 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
1153 StatusIs(absl::StatusCode::kInvalidArgument,
1154 HasSubstr(
"indicator variable is not binary")));
An object oriented wrapper for quadratic constraints in ModelStorage.
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)
SolverType
The solvers supported by MathOpt.
<=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()))
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
absl::StatusOr< std::unique_ptr< IncrementalSolver > > NewIncrementalSolver(Model *model, SolverType solver_type, SolverInitArguments arguments)
Matcher< UpdateResult > DidUpdate()
Actual UpdateResult.did_update is true.
std::string ProtobufShortDebugString(const P &message)
internal::StatusIsMatcher StatusIs(CodeMatcher code_matcher, MessageMatcher message_matcher)
trees with all degrees equal w the current value of w
#define ASSERT_OK(expression)
#define EXPECT_OK(expression)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
bool supports_indicator_constraints
True if the solver supports indicator constraints.
bool supports_sos2
True if the solver supports SOS2 constraints.
SolverType solver_type
The tested solver.
bool supports_incremental_add_and_deletes
bool supports_deleting_indicator_variables
True if the solver supports updates that delete indicator variables.
bool supports_updating_binary_variables
bool supports_integer_variables
True if the solver supports integer variables.
bool supports_incremental_variable_deletions
True if the solver supports updates that delete (non-indicator) variables.
SolveParameters parameters
LogicalConstraintTestParameters(SolverType solver_type, SolveParameters parameters, bool supports_integer_variables, bool supports_sos1, bool supports_sos2, bool supports_indicator_constraints, bool supports_incremental_add_and_deletes, bool supports_incremental_variable_deletions, bool supports_deleting_indicator_variables, bool supports_updating_binary_variables)
bool supports_sos1
True if the solver supports SOS1 constraints.
SolveParametersProto Proto() const