Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
mip_tests.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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 <cmath>
17#include <initializer_list>
18#include <limits>
19#include <ostream>
20
21#include "absl/log/check.h"
22#include "absl/status/statusor.h"
23#include "gtest/gtest.h"
24#include "ortools/base/gmock.h"
28
29namespace operations_research {
30namespace math_opt {
31namespace {
32
33using ::testing::status::IsOkAndHolds;
34
35constexpr double kTolerance = 1e-5;
36constexpr double kInf = std::numeric_limits<double>::infinity();
37
38} // namespace
39
44
45std::ostream& operator<<(std::ostream& out,
46 const SimpleMipTestParameters& params) {
47 out << "{ solver_type: " << params.solver_type
48 << ", report_unboundness_correctly: "
49 << (params.report_unboundness_correctly ? "true" : "false") << "}";
50 return out;
51}
52
53// max 3.0 *x + 2.0 * y + 0.1
54// s.t. 0 <= x + y <= 1.5 (c)
55// 0 <= x <= 1
56// y in {0, 1, 2}
57//
58// Optimal solution is (0.5, 1.0), objective value 3.6
60 : model_("incremental_solve_test"),
61 x_(model_.AddContinuousVariable(0.0, 1.0, "x")),
62 y_(model_.AddIntegerVariable(0.0, 2.0, "y")),
63 c_(model_.AddLinearConstraint(0 <= x_ + y_ <= 1.5, "c")) {
64 model_.Maximize(3.0 * x_ + 2.0 * y_ + 0.1);
65 solver_ = NewIncrementalSolver(&model_, TestedSolver()).value();
66 const SolveResult first_solve = solver_->Solve().value();
67 CHECK(first_solve.has_primal_feasible_solution());
68 CHECK_LE(std::abs(first_solve.objective_value() - 3.6), kTolerance)
69 << first_solve.objective_value();
70}
71
72namespace {
73
74TEST_P(SimpleMipTest, OneVarMax) {
75 Model model;
76 const Variable x = model.AddVariable(0.0, 4.0, false, "x");
77 model.Maximize(2.0 * x);
78 ASSERT_OK_AND_ASSIGN(const SolveResult result,
79 Solve(model, GetParam().solver_type));
80 ASSERT_THAT(result, IsOptimal(8.0));
81 EXPECT_THAT(result.variable_values(), IsNear({{x, 4.0}}));
82}
83
84TEST_P(SimpleMipTest, OneVarMin) {
85 Model model;
86 const Variable x = model.AddVariable(-2.4, 4.0, false, "x");
87 model.Minimize(2.0 * x);
88 ASSERT_OK_AND_ASSIGN(const SolveResult result,
89 Solve(model, GetParam().solver_type));
90 ASSERT_THAT(result, IsOptimal(-4.8));
91 EXPECT_THAT(result.variable_values(), IsNear({{x, -2.4}}));
92}
93
94TEST_P(SimpleMipTest, OneIntegerVar) {
95 Model model;
96 const Variable x = model.AddVariable(0.0, 4.5, true, "x");
97 model.Maximize(2.0 * x);
98 ASSERT_OK_AND_ASSIGN(const SolveResult result,
99 Solve(model, GetParam().solver_type));
100 ASSERT_THAT(result, IsOptimal(8.0));
101 EXPECT_THAT(result.variable_values(), IsNear({{x, 4.0}}));
102}
103
104TEST_P(SimpleMipTest, SimpleLinearConstraint) {
105 Model model;
106 const Variable x = model.AddBinaryVariable("x");
107 const Variable y = model.AddBinaryVariable("y");
108 model.Maximize(2.0 * x + y);
109 model.AddLinearConstraint(0.0 <= x + y <= 1.5, "c");
110 ASSERT_OK_AND_ASSIGN(const SolveResult result,
111 Solve(model, GetParam().solver_type));
112 ASSERT_THAT(result, IsOptimal(2.0));
113 EXPECT_THAT(result.variable_values(), IsNear({{x, 1}, {y, 0}}));
114}
115
116TEST_P(SimpleMipTest, Unbounded) {
117 Model model;
118 const Variable x = model.AddVariable(0.0, kInf, true, "x");
119 model.Maximize(2.0 * x);
120 ASSERT_OK_AND_ASSIGN(const SolveResult result,
121 Solve(model, GetParam().solver_type));
122 if (GetParam().report_unboundness_correctly) {
123 ASSERT_THAT(result, TerminatesWithOneOf(
124 {TerminationReason::kUnbounded,
125 TerminationReason::kInfeasibleOrUnbounded}));
126 } else {
127 ASSERT_THAT(result, TerminatesWith(TerminationReason::kOtherError));
128 }
129}
130
131TEST_P(SimpleMipTest, Infeasible) {
132 Model model;
133 const Variable x = model.AddVariable(0.0, 3.0, true, "x");
134 model.Maximize(2.0 * x);
135 model.AddLinearConstraint(x >= 4.0);
136 ASSERT_OK_AND_ASSIGN(const SolveResult result,
137 Solve(model, GetParam().solver_type));
138 ASSERT_THAT(result, TerminatesWith(TerminationReason::kInfeasible));
139}
140
141TEST_P(SimpleMipTest, FractionalBoundsContainNoInteger) {
142 if (GetParam().solver_type == SolverType::kGurobi) {
143 // TODO(b/272298816): Gurobi bindings are broken here.
144 GTEST_SKIP() << "TODO(b/272298816): Gurobi bindings are broken here.";
145 }
146 if (GetParam().solver_type == SolverType::kXpress) {
147 // Xpress rounds bounds of integer variables on input, so the bounds
148 // specified here result in [1,0]. Xpress also checks that bounds are
149 // not contradicting, so it rejects creation of such a variable.
150 GTEST_SKIP() << "Xpress does not support contradictory bounds.";
151 }
152 Model model;
153 const Variable x = model.AddIntegerVariable(0.5, 0.6, "x");
154 model.Maximize(x);
155 EXPECT_THAT(Solve(model, GetParam().solver_type),
156 IsOkAndHolds(TerminatesWith(TerminationReason::kInfeasible)));
157}
158
159TEST_P(IncrementalMipTest, EmptyUpdate) {
160 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
161 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
162 ASSERT_THAT(result, IsOptimal(3.6));
163 EXPECT_THAT(result.variable_values(), IsNear({{x_, 0.5}, {y_, 1.0}}));
164}
165
166TEST_P(IncrementalMipTest, MakeContinuous) {
167 model_.set_continuous(y_);
168 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
169 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
170 ASSERT_THAT(result, IsOptimal(4.1));
171 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.5}}));
172}
173
174// TODO(b/202494808): Enable this test once this bug is resolved. Today Gurobi
175// and Scip both fail in that case. See the bug for details why.
176TEST_P(IncrementalMipTest, DISABLED_MakeContinuousWithNonIntegralBounds) {
177 // With Gurobi we can only have one solver at a time.
178 solver_.reset();
179
180 Model model("bounds");
181 const Variable x = model.AddIntegerVariable(0.5, 1.5, "x");
182 model.Maximize(x);
183
184 ASSERT_OK_AND_ASSIGN(const auto solver,
185 NewIncrementalSolver(&model, TestedSolver()));
186 ASSERT_THAT(solver->Solve(), IsOkAndHolds(IsOptimal(1.0)));
187
188 // Switching to continuous should use the fractional bound. For solvers that
189 // mandates integral bounds for integer variables this may require updating
190 // the bound to its actual fractional value.
191 model.set_continuous(x);
192 ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));
193 ASSERT_THAT(solver->SolveWithoutUpdate(), IsOkAndHolds(IsOptimal(1.5)));
194
195 model.Minimize(x);
196 ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));
197 ASSERT_THAT(solver->SolveWithoutUpdate(), IsOkAndHolds(IsOptimal(0.5)));
198}
199
200TEST_P(IncrementalMipTest, MakeIntegralWithNonIntegralBounds) {
201 // With Gurobi we can only have one solver at a time.
202 solver_.reset();
203
204 Model model("bounds");
205 const Variable x = model.AddContinuousVariable(0.5, 1.5, "x");
206 model.Maximize(x);
207
208 ASSERT_OK_AND_ASSIGN(const auto solver,
209 NewIncrementalSolver(&model, TestedSolver()));
210 ASSERT_THAT(solver->Solve(), IsOkAndHolds(IsOptimal(1.5)));
211
212 model.set_integer(x);
213 ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));
214 ASSERT_THAT(solver->SolveWithoutUpdate(), IsOkAndHolds(IsOptimal(1.0)));
215
216 model.Minimize(x);
217 ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));
218 ASSERT_THAT(solver->SolveWithoutUpdate(), IsOkAndHolds(IsOptimal(1.0)));
219}
220
222 model_.set_integer(x_);
223 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
224 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
225 ASSERT_THAT(result, IsOptimal(3.1));
226 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.0}}));
227}
228
230 model_.set_minimize();
231 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
232 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
233 ASSERT_THAT(result, IsOptimal(0.1));
234 EXPECT_THAT(result.variable_values(), IsNear({{x_, 0.0}, {y_, 0.0}}));
235}
236
238 model_.set_objective_offset(0.2);
239 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
240 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
241 ASSERT_THAT(result, IsOptimal(3.7));
242 EXPECT_THAT(result.variable_values(), IsNear({{x_, 0.5}, {y_, 1.0}}));
243}
244
245TEST_P(IncrementalMipTest, LinearObjCoef) {
246 model_.set_objective_coefficient(x_, 5.0);
247 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
248 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
249 ASSERT_THAT(result, IsOptimal(5.1));
250 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.0}}));
251}
252
254 model_.set_lower_bound(x_, 0.75);
255 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
256 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
257 ASSERT_THAT(result, IsOptimal(3.1));
258 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.0}}));
259}
260
262 model_.set_upper_bound(x_, 2.0);
263 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
264 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
265 ASSERT_THAT(result, IsOptimal(4.6));
266 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.5}, {y_, 0.0}}));
267}
268
269TEST_P(IncrementalMipTest, LinearConstraintLb) {
270 model_.set_lower_bound(c_, 1.0);
271 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
272 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
273 ASSERT_THAT(result, IsOptimal(3.6));
274 EXPECT_THAT(result.variable_values(), IsNear({{x_, 0.5}, {y_, 1.0}}));
275 // For this change, feasibility is preserved, so the solver should do no extra
276 // work (SCIP enumerates one node, though).
277 if (TestedSolver() != SolverType::kGscip) {
278 EXPECT_EQ(result.solve_stats.node_count, 0);
279 }
280 EXPECT_EQ(result.solve_stats.simplex_iterations, 0);
281 EXPECT_EQ(result.solve_stats.barrier_iterations, 0);
282}
283
284TEST_P(IncrementalMipTest, LinearConstraintUb) {
285 model_.set_upper_bound(c_, 1.0);
286 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
287 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
288 ASSERT_THAT(result, IsOptimal(3.1));
289 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.0}}));
290}
291
292TEST_P(IncrementalMipTest, LinearConstraintCoefficient) {
293 model_.set_coefficient(c_, x_, 0.5);
294 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
295 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
296 ASSERT_THAT(result, IsOptimal(5.1));
297 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 1.0}}));
298}
299
301 Variable z = model_.AddVariable(0.0, 1.0, true, "z");
302 model_.set_objective_coefficient(z, 10.0);
303 model_.set_coefficient(c_, z, 1.0);
304 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
305 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
306 ASSERT_THAT(result, IsOptimal(11.6));
308 IsNear({{x_, 0.5}, {y_, 0.0}, {z, 1.0}}));
309}
310
311TEST_P(IncrementalMipTest, AddLinearConstraint) {
312 model_.AddLinearConstraint(0.0 <= x_ + 2.0 * y_ <= 2.0, "d");
313 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
314 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
315 ASSERT_THAT(result, IsOptimal(3.1));
316 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.0}}));
317}
318
319TEST_P(IncrementalMipTest, DeleteVariable) {
320 model_.DeleteVariable(x_);
321 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
322 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
323 ASSERT_THAT(result, IsOptimal(2.1));
324 EXPECT_THAT(result.variable_values(), IsNear({{y_, 1.0}}));
325}
326
327TEST_P(IncrementalMipTest, DeleteLinearConstraint) {
328 model_.DeleteLinearConstraint(c_);
329 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
330 ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());
331 ASSERT_THAT(result, IsOptimal(7.1));
332 EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 2.0}}));
333}
334
335TEST_P(IncrementalMipTest, ChangeBoundsWithTemporaryInversion) {
336 model_.set_lower_bound(x_, 3.0);
337 // At this point x_ lower bound is 3.0 and upper bound is 1.0.
338 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
339
340 model_.set_upper_bound(x_, 5.0);
341 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
342 // At this point x_ upper bound is 5.0 and so is greater than the new lower
343 // bound.
344
345 // To make the problem feasible we update the bound of the constraint that
346 // contains x_; we take this opportunity to also test inverting bounds of
347 // constraints.
348 model_.set_lower_bound(c_, 4.0);
349 // At this point c_ lower bound is 4.0 and upper bound is 1.5.
350 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
351
352 // We restore valid bounds by setting c_ upper bound to 5.5.
353 model_.set_upper_bound(c_, 5.5);
354 ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));
355
356 EXPECT_THAT(solver_->SolveWithoutUpdate(),
357 IsOkAndHolds(IsOptimal(3 * 4.5 + 2 * 1 + 0.1)));
358}
359
360} // namespace
361} // namespace math_opt
362} // namespace operations_research
Definition model.h:345
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined, bool set_is_fixed=false)
Definition model.cc:1020
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
Definition model.cc:1074
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Definition model.cc:1067
void Maximize(double objective)
Definition model.h:1409
void Minimize(double objective)
Definition model.h:1425
void set_integer(Variable variable)
Definition model.h:1049
Variable AddContinuousVariable(double lower_bound, double upper_bound, absl::string_view name="")
Definition model.h:979
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
Definition gmock.h:53
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)
Matcher< SolveResult > TerminatesWithOneOf(const std::vector< TerminationReason > &allowed)
Definition matchers.cc:616
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Definition solve.cc:62
<=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)
Definition solve.cc:82
Matcher< SolveResult > TerminatesWith(const TerminationReason expected)
Definition matchers.cc:622
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
Matcher< VariableMap< double > > IsNear(VariableMap< double > expected, const double tolerance)
Definition matchers.cc:222
Matcher< UpdateResult > DidUpdate()
Definition matchers.cc:1028
Matcher< SolveResult > IsOptimal(const std::optional< double > expected_primal_objective, const double tolerance)
Definition matchers.cc:763
OR-Tools root namespace.
SimpleMipTestParameters(SolverType solver_type, bool report_unboundness_correctly=true)
Definition mip_tests.cc:40
const VariableMap< double > & variable_values() const