Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
qc_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 <memory>
18#include <ostream>
19#include <utility>
20
21#include "absl/status/status.h"
22#include "absl/strings/string_view.h"
23#include "gtest/gtest.h"
24#include "ortools/base/gmock.h"
28
30
44
45std::ostream& operator<<(std::ostream& out, const QcTestParameters& params) {
46 out << "{ solver_type: " << params.solver_type
47 << ", parameters: " << ProtobufShortDebugString(params.parameters.Proto())
48 << ", supports_qc: " << (params.supports_qc ? "true" : "false")
49 << ", supports_incremental_add_and_deletes: "
50 << (params.supports_incremental_add_and_deletes ? "true" : "false")
51 << ", supports_incremental_variable_deletions: "
52 << (params.supports_incremental_variable_deletions ? "true" : "false")
53 << ", use_integer_variables: "
54 << (params.use_integer_variables ? "true" : "false") << " }";
55 return out;
56}
57
58namespace {
59
60using ::testing::AnyOf;
61using ::testing::HasSubstr;
62using ::testing::status::IsOkAndHolds;
63using ::testing::status::StatusIs;
64
65constexpr double kTolerance = 1.0e-4;
66constexpr absl::string_view no_qc_support_message =
67 "This test is disabled as the solver does not support quadratic "
68 "constraints";
69
70// Models the following problem:
71// min_x x + 5
72// s.t. x^2 - x <= 1
73// -1 <= x <= 1
74//
75// along with, if use_integer_variables = true, integrality on x.
76//
77// If use_integer_variables = false, the unique optimal solution is attained at
78// x = (1 - sqrt(5)) / 2 with objective value (1 - sqrt(5)) / 2 + 5. Otherwise,
79// the unique optimal solution is x = 0 with objective value 5.
80struct UnivariateQcProblem {
81 explicit UnivariateQcProblem(bool use_integer_variables)
82 : model(), x(model.AddVariable(-1.0, 1.0, use_integer_variables, "x")) {
83 model.AddQuadraticConstraint(x * x - x <= 1.0);
84 model.Minimize(x + 5.0);
85 }
86
87 Model model;
88 const Variable x;
89};
90
91TEST_P(SimpleQcTest, CanBuildQcModel) {
92 UnivariateQcProblem qc_problem(GetParam().use_integer_variables);
93 if (GetParam().supports_qc) {
95 NewIncrementalSolver(&qc_problem.model, GetParam().solver_type, {}));
96 } else {
98 NewIncrementalSolver(&qc_problem.model, GetParam().solver_type, {}),
99 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
100 absl::StatusCode::kUnimplemented),
101 HasSubstr("quadratic constraints")));
102 }
103}
104
105TEST_P(SimpleQcTest, SolveSimpleQc) {
106 if (!GetParam().supports_qc) {
107 GTEST_SKIP() << no_qc_support_message;
108 }
109 const UnivariateQcProblem qc_problem(GetParam().use_integer_variables);
110 const double x_expected =
111 GetParam().use_integer_variables ? 0.0 : -0.618033988748785;
112 EXPECT_THAT(SimpleSolve(qc_problem.model),
114 5.0 + x_expected, {{qc_problem.x, x_expected}})));
115}
116
117// Models the following problem:
118// min_{x,y} y
119// s.t. (x - 1)^2 + (y - 1)^2 + xy == x^2 + xy + y^2 - 2x - 2y + 2 <= 1
120// x <= y
121// 0 <= x <= 0.5
122// 0 <= y <= 1
123//
124// along with, if use_integer_variables = true, integrality on x and y.
125//
126// If use_integer_variables = false, the unique optimal solution is attained at
127// (x, y) = (1/3, 1/3) with objective value 1/3. Otherwise, the unique optimal
128// solution is (x, y) = (0, 1) with objective value 1.
129struct HalfEllipseProblem {
130 explicit HalfEllipseProblem(bool use_integer_variables)
131 : model(),
132 x(model.AddVariable(0.0, 0.5, use_integer_variables, "x")),
133 y(model.AddVariable(0.0, 1.0, use_integer_variables, "y")),
134 q(model.AddQuadraticConstraint(x * x + x * y + y * y - 2 * x - 2 * y <=
135 -1)) {
136 model.Minimize(y);
137 model.AddLinearConstraint(x <= y);
138 }
139
140 Model model;
141 const Variable x;
142 const Variable y;
143 const QuadraticConstraint q;
144};
145
146TEST_P(SimpleQcTest, SolveHalfEllipseQc) {
147 if (!GetParam().supports_qc) {
148 GTEST_SKIP() << no_qc_support_message;
149 }
150 const HalfEllipseProblem qc_problem(GetParam().use_integer_variables);
151 if (GetParam().use_integer_variables) {
152 EXPECT_THAT(SimpleSolve(qc_problem.model),
154 1.0, {{qc_problem.x, 0.0}, {qc_problem.y, 1.0}})));
155 } else {
156 const double value = 1.0 / 3.0;
157 EXPECT_THAT(SimpleSolve(qc_problem.model),
159 value, {{qc_problem.x, value}, {qc_problem.y, value}})));
160 }
161}
162
163// We start with the simple LP:
164// max x + 1
165// s.t. 0 <= x <= 1
166//
167// The optimal value is 2. We then add a quadratic constraint:
168// x^2 <= 0.5
169//
170// The optimal solution is x = sqrt(0.5) with objective value 1 + sqrt(0.5).
171// Additionally, if we impose integrality on x, then the optimal solution is
172// x = 0 with objective value 1.
173TEST_P(IncrementalQcTest, LinearToQuadraticUpdate) {
174 if (GetParam().solver_type == SolverType::kXpress) {
175 // This test suffers from a bug in Xpress 9.6.0 (bug introduced in 9.6.0,
176 // fixed in 9.6.1).
177 // We have no easy way to check the Xpress version here, so we just skip
178 // the test.
179 GTEST_SKIP() << "Skip test due to bug in Xpress 9.6.0.";
180 }
181 Model model;
182 const Variable x =
183 model.AddVariable(0.0, 1.0, GetParam().use_integer_variables, "x");
184 model.Maximize(x + 1.0);
185
187 const auto solver,
188 NewIncrementalSolver(&model, GetParam().solver_type, {}));
189 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
190 IsOkAndHolds(IsOptimalWithSolution(2.0, {{x, 1.0}})));
191
192 model.AddQuadraticConstraint(x * x <= 0.5);
193
194 if (!GetParam().supports_qc) {
195 // Here we test that solvers that don't support quadratic constraints return
196 // false in SolverInterface::CanUpdate(). Thus they should fail in their
197 // factory function instead of failing in their SolverInterface::Update()
198 // function. To assert we rely on status annotations added by
199 // IncrementalSolver::Update() to the returned status of Solver::Update()
200 // and Solver::New().
202 solver->Update(),
203 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
204 absl::StatusCode::kUnimplemented),
205 AllOf(HasSubstr("quadratic constraint"),
206 // Sub-string expected for Solver::Update() error.
207 Not(HasSubstr("update failed")),
208 // Sub-string expected for Solver::New() error.
209 HasSubstr("solver re-creation failed"))));
210 return;
211 }
212
213 ASSERT_THAT(solver->Update(),
214 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
215 ? DidUpdate()
216 : Not(DidUpdate())));
217 const double expected_x =
218 GetParam().use_integer_variables ? 0.0 : std::sqrt(0.5);
220 solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
221 IsOkAndHolds(IsOptimalWithSolution(1.0 + expected_x, {{x, expected_x}})));
222}
223
224// We start with the QCP:
225// min_{x,y} y
226// s.t. (x - 1)^2 + (y - 1)^2 + xy <= 1
227// x <= y
228// 0 <= x <= 0.5
229// 0 <= y <= 1
230//
231// We then delete the quadratic constraint, leaving the LP:
232// min_{x,y} y
233// s.t. x <= y
234// 0 <= x <= 0.5
235// 0 <= y <= 1
236//
237// The optimal solution is attained at (x, y) = (0, 0).
238TEST_P(IncrementalQcTest, UpdateDeletesQuadraticConstraint) {
239 if (!GetParam().supports_qc) {
240 GTEST_SKIP() << no_qc_support_message;
241 }
242 HalfEllipseProblem qc_problem(GetParam().use_integer_variables);
244 const auto solver,
245 NewIncrementalSolver(&qc_problem.model, GetParam().solver_type, {}));
246 // We test that the solution is correct elsewhere.
247 ASSERT_OK(solver->Solve({.parameters = GetParam().parameters}));
248
249 qc_problem.model.DeleteQuadraticConstraint(qc_problem.q);
250
251 ASSERT_THAT(solver->Update(),
252 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
253 ? DidUpdate()
254 : Not(DidUpdate())));
255 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
257 0.0, {{qc_problem.x, 0.0}, {qc_problem.y, 0.0}})));
258}
259
260// We start with the QCP:
261// min_{x,y} y
262// s.t. (x - 1)^2 + (y - 1)^2 + xy <= 1
263// x <= y
264// 0 <= x, y <= 2
265//
266// We then delete the x variable, leaving the QCP:
267// min_{y} y
268// s.t. 1 + (y - 1)^2 == y^2 - 2y + 2 <= 1
269// 0 <= y <= 2
270//
271// The optimal solution is attained at y = 1 with objective value 1.
272TEST_P(IncrementalQcTest, UpdateDeletesVariableInQuadraticConstraint) {
273 if (!GetParam().supports_qc) {
274 GTEST_SKIP() << no_qc_support_message;
275 }
276 HalfEllipseProblem qc_problem(GetParam().use_integer_variables);
277
279 const auto solver,
280 NewIncrementalSolver(&qc_problem.model, GetParam().solver_type, {}));
281 // We test that the solution is correct elsewhere.
282 ASSERT_OK(solver->Solve({.parameters = GetParam().parameters}));
283
284 qc_problem.model.DeleteVariable(qc_problem.x);
285
286 ASSERT_THAT(solver->Update(),
287 IsOkAndHolds(GetParam().supports_incremental_variable_deletions
288 ? DidUpdate()
289 : Not(DidUpdate())));
290 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
291 IsOkAndHolds(IsOptimalWithSolution(1.0, {{qc_problem.y, 1.0}},
292 kTolerance)));
293}
294
295// Primal:
296// min_{x} x
297// s.t.
298// Quadratic constraint:
299// x^2 <= 1
300//
301// Optimal solution: x* = -1.
302//
303// Dual (go/mathopt-qcqp-dual):
304// max_{mu, x, r} mu + mu*x^2
305// s.t. mu*2*x + r = 1
306// mu <= 0
307// r = 0
308//
309// Optimal solution: x* = -1, mu* = -0.5.
310TEST_P(QcDualsTest, OnlyQuadraticConstraintLess) {
311 if (!GetParam().supports_qc) {
312 return;
313 }
314 Model model;
315 const Variable x = model.AddVariable();
316 const QuadraticConstraint mu = model.AddQuadraticConstraint(x * x <= 1);
317 model.Minimize(x);
318
319 ASSERT_OK_AND_ASSIGN(const SolveResult solve_result, SimpleSolve(model));
320 const double expected_objective_value = -1.0;
321 EXPECT_THAT(solve_result,
322 IsOptimalWithSolution(expected_objective_value, {{x, -1.0}}));
323 EXPECT_THAT(solve_result,
324 IsOptimalWithDualSolution(expected_objective_value, {},
325 {{mu, -0.5}}, {{x, 0.0}}));
326}
327
328// Primal:
329// min_{x} x
330// s.t.
331// Quadratic constraint:
332// -x^2 >= -1
333//
334// Optimal solution: x* = -1.
335//
336// Dual (go/mathopt-qcqp-dual):
337// max_{mu, x, r} -mu - mu*x^2
338// s.t. -mu*2*x + r = 1
339// mu >= 0
340// r = 0
341//
342// Optimal solution: x* = -1, mu* = 0.5.
343TEST_P(QcDualsTest, OnlyQuadraticConstraintGreater) {
344 if (!GetParam().supports_qc) {
345 return;
346 }
347 Model model;
348 const Variable x = model.AddVariable();
349 const QuadraticConstraint mu = model.AddQuadraticConstraint(-x * x >= -1);
350 model.Minimize(x);
351
352 ASSERT_OK_AND_ASSIGN(const SolveResult solve_result, SimpleSolve(model));
353 const double expected_objective_value = -1.0;
354 EXPECT_THAT(solve_result,
355 IsOptimalWithSolution(expected_objective_value, {{x, -1.0}}));
356 EXPECT_THAT(solve_result,
357 IsOptimalWithDualSolution(expected_objective_value, {},
358 {{mu, 0.5}}, {{x, 0.0}}));
359}
360
361// Primal:
362// min_{x} x1^2 - 10 x1
363// s.t.
364// Quadratic constraints:
365// x1^2 + x0 <= 2
366// Linear constraints:
367// x1 - x0 <= 0
368// -x1 - x0 <= 0
369//
370// Optimal solution: x* = (1, 1).
371//
372// Dual (go/mathopt-qcqp-dual):
373// max_{mu, x, y, r} 2*mu + mu*x1^2 - x1^2
374// s.t. -y0 - y1 + r0 + mu = 0
375// y0 - y1 + r1 + mu*2*x1 = 2*x1 - 10
376// mu <= 0
377// y0 <= 0
378// y1 <= 0
379// r0 = 0
380// r1 = 0
381//
382// Optimal solution: x* = (1, 1), mu* = -8/3, y = (-8/3, 0), r = (0, 0).
383TEST_P(QcDualsTest, QuadraticObjectiveAndLinearAndQuadraticConstraints) {
384 if (!GetParam().supports_qc) {
385 return;
386 }
387 Model model;
388 const Variable x0 = model.AddVariable();
389 const Variable x1 = model.AddVariable();
390 const LinearConstraint y0 = model.AddLinearConstraint(x1 - x0 <= 0.0);
391 const LinearConstraint y1 = model.AddLinearConstraint(-x1 - x0 <= 0.0);
392 const QuadraticConstraint mu =
393 model.AddQuadraticConstraint(x1 * x1 + x0 <= 2);
394 model.Minimize(x1 * x1 - 10.0 * x1);
395
396 ASSERT_OK_AND_ASSIGN(const SolveResult solve_result, SimpleSolve(model));
397 const double expected_objective_value = -9.0;
398 EXPECT_THAT(solve_result, IsOptimalWithSolution(expected_objective_value,
399 {{x0, 1.0}, {x1, 1.0}}));
400 EXPECT_THAT(solve_result,
402 expected_objective_value, {{y0, -8.0 / 3.0}, {y1, 0.0}},
403 {{mu, -8.0 / 3.0}}, {{x0, 0.0}, {x1, 0.0}}));
404}
405
406// Primal:
407// max_{x} -x0^2 + 4x0
408// s.t.
409// Quadratic constraints:
410// x0^2 + x1^2 + x2^2 <= 3
411// Linear constraints:
412// x1 = 1
413// Variable bounds:
414// x2 = 1
415//
416// Optimal solution: x* = (1, 1, 1).
417//
418// Dual (go/mathopt-qcqp-dual):
419// min_{mu, x, y, r} y + r2 + 3*mu + mu*(x0^2 + x1^2 + x2^2) + x0^2
420// s.t. r0 + mu*2*x0 = -2x0 + 4
421// r1 + y + mu*2*x1 = 0
422// r2 + mu*2*x2 = 0
423// mu >= 0
424// r0 = 0
425// r1 = 0
426//
427// Optimal solution: x* = (1, 1, 1), mu* = 1, y = -2, r = (0, 0, -2).
428TEST_P(QcDualsTest, MaxAndVariableBounds) {
429 if (!GetParam().supports_qc) {
430 return;
431 }
432 Model model;
433 const Variable x0 = model.AddVariable();
434 const Variable x1 = model.AddVariable();
435 const Variable x2 = model.AddContinuousVariable(1.0, 1.0);
436 const LinearConstraint y = model.AddLinearConstraint(x1 == 1.0);
437 const QuadraticConstraint mu =
438 model.AddQuadraticConstraint(x0 * x0 + x1 * x1 + x2 * x2 <= 3.0);
439 model.Maximize(-x0 * x0 + 4.0 * x0);
440
441 ASSERT_OK_AND_ASSIGN(const SolveResult solve_result, SimpleSolve(model));
442 const double expected_objective_value = 3.0;
443 EXPECT_THAT(solve_result,
444 IsOptimalWithSolution(expected_objective_value,
445 {{x0, 1.0}, {x1, 1.0}, {x2, 1.0}}));
446 EXPECT_THAT(solve_result,
447 IsOptimalWithDualSolution(expected_objective_value, {{y, -2.0}},
448 {{mu, 1.0}},
449 {{x0, 0.0}, {x1, 0.0}, {x2, -2.0}}));
450}
451
452} // namespace
453} // namespace operations_research::math_opt
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
#define ASSERT_OK(expression)
Definition gmock.h:46
#define EXPECT_OK(expression)
Definition gmock.h:45
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
Definition gmock.h:53
Matcher< SolveResult > IsOptimalWithSolution(const double expected_objective, const VariableMap< double > expected_variable_values, const double tolerance)
Definition matchers.cc:778
EXPECT_THAT(ComputeInfeasibleSubsystem(model, GetParam().solver_type), IsOkAndHolds(IsInfeasible(true, ModelSubset{ .variable_bounds={{x, ModelSubset::Bounds{.lower=false,.upper=true}}},.linear_constraints={ {c, ModelSubset::Bounds{.lower=true,.upper=false}}}})))
TEST_P(InfeasibleSubsystemTest, CanComputeInfeasibleSubsystem)
<=x<=1 IncrementalMipTest::IncrementalMipTest() :model_("incremental_solve_test"), x_(model_.AddContinuousVariable(0.0, 1.0, "x")), y_(model_.AddIntegerVariable(0.0, 2.0, "y")), c_(model_.AddLinearConstraint(0<=x_+y_<=1.5, "c")) { model_.Maximize(3.0 *x_+2.0 *y_+0.1);solver_=NewIncrementalSolver(&model_, TestedSolver()).value();const SolveResult first_solve=solver_->Solve().value();CHECK(first_solve.has_primal_feasible_solution());CHECK_LE(std::abs(first_solve.objective_value() - 3.6), kTolerance)<< first_solve.objective_value();} namespace { TEST_P(SimpleMipTest, OneVarMax) { Model model;const Variable x=model.AddVariable(0.0, 4.0, false, "x");model.Maximize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(8.0));EXPECT_THAT(result.variable_values(), IsNear({{x, 4.0}}));} TEST_P(SimpleMipTest, OneVarMin) { Model model;const Variable x=model.AddVariable(-2.4, 4.0, false, "x");model.Minimize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(-4.8));EXPECT_THAT(result.variable_values(), IsNear({{x, -2.4}}));} TEST_P(SimpleMipTest, OneIntegerVar) { Model model;const Variable x=model.AddVariable(0.0, 4.5, true, "x");model.Maximize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(8.0));EXPECT_THAT(result.variable_values(), IsNear({{x, 4.0}}));} TEST_P(SimpleMipTest, SimpleLinearConstraint) { Model model;const Variable x=model.AddBinaryVariable("x");const Variable y=model.AddBinaryVariable("y");model.Maximize(2.0 *x+y);model.AddLinearConstraint(0.0<=x+y<=1.5, "c");ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, IsOptimal(2.0));EXPECT_THAT(result.variable_values(), IsNear({{x, 1}, {y, 0}}));} TEST_P(SimpleMipTest, Unbounded) { Model model;const Variable x=model.AddVariable(0.0, kInf, true, "x");model.Maximize(2.0 *x);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));if(GetParam().report_unboundness_correctly) { ASSERT_THAT(result, TerminatesWithOneOf({TerminationReason::kUnbounded, TerminationReason::kInfeasibleOrUnbounded}));} else { ASSERT_THAT(result, TerminatesWith(TerminationReason::kOtherError));} } TEST_P(SimpleMipTest, Infeasible) { Model model;const Variable x=model.AddVariable(0.0, 3.0, true, "x");model.Maximize(2.0 *x);model.AddLinearConstraint(x >=4.0);ASSERT_OK_AND_ASSIGN(const SolveResult result, Solve(model, GetParam().solver_type));ASSERT_THAT(result, TerminatesWith(TerminationReason::kInfeasible));} TEST_P(SimpleMipTest, FractionalBoundsContainNoInteger) { if(GetParam().solver_type==SolverType::kGurobi) { GTEST_SKIP()<< "TODO(b/272298816): Gurobi bindings are broken here.";} if(GetParam().solver_type==SolverType::kXpress) { GTEST_SKIP()<< "Xpress does not support contradictory bounds.";} Model model;const Variable x=model.AddIntegerVariable(0.5, 0.6, "x");model.Maximize(x);EXPECT_THAT(Solve(model, GetParam().solver_type), IsOkAndHolds(TerminatesWith(TerminationReason::kInfeasible)));} TEST_P(IncrementalMipTest, EmptyUpdate) { ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());ASSERT_THAT(result, IsOptimal(3.6));EXPECT_THAT(result.variable_values(), IsNear({{x_, 0.5}, {y_, 1.0}}));} TEST_P(IncrementalMipTest, MakeContinuous) { model_.set_continuous(y_);ASSERT_THAT(solver_->Update(), IsOkAndHolds(DidUpdate()));ASSERT_OK_AND_ASSIGN(const SolveResult result, solver_->SolveWithoutUpdate());ASSERT_THAT(result, IsOptimal(4.1));EXPECT_THAT(result.variable_values(), IsNear({{x_, 1.0}, {y_, 0.5}}));} TEST_P(IncrementalMipTest, DISABLED_MakeContinuousWithNonIntegralBounds) { solver_.reset();Model model("bounds");const Variable x=model.AddIntegerVariable(0.5, 1.5, "x");model.Maximize(x);ASSERT_OK_AND_ASSIGN(const auto solver, NewIncrementalSolver(&model, TestedSolver()));ASSERT_THAT(solver->Solve(), IsOkAndHolds(IsOptimal(1.0)));model.set_continuous(x);ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));ASSERT_THAT(solver->SolveWithoutUpdate(), IsOkAndHolds(IsOptimal(1.5)));model.Minimize(x);ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()));ASSERT_THAT(solver-> IsOkAndHolds(IsOptimal(0.5)))
absl::StatusOr< std::unique_ptr< IncrementalSolver > > NewIncrementalSolver(Model *model, SolverType solver_type, SolverInitArguments arguments)
Definition solve.cc:82
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
Matcher< UpdateResult > DidUpdate()
Definition matchers.cc:1028
Matcher< SolveResult > IsOptimalWithDualSolution(const double expected_objective, const LinearConstraintMap< double > expected_dual_values, const VariableMap< double > expected_reduced_costs, const double tolerance)
Definition matchers.cc:791
BoolVar Not(BoolVar x)
Definition cp_model.cc:87
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:46
STL namespace.
QcTestParameters(SolverType solver_type, SolveParameters parameters, bool supports_qc, bool supports_incremental_add_and_deletes, bool supports_incremental_variable_deletions, bool use_integer_variables)
Definition qc_tests.cc:31