Skip to content

Commit

Permalink
polish
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Jul 24, 2024
1 parent 458e2a1 commit 30fe3c2
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 43 deletions.
2 changes: 1 addition & 1 deletion ortools/graph/bidirectional_dijkstra_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ TEST(BidirectionalDijkstraTest, RandomizedCorrectnessTest) {
&forward_graph, &forward_lengths);

// To print some debugging info in case the test fails.
auto print_arc_path = [&](const std::vector<int>& arc_path) -> std::string {
auto print_arc_path = [&](absl::Span<const int> arc_path) -> std::string {
if (arc_path.empty()) return "<EMPTY>";
std::string out = absl::StrCat(forward_graph.Tail(arc_path[0]));
double total_length = 0.0;
Expand Down
86 changes: 44 additions & 42 deletions ortools/linear_solver/java/LinearSolverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private void runLinearSolver(
objective.setCoefficient(x2, 6);
objective.setCoefficient(x3, 4);
objective.setMaximization();
assertEquals(6.0, objective.getCoefficient(x2), 1e-6);
assertThat(objective.getCoefficient(x2)).isWithin(1e-6).of(6.0);
assertTrue(objective.maximization());
assertFalse(objective.minimization());

Expand All @@ -102,7 +102,7 @@ private void runLinearSolver(
c1.setCoefficient(x1, 10);
c1.setCoefficient(x2, 4);
c1.setCoefficient(x3, 5);
assertEquals(4.0, c1.getCoefficient(x2), 1e-6);
assertThat(c1.getCoefficient(x2)).isWithin(1e-6).of(4.0);

final MPConstraint c2 = solver.makeConstraint(-1000, 300.0);
c2.setCoefficient(x1, 2);
Expand All @@ -111,15 +111,15 @@ private void runLinearSolver(

assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
if (integerVariables) {
assertEquals(732.0, objective.value(), NUM_TOLERANCE);
assertEquals(33.0, x1.solutionValue(), NUM_TOLERANCE);
assertEquals(67.0, x2.solutionValue(), NUM_TOLERANCE);
assertEquals(0.0, x3.solutionValue(), NUM_TOLERANCE);
assertThat(objective.value()).isWithin(NUM_TOLERANCE).of(732.0);
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(33.0);
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(67.0);
assertThat(x3.solutionValue()).isWithin(NUM_TOLERANCE).of(0.0);
} else {
assertEquals(733.333333, objective.value(), NUM_TOLERANCE);
assertEquals(33.333333, x1.solutionValue(), NUM_TOLERANCE);
assertEquals(66.666667, x2.solutionValue(), NUM_TOLERANCE);
assertEquals(0, x3.solutionValue(), NUM_TOLERANCE);
assertThat(objective.value()).isWithin(NUM_TOLERANCE).of(733.333333);
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(33.333333);
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(66.666667);
assertThat(x3.solutionValue()).isWithin(NUM_TOLERANCE).of(0);
}
}

Expand Down Expand Up @@ -181,42 +181,42 @@ private void runFirstLinearExample(MPSolver.OptimizationProblemType problemType)
// The problem has an optimal solution.;
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());

assertEquals(733.333333, objective.value(), NUM_TOLERANCE);
assertEquals(33.333333, x1.solutionValue(), NUM_TOLERANCE);
assertEquals(66.666667, x2.solutionValue(), NUM_TOLERANCE);
assertEquals(0, x3.solutionValue(), NUM_TOLERANCE);
assertThat(objective.value()).isWithin(NUM_TOLERANCE).of(733.333333);
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(33.333333);
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(66.666667);
assertThat(x3.solutionValue()).isWithin(NUM_TOLERANCE).of(0);

// c0 and c1 are binding;
final double[] activities = solver.computeConstraintActivities();
assertEquals(3, activities.length);
assertEquals(3.333333, c0.dualValue(), NUM_TOLERANCE);
assertEquals(0.666667, c1.dualValue(), NUM_TOLERANCE);
assertEquals(rhs0, activities[c0.index()], NUM_TOLERANCE);
assertEquals(rhs1, activities[c1.index()], NUM_TOLERANCE);
assertThat(c0.dualValue()).isWithin(NUM_TOLERANCE).of(3.333333);
assertThat(c1.dualValue()).isWithin(NUM_TOLERANCE).of(0.666667);
assertThat(activities[c0.index()]).isWithin(NUM_TOLERANCE).of(rhs0);
assertThat(activities[c1.index()]).isWithin(NUM_TOLERANCE).of(rhs1);
assertEquals(MPSolver.BasisStatus.AT_UPPER_BOUND, c0.basisStatus());
assertEquals(MPSolver.BasisStatus.AT_UPPER_BOUND, c1.basisStatus());
// c2 is not binding;
assertEquals(0.0, c2.dualValue(), NUM_TOLERANCE);
assertEquals(200.0, activities[c2.index()], NUM_TOLERANCE);
assertThat(c2.dualValue()).isWithin(NUM_TOLERANCE).of(0.0);
assertThat(activities[c2.index()]).isWithin(NUM_TOLERANCE).of(200.0);
assertEquals(MPSolver.BasisStatus.BASIC, c2.basisStatus());
// The optimum of the dual problem is equal to the optimum of the;
// primal problem.;
final double dualObjectiveValue = c0.dualValue() * rhs0 + c1.dualValue() * rhs1;
assertEquals(objective.value(), dualObjectiveValue, NUM_TOLERANCE);
assertThat(dualObjectiveValue).isWithin(NUM_TOLERANCE).of(objective.value());

// x1 and x2 are basic;
assertEquals(0.0, x1.reducedCost(), NUM_TOLERANCE);
assertEquals(0.0, x2.reducedCost(), NUM_TOLERANCE);
assertThat(x1.reducedCost()).isWithin(NUM_TOLERANCE).of(0.0);
assertThat(x2.reducedCost()).isWithin(NUM_TOLERANCE).of(0.0);
assertEquals(MPSolver.BasisStatus.BASIC, x1.basisStatus());
assertEquals(MPSolver.BasisStatus.BASIC, x2.basisStatus());
// x3 is non-basic;
final double x3ExpectedReducedCost =
(obj[2] - coef0[2] * c0.dualValue() - coef1[2] * c1.dualValue());
assertEquals(x3ExpectedReducedCost, x3.reducedCost(), NUM_TOLERANCE);
assertThat(x3.reducedCost()).isWithin(NUM_TOLERANCE).of(x3ExpectedReducedCost);
assertEquals(MPSolver.BasisStatus.AT_LOWER_BOUND, x3.basisStatus());

if (solver.problemType() == MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING) {
assertEquals(56.333333, solver.computeExactConditionNumber(), NUM_TOLERANCE);
assertThat(solver.computeExactConditionNumber()).isWithin(NUM_TOLERANCE).of(56.333333);
}
}

Expand Down Expand Up @@ -253,10 +253,12 @@ private void runFirstMIPExample(MPSolver.OptimizationProblemType problemType) {
// Check the solution.
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
final double optObjValue = 6.0;
assertEquals(optObjValue, solver.objective().value(), 1e-6);
assertEquals(optObjValue, solver.objective().bestBound(), 1e-6);
assertThat(solver.objective().value()).isWithin(1e-6).of(optObjValue);
assertThat(solver.objective().bestBound()).isWithin(1e-6).of(optObjValue);
final double optRowActivity = 18.0;
assertEquals(optRowActivity, solver.computeConstraintActivities()[ct.index()], NUM_TOLERANCE);
assertThat(solver.computeConstraintActivities()[ct.index()])
.isWithin(NUM_TOLERANCE)
.of(optRowActivity);
// BOP does not support nodes().
if (solver.problemType() != MPSolver.OptimizationProblemType.BOP_INTEGER_PROGRAMMING) {
assertThat(solver.nodes()).isAtLeast(0);
Expand Down Expand Up @@ -294,26 +296,26 @@ private void runSuccessiveObjectives(MPSolver.OptimizationProblemType problemTyp

// Check the solution.
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(10.0, x1.solutionValue(), NUM_TOLERANCE);
assertEquals(0.0, x2.solutionValue(), NUM_TOLERANCE);
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(10.0);
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(0.0);

objective.setCoefficient(x1, 0);
objective.setCoefficient(x2, 1);
objective.setOptimizationDirection(true);

// Check the solution
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(0.0, x1.solutionValue(), NUM_TOLERANCE);
assertEquals(5.0, x2.solutionValue(), NUM_TOLERANCE);
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(0.0);
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(5.0);

objective.setCoefficient(x1, -1);
objective.setCoefficient(x2, 0);
objective.setOptimizationDirection(false);

// Check the solution.
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(10.0, x1.solutionValue(), NUM_TOLERANCE);
assertEquals(0.0, x2.solutionValue(), NUM_TOLERANCE);
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(10.0);
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(0.0);
}

@Test
Expand Down Expand Up @@ -355,7 +357,7 @@ private void runObjectiveOffset(MPSolver.OptimizationProblemType problemType) {
objective.setOptimizationDirection(false);

assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(2.0 + objectiveOffset, objective.value(), 1e-6);
assertThat(objective.value()).isWithin(1e-6).of(2.0 + objectiveOffset);

// Offset is provided in several separate constants.
objective.setCoefficient(x1, 1.0);
Expand All @@ -364,15 +366,15 @@ private void runObjectiveOffset(MPSolver.OptimizationProblemType problemType) {
objective.setOffset(objectiveOffset + objective.offset());
objective.setOffset(1.0 + objective.offset());
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(2.0 + objectiveOffset, objective.value(), 1e-6);
assertThat(objective.value()).isWithin(1e-6).of(2.0 + objectiveOffset);

// Simple maximization.
objective.setCoefficient(x1, 1.0);
objective.setCoefficient(x2, 1.0);
objective.setOffset(objectiveOffset);
objective.setOptimizationDirection(true);
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(3.0 + objectiveOffset, objective.value(), 1e-6);
assertThat(objective.value()).isWithin(1e-6).of(3.0 + objectiveOffset);
}

@Test
Expand Down Expand Up @@ -418,7 +420,7 @@ public void testMPSolver_lazyConstraints() {
objective.setCoefficient(y, 1.0);
objective.setOptimizationDirection(true);
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
assertEquals(solver.objective().value(), 6.0, NUM_TOLERANCE);
assertThat(6.0).isWithin(NUM_TOLERANCE).of(solver.objective().value());
}

@Test
Expand Down Expand Up @@ -468,7 +470,7 @@ public void testMPsolver_createSolutionResponseProto() {
solver.solve();
final MPSolutionResponse response = solver.createSolutionResponseProto();
assertEquals(MPSolverResponseStatus.MPSOLVER_OPTIMAL, response.getStatus());
assertEquals(10.0, response.getObjectiveValue(), 1e-6);
assertThat(response.getObjectiveValue()).isWithin(1e-6).of(10.0);
}

@Test
Expand All @@ -494,7 +496,7 @@ public void testMPSolver_solveWithProto() {
.build();
final MPSolutionResponse response = MPSolver.solveWithProto(request);
assertEquals(MPSolverResponseStatus.MPSOLVER_OPTIMAL, response.getStatus());
assertEquals(10.0, response.getObjectiveValue(), 1e-6);
assertThat(response.getObjectiveValue()).isWithin(1e-6).of(10.0);
}

@Test
Expand Down Expand Up @@ -566,9 +568,9 @@ public void testMPSolver_setHint() {
assertEquals(2, hint.getVarIndexCount());
assertEquals(2, hint.getVarValueCount());
assertEquals(0, hint.getVarIndex(0));
assertEquals(5.0, hint.getVarValue(0), 1e-6);
assertThat(hint.getVarValue(0)).isWithin(1e-6).of(5.0);
assertEquals(1, hint.getVarIndex(1));
assertEquals(6.0, hint.getVarValue(1), 1e-6);
assertThat(hint.getVarValue(1)).isWithin(1e-6).of(6.0);
}

@Test
Expand Down
27 changes: 27 additions & 0 deletions ortools/linear_solver/proto_solver/sat_proto_solver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@ MPSolutionResponse InvalidParametersResponse(SolverLogger& logger,
return response;
}

MPSolutionResponse TimeLimitResponse(SolverLogger& logger) {
SOLVER_LOG(&logger, "Time limit reached in sat_solve_proto.\n");

// This is needed for our benchmark scripts.
if (logger.LoggingIsEnabled()) {
sat::CpSolverResponse cp_response;
cp_response.set_status(sat::CpSolverStatus::UNKNOWN);
SOLVER_LOG(&logger, CpSolverResponseStats(cp_response));
}

MPSolutionResponse response;
response.set_status(MPSolverResponseStatus::MPSOLVER_NOT_SOLVED);
response.set_status_str("Time limit reached in sat_solve_proto.");
return response;
}

} // namespace

MPSolutionResponse SatSolveProto(
Expand Down Expand Up @@ -201,6 +217,8 @@ MPSolutionResponse SatSolveProto(
params.set_max_time_in_seconds(request->solver_time_limit_seconds());
}

std::unique_ptr<TimeLimit> time_limit = TimeLimit::FromParameters(params);

// Model validation and delta handling.
MPSolutionResponse response;
std::optional<LazyMutableCopy<MPModelProto>> optional_model =
Expand Down Expand Up @@ -287,6 +305,9 @@ MPSolutionResponse SatSolveProto(
}
}

if (time_limit->LimitReached()) {
return TimeLimitResponse(logger);
}
// We need to do that before the automatic detection of integers.
RemoveNearZeroTerms(params, mp_model.get(), &logger);

Expand Down Expand Up @@ -369,6 +390,12 @@ MPSolutionResponse SatSolveProto(
const bool is_maximize = mp_model->maximize();
mp_model.reset();

params.set_max_time_in_seconds(time_limit->GetTimeLeft());
if (time_limit->GetDeterministicTimeLeft() !=
std::numeric_limits<double>::infinity()) {
params.set_max_deterministic_time(time_limit->GetDeterministicTimeLeft());
}

// Configure model.
sat::Model sat_model;
sat_model.Register<SolverLogger>(&logger);
Expand Down
6 changes: 6 additions & 0 deletions ortools/linear_solver/proto_solver/sat_solver_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "ortools/lp_data/lp_types.h"
#include "ortools/lp_data/proto_utils.h"
#include "ortools/util/logging.h"
#include "ortools/util/time_limit.h"

namespace operations_research {

Expand All @@ -47,6 +48,9 @@ glop::ProblemStatus ApplyMipPresolveSteps(
const bool hint_is_present = model->has_solution_hint();
const auto copy_of_hint = model->solution_hint();

std::unique_ptr<TimeLimit> time_limit =
TimeLimit::FromParameters(glop_params);

// TODO(user): Remove this back and forth conversion. We could convert
// the LinearProgram directly to a CpModelProto, or we could have a custom
// implementation of these presolve steps.
Expand Down Expand Up @@ -75,7 +79,9 @@ glop::ProblemStatus ApplyMipPresolveSteps(
ADD_LP_PREPROCESSOR(glop::UnconstrainedVariablePreprocessor);

for (int i = 0; i < lp_preprocessors.size(); ++i) {
if (time_limit->LimitReached()) break;
auto& preprocessor = lp_preprocessors[i];
preprocessor->SetTimeLimit(time_limit.get());
preprocessor->UseInMipContext();
const bool need_postsolve = preprocessor->Run(&lp);
names[i].resize(header.size(), ' '); // padding.
Expand Down

0 comments on commit 30fe3c2

Please sign in to comment.