Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
infeasible_subsystem_tests.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <limits>
17#include <ostream>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/status/status.h"
23#include "absl/status/statusor.h"
24#include "absl/strings/str_join.h"
25#include "absl/time/time.h"
26#include "gtest/gtest.h"
27#include "ortools/base/gmock.h"
31#include "ortools/math_opt/infeasible_subsystem.pb.h"
33
35
36using ::testing::AnyOf;
37using ::testing::HasSubstr;
38using ::testing::status::IsOkAndHolds;
39using ::testing::status::StatusIs;
40
41constexpr double kInf = std::numeric_limits<double>::infinity();
42
43std::ostream& operator<<(std::ostream& ostr,
44 const InfeasibleSubsystemSupport& support_menu) {
45 ostr << "{ infeasible_subsystem_support: "
46 << (support_menu.supports_infeasible_subsystems ? "true" : "false")
47 << " }";
48 return ostr;
49}
50
51std::ostream& operator<<(std::ostream& ostr,
53 ostr << "{ solver_type: " << EnumToString(params.solver_type);
54 ostr << ", support_menu: " << params.support_menu << " }";
55 return ostr;
56}
57
58TEST_P(InfeasibleSubsystemTest, CanComputeInfeasibleSubsystem) {
60 if (GetParam().support_menu.supports_infeasible_subsystems) {
61 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
63 } else {
64 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
65 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
66 absl::StatusCode::kUnimplemented),
67 HasSubstr("infeasible subsystem")));
68 }
69}
70
71// The model is:
72// min 0
73// s.t. 1 ≤ x ≤ 0 (variable bounds)
74//
75// The entire model is an IIS.
76TEST_P(InfeasibleSubsystemTest, InvertedVariableBounds) {
77 if (!GetParam().support_menu.supports_infeasible_subsystems) {
78 return;
79 }
81 const Variable x = model.AddContinuousVariable(1.0, 0.0);
82 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
84 /*expected_is_minimal=*/true,
85 ModelSubset{.variable_bounds = {
86 {x, ModelSubset::Bounds{.lower = true,
87 .upper = true}}}})));
88}
89
90// The model is:
91// min 0
92// s.t. 0.2 ≤ x ≤ 0.8 (variable bounds)
93// x is integer
94//
95// The entire model is an IIS.
96TEST_P(InfeasibleSubsystemTest, IntegerVariableWithInfeasibleBounds) {
97 if (!GetParam().support_menu.supports_infeasible_subsystems) {
98 return;
99 }
100 Model model;
101 const Variable x = model.AddIntegerVariable(0.2, 0.8);
103 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
105 /*expected_is_minimal=*/true,
107 .variable_bounds = {{x, ModelSubset::Bounds{.lower = true,
108 .upper = true}}},
109 .variable_integrality = {x}})));
110}
111
112// The model is:
113// min 0
114// s.t. -∞ ≤ 1 ≤ 0
115//
116// An IIS is:
117// 1 ≤ 0
118TEST_P(InfeasibleSubsystemTest, InconsistentLessThanLinearConstraint) {
119 if (!GetParam().support_menu.supports_infeasible_subsystems) {
120 return;
121 }
122 Model model;
123 const LinearConstraint c =
124 model.AddLinearConstraint(BoundedLinearExpression(1.0, -kInf, 0.0));
125 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
127 /*expected_is_minimal=*/true,
128 ModelSubset{.linear_constraints = {
129 {c, ModelSubset::Bounds{.lower = false,
130 .upper = true}}}})));
131}
132
133// The model is:
134// min 0
135// s.t. 1 ≤ 0 ≤ ∞
136//
137// An IIS is:
138// 1 ≤ 0
139TEST_P(InfeasibleSubsystemTest, InconsistentGreaterThanLinearConstraint) {
140 if (!GetParam().support_menu.supports_infeasible_subsystems) {
141 return;
142 }
143 Model model;
144 const LinearConstraint c =
145 model.AddLinearConstraint(BoundedLinearExpression(0.0, 1.0, kInf));
146 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
148 /*expected_is_minimal=*/true,
149 ModelSubset{.linear_constraints = {
150 {c, ModelSubset::Bounds{.lower = true,
151 .upper = false}}}})));
152}
153
154// The model is:
155// min 0
156// s.t. 1 == 0
157//
158// The entire model is an IIS.
159TEST_P(InfeasibleSubsystemTest, InconsistentEqualityLinearConstraint) {
160 if (!GetParam().support_menu.supports_infeasible_subsystems) {
161 return;
162 }
163 Model model;
164 const LinearConstraint c =
165 model.AddLinearConstraint(BoundedLinearExpression(1.0, 0.0, 0.0));
166 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
168 /*expected_is_minimal=*/true,
169 ModelSubset{.linear_constraints = {
170 {c, ModelSubset::Bounds{.lower = true,
171 .upper = true}}}})));
172}
173
174// The model is:
175// min 0
176// s.t. 0 ≤ 2 ≤ 1
177//
178// The entire model is an IIS.
179TEST_P(InfeasibleSubsystemTest, InconsistentRangedLinearConstraint) {
180 if (!GetParam().support_menu.supports_infeasible_subsystems) {
181 return;
182 }
183 Model model;
184 const LinearConstraint c =
185 model.AddLinearConstraint(BoundedLinearExpression(2.0, 0.0, 1.0));
186 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
188 /*expected_is_minimal=*/true,
189 ModelSubset{.linear_constraints = {
190 {c, ModelSubset::Bounds{.lower = true,
191 .upper = true}}}})));
192}
193
194// The model is:
195// min 0
196// s.t. x ≥ 1 (linear constraint)
197// -∞ ≤ x ≤ 0 (variable bounds)
198//
199// The entire model is an IIS.
201 if (!GetParam().support_menu.supports_infeasible_subsystems) {
202 return;
203 }
204 Model model;
205 const Variable x = model.AddContinuousVariable(-kInf, 0.0);
206 const LinearConstraint c = model.AddLinearConstraint(x >= 1.0);
208 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
210 /*expected_is_minimal=*/true,
212 .variable_bounds = {{x, ModelSubset::Bounds{.lower = false,
213 .upper = true}}},
214 .linear_constraints = {
215 {c, ModelSubset::Bounds{.lower = true, .upper = false}}}})));
216}
217
218// The model is:
219// min 0
220// s.t. -∞ ≤ 1 ≤ 0 (quadratic constraint)
221//
222// An IIS is:
223// 1 ≤ 0
224TEST_P(InfeasibleSubsystemTest, InconsistentLessThanQuadraticConstraint) {
225 if (!GetParam().support_menu.supports_infeasible_subsystems) {
226 return;
227 }
228 Model model;
229 const QuadraticConstraint c =
230 model.AddQuadraticConstraint(BoundedQuadraticExpression(1.0, -kInf, 0.0));
231 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
233 /*expected_is_minimal=*/true,
234 ModelSubset{.quadratic_constraints = {
235 {c, ModelSubset::Bounds{.lower = false,
236 .upper = true}}}})));
237}
238
239// The model is:
240// min 0
241// s.t. 1 ≤ 0 ≤ ∞ (quadratic constraint)
242//
243// An IIS is:
244// 1 ≤ 0
245TEST_P(InfeasibleSubsystemTest, InconsistentGreaterThanQuadraticConstraint) {
246 if (!GetParam().support_menu.supports_infeasible_subsystems) {
247 return;
248 }
249 Model model;
250 const QuadraticConstraint c =
251 model.AddQuadraticConstraint(BoundedQuadraticExpression(0.0, 1.0, kInf));
252 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
254 /*expected_is_minimal=*/true,
255 ModelSubset{.quadratic_constraints = {
256 {c, ModelSubset::Bounds{.lower = true,
257 .upper = false}}}})));
258}
259
260// The model is:
261// min 0
262// s.t. 1 == 0 (quadratic constraint)
263//
264// The entire model is an IIS.
265TEST_P(InfeasibleSubsystemTest, InconsistentEqualityQuadraticConstraint) {
266 if (!GetParam().support_menu.supports_infeasible_subsystems) {
267 return;
268 }
269 Model model;
270 const QuadraticConstraint c =
271 model.AddQuadraticConstraint(BoundedQuadraticExpression(1.0, 0.0, 0.0));
272 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
274 /*expected_is_minimal=*/true,
275 ModelSubset{.quadratic_constraints = {
276 {c, ModelSubset::Bounds{.lower = true,
277 .upper = true}}}})));
278}
279
280// TODO(b/227217735): Test ranged quadratic constraints when supported.
281
282// The model is:
283// min 0
284// s.t. x² ≥ 1
285// -0.5 ≤ x ≤ 0.5 (variable bounds)
286//
287// The entire model is an IIS.
288TEST_P(InfeasibleSubsystemTest,
290 if (!GetParam().support_menu.supports_infeasible_subsystems) {
291 return;
292 }
293 Model model;
294 const Variable x = model.AddContinuousVariable(-0.5, 0.5);
295 const QuadraticConstraint c = model.AddQuadraticConstraint(x * x >= 1.0);
297 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
299 /*expected_is_minimal=*/true,
301 .variable_bounds = {{x, ModelSubset::Bounds{.lower = true,
302 .upper = true}}},
303 .quadratic_constraints = {
304 {c, ModelSubset::Bounds{.lower = true, .upper = false}}}})));
305}
306
307// The model is:
308// min 0
309// s.t. ||{x}||₂ ≤ 1
310// 2 ≤ x ≤ 2 (variable bounds)
311//
312// An IIS is:
313// ||{x}||₂ ≤ 1
314// 2 ≤ x
316 if (!GetParam().support_menu.supports_infeasible_subsystems) {
317 return;
318 }
319 Model model;
320 const Variable x = model.AddContinuousVariable(2.0, 2.0);
322 model.AddSecondOrderConeConstraint({x}, 1.0);
324 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
326 /*expected_is_minimal=*/true,
328 .variable_bounds = {{x, ModelSubset::Bounds{.lower = true,
329 .upper = false}}},
330 .second_order_cone_constraints = {c}})));
331}
332
333// The model is:
334// min 0
335// s.t. ||{2x}||₂ ≤ 1
336// 1 ≤ x ≤ 1 (variable bounds)
337//
338// An IIS is:
339// ||{2x}||₂ ≤ 1
340// 1 ≤ x
341TEST_P(InfeasibleSubsystemTest,
343 if (!GetParam().support_menu.supports_infeasible_subsystems) {
344 return;
345 }
346 Model model;
347 const Variable x = model.AddContinuousVariable(1.0, 1.0);
349 model.AddSecondOrderConeConstraint({2.0 * x}, 1.0);
351 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
353 /*expected_is_minimal=*/true,
355 .variable_bounds = {{x, ModelSubset::Bounds{.lower = true,
356 .upper = false}}},
357 .second_order_cone_constraints = {c}})));
358}
359
360// The model is:
361// min 0
362// s.t. ||{x}||₂ ≤ 2x - 2
363// 1 ≤ x ≤ 1 (variable bounds)
364//
365// An IIS is:
366// ||{x}||₂ ≤ 2x - 2
367// 1 ≤ x
368TEST_P(InfeasibleSubsystemTest,
370 if (!GetParam().support_menu.supports_infeasible_subsystems) {
371 return;
372 }
373 Model model;
374 const Variable x = model.AddContinuousVariable(1.0, 1.0);
376 model.AddSecondOrderConeConstraint({x}, 2.0 * x - 2.0);
378 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
380 /*expected_is_minimal=*/true,
382 .variable_bounds = {{x, ModelSubset::Bounds{.lower = false,
383 .upper = true}}},
384 .second_order_cone_constraints = {c}})));
385}
386
387// The model is:
388// min 0
389// s.t. {x, y} is SOS1
390// 1 ≤ x, y ≤ 1 (variable bounds)
391//
392// An IIS is:
393// {x, y} is SOS1
394// 1 ≤ x, y
395TEST_P(InfeasibleSubsystemTest, InconsistentSos1Constraint) {
396 if (!GetParam().support_menu.supports_infeasible_subsystems) {
397 return;
398 }
399 Model model;
400 const Variable x = model.AddContinuousVariable(1.0, 1.0);
401 const Variable y = model.AddContinuousVariable(1.0, 1.0);
402 const Sos1Constraint c = model.AddSos1Constraint({x, y});
404 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
406 /*expected_is_minimal=*/true,
408 .variable_bounds =
409 {{x, ModelSubset::Bounds{.lower = true, .upper = false}},
410 {y, ModelSubset::Bounds{.lower = true, .upper = false}}},
411 .sos1_constraints = {c}})));
412}
413
414// The model is:
415// min 0
416// s.t. {1, 1} is SOS1
417//
418// The entire problem is an IIS.
419TEST_P(InfeasibleSubsystemTest, InconsistentSos1ConstraintWithExpressions) {
420 if (!GetParam().support_menu.supports_infeasible_subsystems) {
421 return;
422 }
423 Model model;
424 const Sos1Constraint c = model.AddSos1Constraint({1.0, 1.0});
425 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
426 IsOkAndHolds(IsInfeasible(/*expected_is_minimal=*/true,
427 ModelSubset{.sos1_constraints = {c}})));
428}
429
430// The model is:
431// min 0
432// s.t. {x, y, z} is SOS2
433// 1 ≤ x, z ≤ 1 (variable bounds)
434// 0 ≤ y ≤ 1 (variable bounds)
435//
436// An IIS is:
437// {x, y, z} is SOS2
438// 1 ≤ x, z
439// 0 ≤ y
440TEST_P(InfeasibleSubsystemTest, InconsistentSos2Constraint) {
441 if (!GetParam().support_menu.supports_infeasible_subsystems) {
442 return;
443 }
444 Model model;
445 const Variable x = model.AddContinuousVariable(1.0, 1.0);
446 const Variable y = model.AddContinuousVariable(0.0, 1.0);
447 const Variable z = model.AddContinuousVariable(1.0, 1.0);
448 const Sos2Constraint c = model.AddSos2Constraint({x, y, z});
450 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
452 /*expected_is_minimal=*/true,
454 .variable_bounds =
455 {{x, ModelSubset::Bounds{.lower = true, .upper = false}},
456 {z, ModelSubset::Bounds{.lower = true, .upper = false}}},
457 .sos2_constraints = {c}})));
458}
459
460// The model is:
461// min 0
462// s.t. {1, 0, 1} is SOS2
463//
464// The entire model is an IIS.
465TEST_P(InfeasibleSubsystemTest, InconsistentSos2ConstraintWithExpressions) {
466 if (!GetParam().support_menu.supports_infeasible_subsystems) {
467 return;
468 }
469 Model model;
470 const Sos2Constraint c = model.AddSos2Constraint({1.0, 0.0, 1.0});
471 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
472 IsOkAndHolds(IsInfeasible(/*expected_is_minimal=*/true,
473 ModelSubset{.sos2_constraints = {c}})));
474}
475
476// The model is:
477// min 0
478// s.t. x == 1 --> 1 ≤ 0
479// 1 ≤ x ≤ 1 (variable bounds)
480// x is integer
481//
482// An IIS is:
483// x == 1 --> 1 ≤ 0
484// 1 ≤ x
485// x is integer
487 if (!GetParam().support_menu.supports_infeasible_subsystems) {
488 return;
489 }
490 Model model;
491 const Variable x = model.AddIntegerVariable(1.0, 1.0);
492 const IndicatorConstraint c =
493 model.AddIndicatorConstraint(x, BoundedLinearExpression(0.0, 1.0, kInf));
495 ComputeInfeasibleSubsystem(model, GetParam().solver_type),
497 /*expected_is_minimal=*/true,
499 .variable_bounds = {{x, ModelSubset::Bounds{.lower = true,
500 .upper = false}}},
501 .variable_integrality = {x},
502 .indicator_constraints = {c}})));
503}
504
505// The model is:
506// min 0
507// s.t. {null} --> 1 ≤ 0
508//
509// The model is feasible.
511 IndicatorConstraintOkInconsistentImpliedNullIndicator) {
512 if (!GetParam().support_menu.supports_infeasible_subsystems) {
513 return;
514 }
515 Model model;
516 // To get a null indicator variable, we: add a placeholder indicator variable,
517 // add the indicator constraint, and then delete the variable.
518 const Variable x = model.AddIntegerVariable(1.0, 1.0);
519 model.AddIndicatorConstraint(x, BoundedLinearExpression(0.0, 1.0, kInf));
520 model.DeleteVariable(x);
521 EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type),
523}
524
525// The model is
526// 2*x + 2*y + 2*z >= 3
527// x + y <= 1
528// y + z <= 1
529// x + z <= 1
530// x, y, z in {0, 1}
531//
532// The IIS has no variable bounds and all other constraints. In particular, the
533// LP relaxation is feasible.
543
545 : model(),
546 x(model.AddBinaryVariable("x")),
547 y(model.AddBinaryVariable("y")),
548 z(model.AddBinaryVariable("z")),
549 a(model.AddLinearConstraint(2.0 * x + 2.0 * y + 2.0 * z >= 3.0)),
550 b(model.AddLinearConstraint(x + y <= 1.0)),
551 c(model.AddLinearConstraint(y + z <= 1.0)),
552 d(model.AddLinearConstraint(x + z <= 1.0)) {}
553};
554
556 NontrivialInfeasibleIpSolveWithoutLimitsFindsIIS) {
557 if (!GetParam().support_menu.supports_infeasible_subsystems) {
558 return;
559 }
560 const NontrivialInfeasibleIp ip;
561 const ModelSubset expected = {
562 .variable_integrality = {ip.x, ip.y, ip.z},
563 .linear_constraints = {{ip.a, {.lower = true}},
564 {ip.b, {.upper = true}},
565 {ip.c, {.upper = true}},
566 {ip.d, {.upper = true}}}};
568 ComputeInfeasibleSubsystem(ip.model, GetParam().solver_type),
569 IsOkAndHolds(IsInfeasible(/*expected_is_minimal=*/true, expected)));
570}
571
573 NontrivialInfeasibleIpSolveTimeLimitZeroIsUndetermined) {
574 if (!GetParam().support_menu.supports_infeasible_subsystems) {
575 return;
576 }
577 const NontrivialInfeasibleIp ip;
579 ip.model, GetParam().solver_type,
580 {.parameters = {.time_limit = absl::Seconds(0)}}),
582}
583
585 NontrivialInfeasibleIpSolveInterruptedBeforeStartIsUndetermined) {
586 if (!GetParam().support_menu.supports_infeasible_subsystems) {
587 return;
588 }
589 const NontrivialInfeasibleIp ip;
590 SolveInterrupter interrupter;
591 interrupter.Interrupt();
592 EXPECT_THAT(ComputeInfeasibleSubsystem(ip.model, GetParam().solver_type,
593 {.interrupter = &interrupter}),
595}
596
598 NontrivialInfeasibleIpSolveWithMessageCallbackIsInvoked) {
599 if (!GetParam().support_menu.supports_infeasible_subsystems) {
600 return;
601 }
602 const NontrivialInfeasibleIp ip;
603 std::vector<std::string> logs;
606 ip.model, GetParam().solver_type,
607 {.message_callback = VectorMessageCallback(&logs)}));
609 EXPECT_THAT(absl::StrJoin(logs, "\n"), HasSubstr("IIS computed"));
610}
611
612#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
613TEST_P(InfeasibleSubsystemTest, NoStdoutOutputByDefault) {
614 if (!GetParam().support_menu.supports_infeasible_subsystems) {
615 return;
616 }
617 const NontrivialInfeasibleIp ip;
618 absl::StatusOr<ComputeInfeasibleSubsystemResult> result;
619 // DO NOT ASSERT until after calling StopCaptureAndReturnContents.
620 ScopedStdStreamCapture stdout_capture(CapturedStream::kStdout);
621 result = ComputeInfeasibleSubsystem(ip.model, GetParam().solver_type);
622 const std::string standard_output =
623 std::move(stdout_capture).StopCaptureAndReturnContents();
624 ASSERT_OK(result);
625 EXPECT_THAT(standard_output,
627 /*is_gurobi=*/GetParam().solver_type == SolverType::kGurobi));
628 EXPECT_EQ(result->feasibility, FeasibilityStatus::kInfeasible);
629}
630
631TEST_P(InfeasibleSubsystemTest, EnableOutputPrintsToStdOut) {
632 if (!GetParam().support_menu.supports_infeasible_subsystems) {
633 return;
634 }
635 const NontrivialInfeasibleIp ip;
636 const SolveParameters params = {.enable_output = true};
637
638 absl::StatusOr<ComputeInfeasibleSubsystemResult> result;
639 // DO NOT ASSERT until after calling StopCaptureAndReturnContents.
640 ScopedStdStreamCapture stdout_capture(CapturedStream::kStdout);
641 result = ComputeInfeasibleSubsystem(ip.model, GetParam().solver_type,
642 {.parameters = params});
643 const std::string standard_output =
644 std::move(stdout_capture).StopCaptureAndReturnContents();
645 ASSERT_OK(result);
646 EXPECT_THAT(standard_output, HasSubstr("IIS computed"));
647 EXPECT_EQ(result->feasibility, FeasibilityStatus::kInfeasible);
648}
649
650TEST_P(InfeasibleSubsystemTest, EnableOutputIgnoredWithMessageCallback) {
651 if (!GetParam().support_menu.supports_infeasible_subsystems) {
652 return;
653 }
654 const NontrivialInfeasibleIp ip;
655 const SolveParameters params = {.enable_output = true};
656 std::vector<std::string> logs;
657
658 absl::StatusOr<ComputeInfeasibleSubsystemResult> result;
659 // DO NOT ASSERT until after calling StopCaptureAndReturnContents.
660 ScopedStdStreamCapture stdout_capture(CapturedStream::kStdout);
662 ip.model, GetParam().solver_type,
663 {.parameters = params, .message_callback = VectorMessageCallback(&logs)});
664 const std::string standard_output =
665 std::move(stdout_capture).StopCaptureAndReturnContents();
666 ASSERT_OK(result);
667 EXPECT_THAT(standard_output,
669 /*is_gurobi=*/GetParam().solver_type == SolverType::kGurobi));
670 EXPECT_EQ(result->feasibility, FeasibilityStatus::kInfeasible);
671 EXPECT_THAT(absl::StrJoin(logs, "\n"), HasSubstr("IIS computed"));
672}
673
674#endif // OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
675
676} // namespace operations_research::math_opt
IntegerValue y
GRBmodel * model
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
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.";} 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)))
testing::Matcher< ComputeInfeasibleSubsystemResult > IsInfeasible(const std::optional< bool > expected_is_minimal, const std::optional< ModelSubset > expected_infeasible_subsystem)
Definition matchers.cc:994
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
absl::StatusOr< ComputeInfeasibleSubsystemResult > ComputeInfeasibleSubsystem(const Model &model, const SolverType solver_type, const ComputeInfeasibleSubsystemArguments &compute_args, const SolverInitArguments &init_args)
Definition solve.cc:72
MessageCallback VectorMessageCallback(std::vector< std::string > *sink)
absl::string_view EnumToString(E value)
Definition enums.h:289
testing::Matcher< ComputeInfeasibleSubsystemResult > IsUndetermined()
Definition matchers.cc:983
@ kInfeasible
Solver claims the problem is infeasible.
testing::Matcher< std::string > EmptyOrGurobiLicenseWarningIfGurobi(const bool is_gurobi)
#define ASSERT_OK(expression)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
A LinearExpression with upper and lower bounds.
A QuadraticExpression with upper and lower bounds.
FeasibilityStatus feasibility
The primal feasibility status of the model, as determined by the solver.