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 ",
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.";
110 model.AddLinearConstraint(x1 - x2 <= 1,
"c1");
111 model.AddLinearConstraint(x1 - x2 >= 2,
"c2");
121 StatusIsPrimalOrDualInfeasible()));
124 if (GetParam().disallow_primal_or_dual_infeasible) {
135 if (GetParam().disallow_primal_or_dual_infeasible &&
146 GTEST_SKIP() <<
"Ignoring this test as CpSat bounds all variables";
158 model.AddLinearConstraint(100 <= x1 - 2 * x2,
"c1");
160 model.AddLinearConstraint(100 <= x1 - 2 * x2 <= 200,
"c1");
172 StatusIsPrimalOrDualInfeasible()));
175 if (GetParam().disallow_primal_or_dual_infeasible) {
180 EXPECT_EQ(result.termination.problem_status.dual_status,
201 model.AddLinearConstraint(x1 + x2 <= -1,
"c1");
210 StatusIsPrimalOrDualInfeasible()));
215 if (GetParam().disallow_primal_or_dual_infeasible) {
227 EXPECT_EQ(result.termination.problem_status.dual_status,
239 model.AddLinearConstraint(x1 + x2 <= 1,
"c1");
242 Solve(model, TestedSolver(), {.parameters = params}));
249 if (!GetParam().supports_iteration_limit ||
250 GetParam().use_integer_variables) {
251 GTEST_SKIP() <<
"Ignoring this test as it is an LP-only test and requires "
252 "support for iteration limit.";
255 const std::unique_ptr<Model> model =
259 if (GetParam().supports_one_thread) {
264 Solve(*model, TestedSolver(), {.parameters = params}));
299 if (!GetParam().use_integer_variables) {
300 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
303 const Variable x1 = model.AddIntegerVariable(0.5,
kInf,
"x1");
304 const Variable x2 = model.AddIntegerVariable(0.5,
kInf,
"x2");
306 model.AddLinearConstraint(x1 + x2 <= 1,
"c1");
321 if (!GetParam().use_integer_variables) {
322 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
326 const Variable x1 = model.AddBinaryVariable(
"x1");
327 const Variable x2 = model.AddBinaryVariable(
"x2");
329 model.AddLinearConstraint(x1 + x2 == 1.5,
"c1");
342 if (!GetParam().use_integer_variables) {
343 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
347 <<
"Ignoring this test as GLPK gets stuck in presolve searching "
348 "for an integer point in the unbounded feasible region of the"
352 GTEST_SKIP() <<
"Ignoring this test as CpSat as it returns MODEL_INVALID";
355 GTEST_SKIP() <<
"Infinite loop for santorini.";
359 const Variable x1 = model.AddIntegerVariable(1,
kInf,
"x1");
360 const Variable x2 = model.AddIntegerVariable(1,
kInf,
"x2");
362 model.AddLinearConstraint(2 * x2 == 2 * x1 + 1,
"c1");
372 if (!GetParam().use_integer_variables || !GetParam().supports_node_limit) {
373 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test and requires "
374 "support for node_limit.";
377 GTEST_SKIP() <<
"Ignoring this test as Highs 1.7+ returns MODEL_INVALID";
381 .parameters = {.enable_output =
true, .node_limit = 1}};
383 Solve(*model, GetParam().solver_type, args));
393 if (!GetParam().use_integer_variables) {
394 GTEST_SKIP() <<
"Ignoring this test as it is an IP-only test.";
397 const std::unique_ptr<const Model> model =
400 SolveInterrupter interrupter;
401 SolveArguments args = {.parameters = {.time_limit = absl::Microseconds(1)}};
402 if (GetParam().supports_one_thread) {
403 args.parameters.threads = 1;
421 if (GetParam().support_interrupter) {
422 interrupter.Interrupt();
423 args.interrupter = &interrupter;
426 Solve(*model, GetParam().solver_type, args));
#define ASSIGN_OR_RETURN(lhs, rexpr)
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined)
--— Builder methods --—
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
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).
std::optional< int64_t > iteration_limit
SolveParametersProto Proto() const
StatusTestParameters(const SolverType solver_type, SolveParameters parameters, const bool disallow_primal_or_dual_infeasible, const bool supports_iteration_limit, const bool use_integer_variables, const bool supports_node_limit, const bool support_interrupter, const bool supports_one_thread)
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.