22#include "absl/status/statusor.h"
23#include "absl/strings/str_cat.h"
24#include "absl/strings/string_view.h"
25#include "absl/time/time.h"
26#include "gtest/gtest.h"
37using ::testing::AnyOf;
38using ::testing::Field;
39using ::testing::Matcher;
41constexpr double kInf = std::numeric_limits<double>::infinity();
47 <<
", disallow_primal_or_dual_infeasible: "
49 <<
", supports_iteration_limit: "
51 <<
", use_integer_variables: "
53 <<
", supports_node_limit: "
55 <<
", support_interrupter: "
57 <<
", supports_one_thread: "
64absl::StatusOr<std::unique_ptr<Model>> LoadMiplibInstance(
65 absl::string_view
name) {
67 const ModelProto model_proto,
68 ReadMpsFile(absl::StrCat(
"ortools/math_opt/solver_tests/testdata/",
name,
73absl::StatusOr<std::unique_ptr<Model>> Load23588() {
74 return LoadMiplibInstance(
"23588");
83Matcher<ProblemStatus> StatusIsPrimalOrDualInfeasible() {
84 return Field(
"primal_or_dual_infeasible ",
88TEST_P(StatusTest, EmptyModel) {
95TEST_P(StatusTest, PrimalAndDualInfeasible) {
96 if (GetParam().use_integer_variables &&
99 <<
"Ignoring this test as GLPK gets stuck in presolve for IP's "
100 "with a primal-dual infeasible LP relaxation.";
103 GTEST_SKIP() <<
"Ignoring this test as Highs 1.7+ returns error.";
108 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x1");
110 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x2");
113 model.AddLinearConstraint(
x1 -
x2 <= 1,
"c1");
114 model.AddLinearConstraint(
x1 -
x2 >= 2,
"c2");
124 StatusIsPrimalOrDualInfeasible()));
127 if (GetParam().disallow_primal_or_dual_infeasible) {
138 if (GetParam().disallow_primal_or_dual_infeasible &&
147TEST_P(StatusTest, PrimalFeasibleAndDualInfeasible) {
149 GTEST_SKIP() <<
"Ignoring this test as CpSat bounds all variables";
154 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x1");
156 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x2");
161 model.AddLinearConstraint(100 <=
x1 - 2 *
x2,
"c1");
163 model.AddLinearConstraint(100 <=
x1 - 2 *
x2 <= 200,
"c1");
175 StatusIsPrimalOrDualInfeasible()));
178 if (GetParam().disallow_primal_or_dual_infeasible) {
183 EXPECT_EQ(result.termination.problem_status.dual_status,
197TEST_P(StatusTest, PrimalInfeasibleAndDualFeasible) {
200 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x1");
202 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x2");
204 model.AddLinearConstraint(
x1 +
x2 <= -1,
"c1");
213 StatusIsPrimalOrDualInfeasible()));
218 if (GetParam().disallow_primal_or_dual_infeasible) {
230 EXPECT_EQ(result.termination.problem_status.dual_status,
235TEST_P(StatusTest, PrimalFeasibleAndDualFeasible) {
238 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x1");
240 model.AddVariable(0,
kInf, GetParam().use_integer_variables,
"x2");
242 model.AddLinearConstraint(
x1 +
x2 <= 1,
"c1");
243 SolveParameters params = GetParam().parameters;
245 Solve(
model, TestedSolver(), {.parameters = params}));
251TEST_P(StatusTest, PrimalFeasibleAndDualFeasibleLpIncomplete) {
252 if (!GetParam().supports_iteration_limit ||
253 GetParam().use_integer_variables) {
254 GTEST_SKIP() <<
"Ignoring this test as it is an LP-only test and requires "
255 "support for iteration limit.";
258 const std::unique_ptr<Model>
model =
261 SolveParameters params = GetParam().parameters;
262 if (GetParam().supports_one_thread) {
265 params.iteration_limit = 2;
267 Solve(*
model, TestedSolver(), {.parameters = params}));
301TEST_P(StatusTest, InfeasibleIpWithPrimalDualFeasibleRelaxation) {
302 if (!GetParam().use_integer_variables) {
303 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
306 const Variable
x1 =
model.AddIntegerVariable(0.5,
kInf,
"x1");
307 const Variable
x2 =
model.AddIntegerVariable(0.5,
kInf,
"x2");
309 model.AddLinearConstraint(
x1 +
x2 <= 1,
"c1");
323TEST_P(StatusTest, InfeasibleIpWithPrimalDualFeasibleRelaxation2) {
324 if (!GetParam().use_integer_variables) {
325 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
329 const Variable
x1 =
model.AddBinaryVariable(
"x1");
330 const Variable
x2 =
model.AddBinaryVariable(
"x2");
332 model.AddLinearConstraint(
x1 +
x2 == 1.5,
"c1");
344TEST_P(StatusTest, InfeasibleIpWithPrimalFeasibleDualInfeasibleRelaxation) {
345 if (!GetParam().use_integer_variables) {
346 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
350 <<
"Ignoring this test as GLPK gets stuck in presolve searching "
351 "for an integer point in the unbounded feasible region of the"
355 GTEST_SKIP() <<
"Ignoring this test as CpSat as it returns MODEL_INVALID";
358 GTEST_SKIP() <<
"Infinite loop for santorini.";
362 const Variable
x1 =
model.AddIntegerVariable(1,
kInf,
"x1");
363 const Variable
x2 =
model.AddIntegerVariable(1,
kInf,
"x2");
365 model.AddLinearConstraint(2 *
x2 == 2 *
x1 + 1,
"c1");
374TEST_P(StatusTest, IncompleteIpSolve) {
375 if (!GetParam().use_integer_variables || !GetParam().supports_node_limit) {
376 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test and requires "
377 "support for node_limit.";
380 GTEST_SKIP() <<
"Ignoring this test as Highs 1.7+ returns MODEL_INVALID";
383 SolveArguments args = {
384 .parameters = {.enable_output =
true, .node_limit = 1}};
395TEST_P(StatusTest, IncompleteIpSolveNoSolution) {
396 if (!GetParam().use_integer_variables) {
397 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
400 const std::unique_ptr<const Model>
model =
403 SolveInterrupter interrupter;
404 SolveArguments args = {.parameters = {.time_limit = absl::Microseconds(1)}};
405 if (GetParam().supports_one_thread) {
406 args.parameters.threads = 1;
424 if (GetParam().support_interrupter) {
425 interrupter.Interrupt();
426 args.interrupter = &interrupter;
#define ASSIGN_OR_RETURN(lhs, rexpr)
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
const std::string name
A name for logging purposes.
An object oriented wrapper for quadratic constraints in ModelStorage.
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)
@ kInfeasible
The primal problem has no feasible solutions.
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)
Matcher< SolveResult > TerminatesWith(const TerminationReason expected)
std::unique_ptr< Model > IndependentSetCompleteGraph(const bool integer, const int n)
absl::StatusOr< ModelProto > ReadMpsFile(const absl::string_view filename)
testing::Matcher< SolveResult > TerminatesWithLimit(const Limit expected, const bool allow_limit_undetermined)
@ kTime
The algorithm stopped after a user-specified computation time.
std::unique_ptr< Model > DenseIndependentSet(const bool integer, const int n)
Matcher< SolveResult > IsOptimal(const std::optional< double > expected_primal_objective, const double tolerance)
@ kUndetermined
Solver does not claim a status.
@ kFeasible
Solver claims the problem is feasible.
@ kInfeasible
Solver claims the problem is infeasible.
testing::Matcher< SolveResult > TerminatesWithReasonNoSolutionFound(const Limit expected, const bool allow_limit_undetermined)
std::string ProtobufShortDebugString(const P &message)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr)
FeasibilityStatus primal_status
Status for the primal problem.
bool primal_or_dual_infeasible
FeasibilityStatus dual_status
Status for the dual problem (or for the dual of a continuous relaxation).
SolveParametersProto Proto() const
bool supports_iteration_limit
bool disallow_primal_or_dual_infeasible
SolveParameters parameters
bool support_interrupter
True if the solver support SolveInterrupter.
bool use_integer_variables
True if the tests should be performed with integer variables.
SolverType solver_type
The tested solver.