Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
second_order_cone_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 <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
32 const SolverType solver_type, SolveParameters parameters,
33 const bool supports_soc_constraints,
34 const bool supports_incremental_add_and_deletes)
35 : solver_type(solver_type),
37 supports_soc_constraints(supports_soc_constraints),
38 supports_incremental_add_and_deletes(
39 supports_incremental_add_and_deletes) {}
40
41std::ostream& operator<<(std::ostream& out,
42 const SecondOrderConeTestParameters& params) {
43 out << "{ solver_type: " << params.solver_type
44 << ", parameters: " << ProtobufShortDebugString(params.parameters.Proto())
45 << ", supports_soc_constraints: "
46 << (params.supports_soc_constraints ? "true" : "false")
47 << ", supports_incremental_add_and_deletes: "
48 << (params.supports_incremental_add_and_deletes ? "true" : "false");
49 return out;
50}
51
52namespace {
53
54using ::testing::AnyOf;
55using ::testing::HasSubstr;
56using ::testing::status::IsOkAndHolds;
57using ::testing::status::StatusIs;
58
59// A bit larger than expected; as of 2023-01-31 Gurobi produces slightly
60// inaccurate solutions on some of the tests.
61constexpr double kTolerance = 1.0e-3;
62constexpr absl::string_view kNoSocSupportMessage =
63 "This test is disabled as the solver does not support second-order cone "
64 "constraints";
65
66// Builds the simple (and uninteresting) SOC model:
67//
68// min 0
69// s.t. ||x||_2 <= 2x
70// 0 <= x <= 1.
71TEST_P(SimpleSecondOrderConeTest, CanBuildSecondOrderConeModel) {
72 Model model;
73 const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
74 model.AddSecondOrderConeConstraint({x}, 2 * x);
75 if (GetParam().supports_soc_constraints) {
76 EXPECT_OK(NewIncrementalSolver(&model, GetParam().solver_type, {}));
77 } else {
78 EXPECT_THAT(NewIncrementalSolver(&model, GetParam().solver_type, {}),
79 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
80 absl::StatusCode::kUnimplemented),
81 HasSubstr("second-order cone constraints")));
82 }
83}
84
85// We model the second-order cone program:
86//
87// max x + y + z
88// s.t. ||(x, 2y, 3z)||_2 <= 1
89// 0 <= x, y <= 1
90//
91// The unique optimal solution is (x*, y*, z*) = (6/7, 3/14, 2/21) with
92// objective value 7/6.
93TEST_P(SimpleSecondOrderConeTest, SolveSimpleSocModel) {
94 if (!GetParam().supports_soc_constraints) {
95 GTEST_SKIP() << kNoSocSupportMessage;
96 }
97 Model model;
98 const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
99 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
100 const Variable z = model.AddContinuousVariable(0.0, 1.0, "z");
101 model.Maximize(x + y + z);
102 model.AddSecondOrderConeConstraint({x, 2.0 * y, 3.0 * z}, 1.0);
103 EXPECT_THAT(SimpleSolve(model),
105 7.0 / 6.0, {{x, 6.0 / 7.0}, {y, 3.0 / 14.0}, {z, 2.0 / 21.0}},
106 kTolerance)));
107}
108
109// We model the second-order cone program:
110//
111// max x + y
112// s.t. ||(x, 2y)||_2 <= 2x + 3
113// ||(2x, y)||_2 <= 2y + 3
114//
115// The unique optimal solution is (x*, y*) = (1, 1) with objective value 2.
116TEST_P(SimpleSecondOrderConeTest, SolveMultipleSocConstraintModel) {
117 if (!GetParam().supports_soc_constraints) {
118 GTEST_SKIP() << kNoSocSupportMessage;
119 }
120 Model model;
121 const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
122 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
123 model.Maximize(x + y);
124 model.AddSecondOrderConeConstraint({x, 2.0 * y}, 2.0 * x + 3.0);
125 model.AddSecondOrderConeConstraint({2.0 * x, y}, 2.0 * y + 3.0);
126 EXPECT_THAT(SimpleSolve(model),
127 IsOkAndHolds(IsOptimalWithSolution(2.0, {{x, 1.0}, {y, 1.0}})));
128}
129
130// We model the second-order cone program:
131//
132// max x
133// s.t. x - y <= 1
134// ||(x, y)||_2 <= 2
135//
136// The unique optimal solution is (x*, y*) = ((sqrt(7)+1)/2, (sqrt(7)-1)/2) with
137// objective value (sqrt(7)+1)/2.
138TEST_P(SimpleSecondOrderConeTest, SolveModelWithSocAndLinearConstraints) {
139 if (!GetParam().supports_soc_constraints) {
140 GTEST_SKIP() << kNoSocSupportMessage;
141 }
142 Model model;
143 const Variable x = model.AddVariable("x");
144 const Variable y = model.AddVariable("y");
145 model.Maximize(x);
146 model.AddLinearConstraint(x - y <= 1.0);
147 model.AddSecondOrderConeConstraint({x, y}, 2.0);
148 const double sqrt_of_seven = std::sqrt(7.0);
150 SimpleSolve(model),
152 (sqrt_of_seven + 1.0) / 2.0,
153 {{x, (sqrt_of_seven + 1.0) / 2.0}, {y, (sqrt_of_seven - 1.0) / 2.0}},
154 kTolerance)));
155}
156
157// We start with the LP:
158//
159// max x + y
160// s.t. x + 0.5y <= 1
161// 0 <= x, y <= 1
162//
163// The unique optimal solution is (x*, y*) = (0.5, 1) with objective value 1.5.
164//
165// We then add the second-order cone constraint
166//
167// ||(x, y)||_2 <= sqrt(0.5)
168//
169// The unique optimal solution is then (x*, y*) = (0.5, 0.5) with objective
170// value 1.
171TEST_P(IncrementalSecondOrderConeTest, LinearToSecondOrderConeUpdate) {
172 Model model;
173 const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
174 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
175 model.AddLinearConstraint(x + 0.5 * y <= 1.0);
176 model.Maximize(x + y);
177
179 const auto solver,
180 NewIncrementalSolver(&model, GetParam().solver_type, {}));
181 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
182 IsOkAndHolds(IsOptimalWithSolution(1.5, {{x, 0.5}, {y, 1.0}})));
183
184 model.AddSecondOrderConeConstraint({x, y}, std::sqrt(0.5));
185
186 if (!GetParam().supports_soc_constraints) {
187 // Here we test that solvers that don't support second-order cone
188 // constraints return false in SolverInterface::CanUpdate(). Thus they
189 // should fail in their factory function instead of failing in their
190 // SolverInterface::Update() function. To assert we rely on status
191 // annotations added by IncrementalSolver::Update() to the returned status
192 // of Solver::Update() and Solver::New().
194 solver->Update(),
195 StatusIs(AnyOf(absl::StatusCode::kInvalidArgument,
196 absl::StatusCode::kUnimplemented),
197 AllOf(HasSubstr("second-order cone constraint"),
198 // Sub-string expected for Solver::Update() error.
199 Not(HasSubstr("update failed")),
200 // Sub-string expected for Solver::New() error.
201 HasSubstr("solver re-creation failed"))));
202 return;
203 }
204
205 ASSERT_THAT(solver->Update(),
206 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
207 ? DidUpdate()
208 : Not(DidUpdate())));
209 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
210 IsOkAndHolds(IsOptimalWithSolution(1.0, {{x, 0.5}, {y, 0.5}},
211 kTolerance)));
212}
213
214// We start with the SOCP:
215//
216// max x + y
217// s.t. x + 0.5y <= 1
218// ||(x, y)||_2 <= sqrt(0.5)
219// 0 <= x, y <= 1
220//
221// The unique optimal solution is then (x*, y*) = (0.5, 0.5) with objective
222// value 1.
223//
224// We then delete the SOC constraint, leaving the LP:
225//
226// max x + y
227// s.t. x + 0.5y <= 1
228// 0 <= x, y <= 1
229//
230// The unique optimal solution is (x*, y*) = (0.5, 1) with objective value 1.5.
231TEST_P(IncrementalSecondOrderConeTest, UpdateDeletesSecondOrderConeConstraint) {
232 if (!GetParam().supports_soc_constraints) {
233 GTEST_SKIP() << kNoSocSupportMessage;
234 }
235 Model model;
236 const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
237 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
238 model.AddLinearConstraint(x + 0.5 * y <= 1.0);
239 const SecondOrderConeConstraint c =
240 model.AddSecondOrderConeConstraint({x, y}, std::sqrt(0.5));
241 model.Maximize(x + y);
242
244 const auto solver,
245 NewIncrementalSolver(&model, GetParam().solver_type, {}));
246 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
247 IsOkAndHolds(IsOptimalWithSolution(1.0, {{x, 0.5}, {y, 0.5}},
248 kTolerance)));
249
250 model.DeleteSecondOrderConeConstraint(c);
251
252 ASSERT_THAT(solver->Update(),
253 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
254 ? DidUpdate()
255 : Not(DidUpdate())));
256 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
257 IsOkAndHolds(IsOptimalWithSolution(1.5, {{x, 0.5}, {y, 1.0}})));
258}
259
260// We start with the SOCP:
261//
262// max x + y
263// s.t. ||x||_2 <= y
264// 0 <= x, y <= 1
265//
266// The unique optimal solution is then (x*, y*) = (1, 1) with objective value 2.
267//
268// We then delete the y variable, leaving the SOCP:
269//
270// max x
271// s.t. ||x||_2 <= 0
272// 0 <= x <= 1
273//
274// The unique optimal solution is x* = 0 with objective value 0.
275TEST_P(IncrementalSecondOrderConeTest, UpdateDeletesUpperBoundingVariable) {
276 if (!GetParam().supports_soc_constraints) {
277 GTEST_SKIP() << kNoSocSupportMessage;
278 }
279 Model model;
280 const Variable x = model.AddContinuousVariable(0.0, 1.0, "x");
281 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
282 model.AddSecondOrderConeConstraint({x}, y);
283 model.Maximize(x + y);
284
286 const auto solver,
287 NewIncrementalSolver(&model, GetParam().solver_type, {}));
288 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
289 IsOkAndHolds(IsOptimalWithSolution(2.0, {{x, 1.0}, {y, 1.0}})));
290
291 model.DeleteVariable(y);
292
293 ASSERT_THAT(solver->Update(),
294 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
295 ? DidUpdate()
296 : Not(DidUpdate())));
297 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
298 IsOkAndHolds(IsOptimalWithSolution(0.0, {{x, 0.0}})));
299}
300
301// We start with the SOCP:
302//
303// max x + y
304// s.t. ||x||_2 <= y + 1
305// 0 <= x <= 2
306// 0 <= y <= 1
307//
308// The unique optimal solution is then (x*, y*) = (2, 1) with objective value 3.
309//
310// We then delete the y variable, leaving the SOCP:
311//
312// max x
313// s.t. ||x||_2 <= 1
314// 0 <= x <= 2
315//
316// The unique optimal solution is x* = 1 with objective value 1.
317TEST_P(IncrementalSecondOrderConeTest,
318 UpdateDeletesVariableInUpperBoundingExpression) {
319 if (!GetParam().supports_soc_constraints) {
320 GTEST_SKIP() << kNoSocSupportMessage;
321 }
322 Model model;
323 const Variable x = model.AddContinuousVariable(0.0, 2.0, "x");
324 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
325 model.AddSecondOrderConeConstraint({x}, y + 1.0);
326 model.Maximize(x + y);
327
329 const auto solver,
330 NewIncrementalSolver(&model, GetParam().solver_type, {}));
331 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
332 IsOkAndHolds(IsOptimalWithSolution(3.0, {{x, 2.0}, {y, 1.0}})));
333
334 model.DeleteVariable(y);
335
336 ASSERT_THAT(solver->Update(),
337 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
338 ? DidUpdate()
339 : Not(DidUpdate())));
340 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
341 IsOkAndHolds(IsOptimalWithSolution(1.0, {{x, 1.0}})));
342}
343
344// We start with the SOCP:
345//
346// min y
347// s.t. ||x||_2 <= y
348// 1 <= x <= 1
349// 0 <= y <= 1
350//
351// The unique optimal solution is then (x*, y*) = (1, 1) with objective value 1.
352//
353// We then delete the x variable, leaving the SOCP:
354//
355// max y
356// s.t. ||0||_2 <= y
357// 0 <= y <= 1
358//
359// The unique optimal solution is y* = 0 with objective value 0.
360TEST_P(IncrementalSecondOrderConeTest, UpdateDeletesVariableThatIsAnArgument) {
361 if (!GetParam().supports_soc_constraints) {
362 GTEST_SKIP() << kNoSocSupportMessage;
363 }
364 Model model;
365 const Variable x = model.AddContinuousVariable(1.0, 1.0, "x");
366 const Variable y = model.AddContinuousVariable(0.0, 1.0, "y");
367 model.AddSecondOrderConeConstraint({x}, y);
368 model.Minimize(y);
369
371 const auto solver,
372 NewIncrementalSolver(&model, GetParam().solver_type, {}));
373 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
374 IsOkAndHolds(IsOptimalWithSolution(1.0, {{x, 1.0}, {y, 1.0}})));
375
376 model.DeleteVariable(x);
377
378 ASSERT_THAT(solver->Update(),
379 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
380 ? DidUpdate()
381 : Not(DidUpdate())));
382 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
383 IsOkAndHolds(IsOptimalWithSolution(0.0, {{y, 0.0}})));
384}
385
386// We start with the SOCP:
387//
388// min y
389// s.t. ||x + 1||_2 <= y
390// 1 <= x <= 1
391// 0 <= y <= 2
392//
393// The unique optimal solution is then (x*, y*) = (1, 2) with objective value 2.
394//
395// We then delete the x variable, leaving the SOCP:
396//
397// max y
398// s.t. ||1||_2 <= y
399// 0 <= y <= 2
400//
401// The unique optimal solution is y* = 1 with objective value 1.
402TEST_P(IncrementalSecondOrderConeTest, UpdateDeletesVariableInAnArgument) {
403 if (!GetParam().supports_soc_constraints) {
404 GTEST_SKIP() << kNoSocSupportMessage;
405 }
406 Model model;
407 const Variable x = model.AddContinuousVariable(1.0, 1.0, "x");
408 const Variable y = model.AddContinuousVariable(0.0, 2.0, "y");
409 model.AddSecondOrderConeConstraint({x + 1.0}, y);
410 model.Minimize(y);
411
413 const auto solver,
414 NewIncrementalSolver(&model, GetParam().solver_type, {}));
415 ASSERT_THAT(solver->Solve({.parameters = GetParam().parameters}),
416 IsOkAndHolds(IsOptimalWithSolution(2.0, {{x, 1.0}, {y, 2.0}})));
417
418 model.DeleteVariable(x);
419
420 ASSERT_THAT(solver->Update(),
421 IsOkAndHolds(GetParam().supports_incremental_add_and_deletes
422 ? DidUpdate()
423 : Not(DidUpdate())));
424 EXPECT_THAT(solver->SolveWithoutUpdate({.parameters = GetParam().parameters}),
425 IsOkAndHolds(IsOptimalWithSolution(1.0, {{y, 1.0}})));
426}
427
428} // namespace
429} // namespace operations_research::math_opt
IntegerValue y
SatParameters parameters
GRBmodel * model
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
Matcher< SolveResult > IsOptimalWithSolution(const double expected_objective, const VariableMap< double > expected_variable_values, const double tolerance)
Definition matchers.cc:777
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.
Definition parameters.h:42
<=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)
Definition solve.cc:82
Matcher< UpdateResult > DidUpdate()
Actual UpdateResult.did_update is true.
Definition matchers.cc:1027
BoolVar Not(BoolVar x)
Definition cp_model.cc:87
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:41
STL namespace.
internal::StatusIsMatcher StatusIs(CodeMatcher code_matcher, MessageMatcher message_matcher)
#define EXPECT_OK(expression)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
SecondOrderConeTestParameters(SolverType solver_type, SolveParameters parameters, bool supports_soc_constraints, bool supports_incremental_add_and_deletes)
bool supports_soc_constraints
True if the solver supports second-order cone constraints.