Skip to content

Xpress: empty bound check not working #5085

@Mizux

Description

@Mizux

For SCIP and GLPK the following test pass since integer round leading to empty bound is correctly managed

We should try to improve xpress_solver to have the same behaviour...

TEST_P(SimpleMipTest, FractionalBoundsContainNoInteger) {
if (GetParam().solver_type == SolverType::kGurobi) {
// TODO(b/272298816): Gurobi bindings are broken here.
GTEST_SKIP() << "TODO(b/272298816): Gurobi bindings are broken here.";
}
if (GetParam().solver_type == SolverType::kXpress) {
// Xpress rounds bounds of integer variables on input, so the bounds
// specified here result in [1,0]. Xpress also checks that bounds are
// not contradicting, so it rejects creation of such a variable.
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)));
}

ref: GLPK implem...

std::optional<SolveResultProto> GlpkSolver::EmptyIntegerBoundsResult() {
const int num_cols = glp_get_num_cols(problem_);
for (int c = 1; c <= num_cols; ++c) {
if (!variables_.IsInteger(problem_, c)) {
continue;
}
const double lb = variables_.unrounded_lower_bounds[c - 1];
const double ub = variables_.unrounded_upper_bounds[c - 1];
if (lb > ub) {
// Unrounded bounds are inverted; this case is covered by
// ListInvertedBounds(). We don't want to depend on the order of calls of
// the two functions here so we exclude this case.
continue;
}
if (std::ceil(lb) <= std::floor(ub)) {
continue;
}
// We found a variable with empty integer bounds (that is lb <= ub but
// ceil(lb) > floor(ub)).
return ResultForIntegerInfeasible(
/*is_maximize=*/glp_get_obj_dir(problem_) == GLP_MAX,
/*bad_variable_id=*/variables_.ids[c - 1],
/*lb=*/lb, /*ub=*/ub);
}
return std::nullopt;
}

// Deal with empty integer bounds that result in inverted bounds due to bounds
// rounding.
{ // Limit scope of `result`.
std::optional<SolveResultProto> result = EmptyIntegerBoundsResult();
if (result.has_value()) {
RETURN_IF_ERROR(set_solve_time(result.value()));
return std::move(result).value();
}
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions