17#include <initializer_list>
21#include "absl/log/check.h"
22#include "absl/status/statusor.h"
23#include "gtest/gtest.h"
33using ::testing::status::IsOkAndHolds;
36constexpr double kInf = std::numeric_limits<double>::infinity();
41 SolverType solver_type,
bool report_unboundness_correctly)
42 : solver_type(solver_type),
43 report_unboundness_correctly(report_unboundness_correctly) {}
48 <<
", report_unboundness_correctly: "
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);
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();
74TEST_P(SimpleMipTest, OneVarMax) {
76 const Variable
x =
model.AddVariable(0.0, 4.0,
false,
"x");
79 Solve(
model, GetParam().solver_type));
80 ASSERT_THAT(result, IsOptimal(8.0));
81 EXPECT_THAT(result.variable_values(), IsNear({{
x, 4.0}}));
84TEST_P(SimpleMipTest, OneVarMin) {
86 const Variable
x =
model.AddVariable(-2.4, 4.0,
false,
"x");
94TEST_P(SimpleMipTest, OneIntegerVar) {
96 const Variable
x =
model.AddVariable(0.0, 4.5,
true,
"x");
104TEST_P(SimpleMipTest, SimpleLinearConstraint) {
106 const Variable
x =
model.AddBinaryVariable(
"x");
107 const Variable
y =
model.AddBinaryVariable(
"y");
109 model.AddLinearConstraint(0.0 <=
x +
y <= 1.5,
"c");
116TEST_P(SimpleMipTest, Unbounded) {
118 const Variable
x =
model.AddVariable(0.0, kInf,
true,
"x");
122 if (GetParam().report_unboundness_correctly) {
124 {TerminationReason::kUnbounded,
125 TerminationReason::kInfeasibleOrUnbounded}));
131TEST_P(SimpleMipTest, Infeasible) {
133 const Variable
x =
model.AddVariable(0.0, 3.0,
true,
"x");
135 model.AddLinearConstraint(
x >= 4.0);
141TEST_P(SimpleMipTest, FractionalBoundsContainNoInteger) {
142 if (GetParam().solver_type == SolverType::kGurobi) {
144 GTEST_SKIP() <<
"TODO(b/272298816): Gurobi bindings are broken here.";
147 const Variable
x =
model.AddIntegerVariable(0.5, 0.6,
"x");
153TEST_P(IncrementalMipTest, EmptyUpdate) {
160TEST_P(IncrementalMipTest, MakeContinuous) {
161 model_.set_continuous(y_);
170TEST_P(IncrementalMipTest, DISABLED_MakeContinuousWithNonIntegralBounds) {
174 Model
model(
"bounds");
175 const Variable
x =
model.AddIntegerVariable(0.5, 1.5,
"x");
216 model_.set_integer(x_);
224 model_.set_minimize();
232 model_.set_objective_offset(0.2);
240 model_.set_objective_coefficient(x_, 5.0);
248 model_.set_lower_bound(x_, 0.75);
256 model_.set_upper_bound(x_, 2.0);
264 model_.set_lower_bound(c_, 1.0);
271 if (TestedSolver() != SolverType::kGscip) {
272 EXPECT_EQ(result.solve_stats.node_count, 0);
274 EXPECT_EQ(result.solve_stats.simplex_iterations, 0);
275 EXPECT_EQ(result.solve_stats.barrier_iterations, 0);
279 model_.set_upper_bound(c_, 1.0);
287 model_.set_coefficient(c_, x_, 0.5);
295 Variable z = model_.AddVariable(0.0, 1.0,
true,
"z");
296 model_.set_objective_coefficient(z, 10.0);
297 model_.set_coefficient(c_, z, 1.0);
302 IsNear({{x_, 0.5}, {y_, 0.0}, {z, 1.0}}));
306 model_.AddLinearConstraint(0.0 <= x_ + 2.0 * y_ <= 2.0,
"d");
314 model_.DeleteVariable(x_);
322 model_.DeleteLinearConstraint(c_);
330 model_.set_lower_bound(x_, 3.0);
334 model_.set_upper_bound(x_, 5.0);
342 model_.set_lower_bound(c_, 4.0);
347 model_.set_upper_bound(c_, 5.5);
351 IsOkAndHolds(
IsOptimal(3 * 4.5 + 2 * 1 + 0.1)));
absl::StatusOr< SolveResultProto > Solve(const SolveArgs &arguments) override
Solves the current model (including all updates).
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.
ASSERT_THAT(solver->Update(), IsOkAndHolds(DidUpdate()))
Matcher< SolveResult > TerminatesWithOneOf(const std::vector< TerminationReason > &allowed)
Checks that the result has one of the allowed termination reasons.
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
absl::StatusOr< std::unique_ptr< IncrementalSolver > > NewIncrementalSolver(Model *model, SolverType solver_type, SolverInitArguments arguments)
Matcher< SolveResult > TerminatesWith(const TerminationReason expected)
Matcher< VariableMap< double > > IsNear(VariableMap< double > expected, const double tolerance)
Matcher< UpdateResult > DidUpdate()
Actual UpdateResult.did_update is true.
constexpr double kTolerance
Matcher< SolveResult > IsOptimal(const std::optional< double > expected_primal_objective, const double tolerance)
In SWIG mode, we don't want anything besides these top-level includes.
internal::IsOkAndHoldsMatcher< typename std::decay< InnerMatcher >::type > IsOkAndHolds(InnerMatcher &&inner_matcher)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
SimpleMipTestParameters(SolverType solver_type, bool report_unboundness_correctly=true)
SolverType solver_type
The tested solver.
bool report_unboundness_correctly