26#include "absl/container/flat_hash_set.h"
27#include "absl/log/check.h"
28#include "absl/status/status.h"
29#include "absl/status/statusor.h"
30#include "absl/strings/escaping.h"
31#include "absl/strings/str_cat.h"
32#include "absl/strings/str_join.h"
33#include "absl/strings/string_view.h"
34#include "absl/synchronization/mutex.h"
35#include "absl/types/span.h"
36#include "gtest/gtest.h"
72 <<
"\", solve_parameters: "
95 void operator()(std::string*
const out,
const T value) {
100absl::StatusOr<std::unique_ptr<Model>> LoadMiplibInstance(
101 const absl::string_view name) {
103 const ModelProto model_proto,
104 ReadMpsFile(absl::StrCat(
"ortools/math_opt/solver_tests/testdata/", name,
115 <<
", add_cuts: " << params.
add_cuts <<
", supported_events: "
117 EnumFormatter<CallbackEvent>())
118 <<
", all_solutions: "
122 <<
", reaches_cut_callback: "
133using ::testing::AllOf;
134using ::testing::AnyOf;
135using ::testing::Each;
136using ::testing::HasSubstr;
137using ::testing::IsEmpty;
138using ::testing::Pair;
139using ::testing::UnorderedElementsAre;
140using ::testing::status::IsOkAndHolds;
141using ::testing::status::StatusIs;
143constexpr double kInf = std::numeric_limits<double>::infinity();
147 Model model(
"model");
150 std::vector<std::string> callback_messages;
151 const auto callback = [&](absl::Span<const std::string> messages) {
152 const absl::MutexLock lock(mutex);
153 for (
const auto& message : messages) {
154 callback_messages.push_back(message);
159 Solve(model, GetParam().solver_type, {.message_callback = callback}),
161 if (!GetParam().support_message_callback) {
167 Model model(
"model");
169 model.AddVariable(0, 21.0, GetParam().integer_variables,
"x");
170 model.Maximize(2.0 *
x);
173 std::vector<std::string> callback_messages;
176 .parameters = GetParam().solve_parameters,
178 [&](absl::Span<const std::string> messages) {
179 const absl::MutexLock lock(mutex);
180 for (
const auto& message : messages) {
181 callback_messages.push_back(message);
189#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
190 ScopedStdStreamCapture stdout_capture(CapturedStream::kStdout);
193 Solve(model, GetParam().solver_type, args));
194#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
196 std::move(stdout_capture).StopCaptureAndReturnContents(),
202 if (GetParam().support_message_callback) {
203 EXPECT_THAT(absl::StrJoin(callback_messages,
"\n"),
204 AllOf(AnyOf(HasSubstr(
"42"), HasSubstr(
"4.2")),
205 HasSubstr(GetParam().ending_substring)));
212 callback_messages.clear();
213 args.parameters.enable_output =
true;
215#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
216 ScopedStdStreamCapture stdout_capture(CapturedStream::kStdout);
219 Solve(model, GetParam().solver_type, args));
220#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
222 std::move(stdout_capture).StopCaptureAndReturnContents(),
228 if (GetParam().support_message_callback) {
229 EXPECT_THAT(absl::StrJoin(callback_messages,
"\n"),
230 AllOf(AnyOf(HasSubstr(
"42"), HasSubstr(
"4.2")),
231 HasSubstr(GetParam().ending_substring)));
238 args.parameters.enable_output =
true;
239 args.message_callback =
nullptr;
240 callback_messages.clear();
242#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
243 ScopedStdStreamCapture stdout_capture(CapturedStream::kStdout);
246 Solve(model, GetParam().solver_type, args));
247#ifdef OPERATIONS_RESEARCH_OUTPUT_CAPTURE_SUPPORTED
248 EXPECT_THAT(std::move(stdout_capture).StopCaptureAndReturnContents(),
249 AllOf(AnyOf(HasSubstr(
"42"), HasSubstr(
"4.2")),
250 HasSubstr(GetParam().ending_substring)));
257 if (!GetParam().support_message_callback) {
258 GTEST_SKIP() <<
"Message callback not supported. Ignoring this test.";
260 if (!GetParam().support_interrupter) {
261 GTEST_SKIP() <<
"Solve interrupter not supported. Ignoring this test.";
263 const std::unique_ptr<const Model> model =
267 std::vector<std::string> callback_messages;
271 SolveInterrupter interrupter;
273 args.message_callback = [&](absl::Span<const std::string> messages) {
274 const absl::MutexLock lock(mutex);
275 for (
const auto& message : messages) {
276 callback_messages.push_back(message);
280 interrupter.Interrupt();
284 Solve(*model, GetParam().solver_type, args));
287 EXPECT_THAT(absl::StrJoin(callback_messages,
"\n"),
288 Not(HasSubstr(GetParam().ending_substring)));
296 <<
"Test skipped because this solver does not support this event.";
299 Model model(
"model");
300 Variable x = model.AddVariable(0, 2.0, GetParam().integer_variables,
"x");
301 Variable y = model.AddVariable(0, 3.0, GetParam().integer_variables,
"y");
302 model.AddLinearConstraint(y <= 1.0);
303 model.Maximize(2.0 *
x + y);
307 std::optional<CallbackData> last_presolve_data;
309 const absl::MutexLock lock(mutex);
310 last_presolve_data = callback_data;
315 Solve(model, GetParam().solver_type, args));
317 ASSERT_TRUE(last_presolve_data.has_value());
318 EXPECT_EQ(last_presolve_data->presolve_stats.removed_variables(), 2);
319 EXPECT_EQ(last_presolve_data->presolve_stats.removed_constraints(), 1);
325 <<
"Test skipped because this solver does not support this event.";
328 Model model(
"model");
329 Variable x1 = model.AddVariable(0, 2.0,
false,
"x1");
330 Variable x2 = model.AddVariable(0, 3.0,
false,
"x2");
331 Variable x3 = model.AddVariable(0, 4.0,
false,
"x3");
332 model.Maximize(x1 - x2 + x3);
348 model.Maximize(-x1 + x2 - x3);
352 std::vector<CallbackDataProto::SimplexStats> stats;
353 args.callback = [&](
const CallbackData& callback_data) {
354 const absl::MutexLock lock(mutex);
355 stats.push_back(callback_data.simplex_stats);
361 ASSERT_GE(stats.size(), 3);
365 EXPECT_NEAR(s.primal_infeasibility(), 0,
kTolerance);
368 EXPECT_EQ(stats[0].iteration_count(), 0);
369 EXPECT_GT(stats[0].dual_infeasibility(), 0.0);
370 EXPECT_NEAR(stats[0].objective_value(), -6.0,
kTolerance);
372 EXPECT_GE(stats.back().iteration_count(), 3);
379 <<
"Test skipped because this solver does not support this event.";
383 const std::unique_ptr<const Model> model =
391 std::vector<CallbackDataProto::BarrierStats> stats;
392 args.callback = [&](
const CallbackData& callback_data) {
393 const absl::MutexLock lock(mutex);
394 stats.push_back(callback_data.barrier_stats);
398 Solve(*model, GetParam().solver_type, args));
402 ASSERT_GE(stats.size(), 1);
403 EXPECT_GE(stats.back().iteration_count(), 3);
408 GTEST_SKIP() <<
"Test skipped because this solver does not support "
409 "CallbackEvent::kMipSolution.";
413 ASSERT_TRUE(GetParam().integer_variables);
415 Model model(
"model");
416 const Variable x = model.AddBinaryVariable(
"x");
417 const Variable y = model.AddBinaryVariable(
"y");
418 model.AddLinearConstraint(
x + y <= 1);
419 model.Maximize(
x + 2 * y);
424 std::atomic<bool> cb_called =
false;
425 std::atomic<bool> cb_called_on_optimal =
false;
427 const absl::MutexLock lock(mutex);
430 if (!callback_data.solution.has_value()) {
431 ADD_FAILURE() <<
"callback_data.solution should always be set at event "
432 "MIP_SOLUTION but was empty";
437 sol, AnyOf(
IsNear({{
x, 0.0}, {y, 0.0}}),
IsNear({{
x, 1.0}, {y, 0.0}}),
438 IsNear({{
x, 0.0}, {y, 1.0}})));
439 EXPECT_LE(callback_data.mip_stats.primal_bound(), 2.05);
440 EXPECT_GE(callback_data.mip_stats.dual_bound(), 1.95);
442 cb_called_on_optimal =
true;
448 EXPECT_TRUE(cb_called);
449 EXPECT_TRUE(cb_called_on_optimal);
454 GTEST_SKIP() <<
"Test skipped because this solver does not support "
455 "CallbackEvent::kMipSolution.";
459 ASSERT_TRUE(GetParam().integer_variables);
462 const std::unique_ptr<const Model> model =
472 Solve(*model, GetParam().solver_type, args));
474 EXPECT_TRUE(result.has_primal_feasible_solution());
479 GTEST_SKIP() <<
"Test skipped because this solver does not support "
480 "CallbackEvent::kMipSolution.";
482 if (!GetParam().all_solutions.has_value()) {
483 GTEST_SKIP() <<
"Test skipped because this solver does not support "
484 "getting all solutions.";
488 ASSERT_TRUE(GetParam().integer_variables);
490 Model model(
"model");
491 const Variable x = model.AddBinaryVariable(
"x");
492 const Variable y = model.AddBinaryVariable(
"y");
493 const Variable z = model.AddBinaryVariable(
"z");
494 model.AddLinearConstraint(
x + y + z <= 1);
497 .parameters = *GetParam().all_solutions,
501 bool cb_called_on_zero =
false;
502 bool cb_called_on_x =
false;
503 bool cb_called_on_y =
false;
504 bool cb_called_on_z =
false;
506 const absl::MutexLock lock(mutex);
508 if (!callback_data.solution.has_value()) {
509 ADD_FAILURE() <<
"callback_data.solution should always be set at event "
510 "MIP_SOLUTION but was empty";
515 IsNear({{
x, 1.0}, {y, 0.0}, {z, 0.0}}),
516 IsNear({{
x, 0.0}, {y, 1.0}, {z, 0.0}}),
517 IsNear({{
x, 0.0}, {y, 0.0}, {z, 1.0}})));
519 cb_called_on_x =
true;
521 cb_called_on_y =
true;
523 cb_called_on_z =
true;
525 cb_called_on_zero =
true;
531 EXPECT_TRUE(cb_called_on_zero);
532 EXPECT_TRUE(cb_called_on_x);
533 EXPECT_TRUE(cb_called_on_y);
534 EXPECT_TRUE(cb_called_on_z);
539 GTEST_SKIP() <<
"Test skipped because this solver does not support "
540 "CallbackEvent::kMipSolution.";
542 if (!GetParam().add_lazy_constraints) {
543 GTEST_SKIP() <<
"Test skipped because this solver does not support adding "
548 ASSERT_TRUE(GetParam().integer_variables);
550 Model model(
"model");
551 const Variable x = model.AddBinaryVariable(
"x");
552 const Variable y = model.AddBinaryVariable(
"y");
553 model.Maximize(
x + 2 * y);
557 .add_lazy_constraints =
true}};
560 if (!callback_data.solution.has_value()) {
561 ADD_FAILURE() <<
"callback_data.solution should always be set at event "
562 "MIP_SOLUTION but was empty";
566 if (sol.size() != 2) {
568 <<
"callback_data.solution should have two elements but found: "
572 const double x_val = sol.at(
x);
573 const double y_val = sol.at(y);
575 if (x_val + y_val >= 1.0 + 1e-5) {
576 result.AddLazyConstraint(
x + y <= 1.0);
581 Solve(model, GetParam().solver_type, args));
587 GTEST_SKIP() <<
"Test skipped because this solver does not support "
588 "CallbackEvent::kMipSolution.";
590 if (!GetParam().add_lazy_constraints) {
591 GTEST_SKIP() <<
"Test skipped because this solver does not support adding "
596 ASSERT_TRUE(GetParam().integer_variables);
598 Model model(
"model");
599 const Variable x = model.AddBinaryVariable(
"x");
600 const Variable y = model.AddBinaryVariable(
"y");
601 const Variable z = model.AddBinaryVariable(
"z");
602 model.Maximize(
x + 2 * y - z);
603 model.AddLinearConstraint(
x + y + z >= 1.0);
607 .add_lazy_constraints =
true}};
610 if (!callback_data.solution.has_value()) {
611 ADD_FAILURE() <<
"callback_data.solution should always be set at event "
612 "MIP_SOLUTION but was empty";
616 if (sol.size() != 3) {
618 <<
"callback_data.solution should have two elements but found: "
622 const double x_val = sol.at(
x);
623 const double y_val = sol.at(y);
625 if (x_val + y_val >= 1.0 + 1e-5) {
626 result.AddLazyConstraint(
x + y <= 1.0);
631 Solve(model, GetParam().solver_type, args));
637 GTEST_SKIP() <<
"Test skipped because this solver does not support "
638 "CallbackEvent::kMipSolution.";
642 ASSERT_TRUE(GetParam().integer_variables);
644 Model model(
"model");
645 const Variable x = model.AddBinaryVariable(
"x");
646 const Variable y = model.AddBinaryVariable(
"y");
647 model.AddLinearConstraint(
x + y <= 1);
648 model.Maximize(
x + 2 * y);
654 std::atomic<bool> cb_called =
false;
655 std::atomic<bool> cb_called_on_optimal =
false;
656 args.callback = [&](
const CallbackData& callback_data) {
657 const absl::MutexLock lock(mutex);
660 if (!callback_data.solution.has_value()) {
661 ADD_FAILURE() <<
"callback_data.solution should always be set at event "
662 "MIP_SOLUTION but was empty";
668 cb_called_on_optimal =
true;
674 EXPECT_TRUE(cb_called);
675 EXPECT_TRUE(cb_called_on_optimal);
680 GTEST_SKIP() <<
"This test does not work with SCIP v900";
683 GTEST_SKIP() <<
"Test skipped because this solver does not support "
684 "CallbackEvent::kMipNode.";
686 if (!GetParam().add_cuts) {
687 GTEST_SKIP() <<
"Test skipped because this solver does not support adding "
690 if (!GetParam().reaches_cut_callback.has_value()) {
691 GTEST_SKIP() <<
"Test skipped, needs reaches_cut_callback to be set.";
695 ASSERT_TRUE(GetParam().integer_variables);
710 Model model(
"model");
711 constexpr int n = 10;
712 std::vector<Variable>
x;
713 for (
int i = 0;
i < n; ++
i) {
714 x.push_back(model.AddBinaryVariable(absl::StrCat(
"x", i)));
716 for (
int i = 0;
i < n; ++
i) {
717 for (
int j = i + 1; j < n; ++j) {
718 for (
int k = j + 1; k < n; ++k) {
719 model.AddLinearConstraint(
x[i] +
x[j] +
x[k] <= 2.0);
723 model.Maximize(
Sum(
x));
725 for (
const bool use_cut : {
false,
true}) {
726 SCOPED_TRACE(absl::StrCat(
"use_cut:", use_cut));
728 GetParam().reaches_cut_callback.value()};
735 if (!callback_data.solution.has_value()) {
740 if (
Sum(
x).Evaluate(sol) > 2.0 + 1.0e-5) {
741 result.AddUserCut(
Sum(
x) <= 2.0);
747 Solve(model, GetParam().solver_type, args));
763 GTEST_SKIP() <<
"Test skipped because this solver does not support "
764 "CallbackEvent::kMipNode.";
768 ASSERT_TRUE(GetParam().integer_variables);
774 LoadMiplibInstance(
"23588"));
775 const std::vector<Variable> variables = model->SortedVariables();
776 CHECK_GE(variables.size(), 3);
784 std::vector<VariableMap<double>> solutions;
785 int empty_solution_count = 0;
788 const absl::MutexLock lock(mutex);
789 if (!callback_data.solution.has_value()) {
790 empty_solution_count++;
792 solutions.push_back(callback_data.solution.value());
798 LOG(INFO) <<
"callback_data.solution was not set " << empty_solution_count
800 EXPECT_THAT(solutions, Each(UnorderedElementsAre(Pair(x0, _), Pair(x2, _))));
805 GTEST_SKIP() <<
"Test skipped because this solver does not support "
806 "CallbackEvent::kMip.";
810 ASSERT_TRUE(GetParam().integer_variables);
817 LoadMiplibInstance(
"23588"));
819 std::atomic<double> best_primal_bound =
820 std::numeric_limits<double>::infinity();
821 std::atomic<double> best_dual_bound =
822 -std::numeric_limits<double>::infinity();
827 const double primal_bound = callback_data.mip_stats.primal_bound();
828 const double dual_bound = callback_data.mip_stats.dual_bound();
829 best_primal_bound = std::fmin(best_primal_bound, primal_bound);
830 best_dual_bound = std::fmax(best_dual_bound, dual_bound);
835 LOG(INFO) <<
"best_primal_bound: "
836 << RoundTripDoubleFormat(best_primal_bound.load());
837 LOG(INFO) <<
"best_dual_bound: "
838 << RoundTripDoubleFormat(best_dual_bound.load());
839 EXPECT_THAT(best_primal_bound.load(), testing::DoubleNear(8090, 0.5));
840 EXPECT_LE(best_dual_bound.load(), 8090.5);
841 EXPECT_GE(best_dual_bound.load(), 7640);
846 GTEST_SKIP() <<
"Test skipped because this solver does not support "
847 "CallbackEvent::kMipSolution.";
849 if (!GetParam().add_lazy_constraints) {
850 GTEST_SKIP() <<
"Test skipped because this solver does not support adding "
855 ASSERT_TRUE(GetParam().integer_variables);
858 Model model(
"model");
859 const Variable x = model.AddBinaryVariable(
"x");
860 const Variable y = model.AddBinaryVariable(
"y");
861 model.Maximize(
x + 2 * y);
865 .add_lazy_constraints =
true}};
867 bool added_cut =
false;
869 const absl::MutexLock lock(mutex);
872 result.AddLazyConstraint(
x + y <= -
kInf);
878 StatusIs(absl::StatusCode::kInvalidArgument,
879 HasSubstr(
"Invalid negative infinite value; for "
880 "GeneratedLinearConstraint.upper_bound")));
884 Model model(
"model");
885 model.AddVariable(0, 1.0, GetParam().integer_variables,
"x");
888 if (GetParam().supported_events.contains(event)) {
891 SCOPED_TRACE(absl::StrCat(
"event: ",
EnumToString(event)));
894 .callback_registration = {.events = {
event}},
898 StatusIs(absl::StatusCode::kInvalidArgument,
#define ASSIGN_OR_RETURN(lhs, rexpr)
CallbackDataProto_SimplexStats SimplexStats
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
const MapUtilMappedT< Collection > & FindWithDefault(const Collection &collection, const KeyType &key, const MapUtilMappedT< Collection > &value)
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)
absl::flat_hash_map< Variable, V > VariableMap
LinearExpression Sum(const Iterable &items)
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Matcher< Termination > LimitIs(math_opt::Limit limit, const Matcher< std::string > detail_matcher)
std::unique_ptr< Model > SmallModel(const bool integer)
<=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)
testing::Matcher< SolveResult > TerminatesWithReasonFeasible(const Limit expected, const bool allow_limit_undetermined)
absl::StatusOr< ModelProto > ReadMpsFile(const absl::string_view filename)
testing::Matcher< SolveResult > TerminatesWithLimit(const Limit expected, const bool allow_limit_undetermined)
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
Matcher< VariableMap< double > > IsNear(VariableMap< double > expected, const double tolerance)
MapFilter< ValueType > MakeKeepKeysFilter(const Collection &keys)
Enum< E >::Proto EnumToProto(std::optional< E > value)
constexpr double kTolerance
std::unique_ptr< Model > DenseIndependentSet(const bool integer, const int n)
absl::string_view EnumToString(E value)
Matcher< SolveResult > IsOptimal(const std::optional< double > expected_primal_objective, const double tolerance)
std::string ProtoEnumToString(ProtoEnumType enum_value)
std::string ProtobufShortDebugString(const P &message)
testing::Matcher< std::string > EmptyOrGurobiLicenseWarningIfGurobi(const bool is_gurobi)
std::optional< SolveParameters > all_solutions
CallbackTestParams(SolverType solver_type, bool integer_variables, bool add_lazy_constraints, bool add_cuts, absl::flat_hash_set< CallbackEvent > supported_events, std::optional< SolveParameters > all_solutions, std::optional< SolveParameters > reaches_cut_callback)
absl::flat_hash_set< CallbackEvent > supported_events
bool add_lazy_constraints
std::optional< SolveParameters > reaches_cut_callback
static absl::Span< const E > AllValues()
SolveParameters solve_parameters
bool support_message_callback
std::string ending_substring
MessageCallbackTestParams(SolverType solver_type, bool support_message_callback, bool support_interrupter, bool integer_variables, std::string ending_substring, SolveParameters solve_parameters={})
const SolveInterrupter *absl_nullable interrupter
SolveParameters parameters
std::optional< int64_t > node_limit
SolveParametersProto Proto() const
std::optional< Emphasis > presolve