661 lines
27 KiB
Java
661 lines
27 KiB
Java
// Copyright 2010-2025 Google LLC
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package com.google.ortools.linearsolver;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertFalse;
|
|
import static org.junit.Assert.assertNotNull;
|
|
import static org.junit.Assert.assertTrue;
|
|
|
|
import com.google.ortools.Loader;
|
|
import com.google.ortools.linearsolver.MPConstraintProto;
|
|
import com.google.ortools.linearsolver.MPModelProto;
|
|
import com.google.ortools.linearsolver.MPModelRequest;
|
|
import com.google.ortools.linearsolver.MPSolutionResponse;
|
|
import com.google.ortools.linearsolver.MPSolverResponseStatus;
|
|
import com.google.ortools.linearsolver.MPVariableProto;
|
|
import com.google.ortools.linearsolver.PartialVariableAssignment;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.JUnit4;
|
|
|
|
/** Test the Linear Solver java interface. */
|
|
@RunWith(JUnit4.class)
|
|
public final class LinearSolverTest {
|
|
// Numerical tolerance for checking primal, dual, objective values
|
|
// and other values.
|
|
private static final double NUM_TOLERANCE = 1e-5;
|
|
|
|
@SuppressWarnings("EmptyMethods")
|
|
@Before
|
|
public void setUp() {
|
|
Loader.loadNativeLibraries();
|
|
}
|
|
|
|
private void runBasicCtor(MPSolver.OptimizationProblemType solverType) {
|
|
if (!MPSolver.supportsProblemType(solverType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("testBasicCtor", solverType);
|
|
assertNotNull(solver);
|
|
solver.solve();
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_basicCtor() {
|
|
runBasicCtor(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING);
|
|
runBasicCtor(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING);
|
|
runBasicCtor(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING);
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_destructor() {
|
|
final MPSolver solver =
|
|
new MPSolver("testDestructor", MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING);
|
|
assertNotNull(solver);
|
|
solver.delete();
|
|
}
|
|
|
|
private void runLinearSolver(
|
|
MPSolver.OptimizationProblemType problemType, boolean integerVariables) {
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("Solver", problemType);
|
|
assertNotNull(solver);
|
|
|
|
final double infinity = MPSolver.infinity();
|
|
final MPVariable x1 = solver.makeNumVar(0.0, infinity, "x1");
|
|
final MPVariable x2 = solver.makeNumVar(0.0, infinity, "x2");
|
|
final MPVariable x3 = solver.makeNumVar(0.0, infinity, "x3");
|
|
if (integerVariables) {
|
|
x1.setInteger(true);
|
|
x2.setInteger(true);
|
|
x3.setInteger(true);
|
|
}
|
|
assertEquals(3, solver.numVariables());
|
|
|
|
final MPObjective objective = solver.objective();
|
|
objective.setCoefficient(x1, 10);
|
|
objective.setCoefficient(x2, 6);
|
|
objective.setCoefficient(x3, 4);
|
|
objective.setMaximization();
|
|
assertThat(objective.getCoefficient(x2)).isWithin(1e-6).of(6.0);
|
|
assertTrue(objective.maximization());
|
|
assertFalse(objective.minimization());
|
|
|
|
final MPConstraint c0 = solver.makeConstraint(-1000, 100.0);
|
|
c0.setCoefficient(x1, 1);
|
|
c0.setCoefficient(x2, 1);
|
|
c0.setCoefficient(x3, 1);
|
|
|
|
final MPConstraint c1 = solver.makeConstraint(-1000, 600.0);
|
|
c1.setCoefficient(x1, 10);
|
|
c1.setCoefficient(x2, 4);
|
|
c1.setCoefficient(x3, 5);
|
|
assertThat(c1.getCoefficient(x2)).isWithin(1e-6).of(4.0);
|
|
|
|
final MPConstraint c2 = solver.makeConstraint(-1000, 300.0);
|
|
c2.setCoefficient(x1, 2);
|
|
c2.setCoefficient(x2, 2);
|
|
c2.setCoefficient(x3, 6);
|
|
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
if (integerVariables) {
|
|
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 {
|
|
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);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_linearSolver() {
|
|
runLinearSolver(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING, false);
|
|
runLinearSolver(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING, false);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runLinearSolver(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING, false);
|
|
|
|
runLinearSolver(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING, true);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runLinearSolver(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING, true);
|
|
runLinearSolver(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING, true);
|
|
}
|
|
|
|
private void runFirstLinearExample(MPSolver.OptimizationProblemType problemType) {
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("Solver", problemType);
|
|
assertNotNull(solver);
|
|
|
|
final MPVariable x1 = solver.makeNumVar(0.0, Double.POSITIVE_INFINITY, "x1");
|
|
final MPVariable x2 = solver.makeNumVar(0.0, Double.POSITIVE_INFINITY, "x2");
|
|
final MPVariable x3 = solver.makeNumVar(0.0, Double.POSITIVE_INFINITY, "x3");
|
|
assertEquals(3, solver.numVariables());
|
|
|
|
final double[] obj = {10.0, 6.0, 4.0};
|
|
final MPObjective objective = solver.objective();
|
|
objective.setCoefficient(x1, obj[0]);
|
|
objective.setCoefficient(x2, obj[1]);
|
|
objective.setCoefficient(x3, obj[2]);
|
|
objective.setMaximization();
|
|
|
|
final double rhs0 = 100.0;
|
|
final MPConstraint c0 = solver.makeConstraint(-Double.POSITIVE_INFINITY, rhs0, "c0");
|
|
final double[] coef0 = {1.0, 1.0, 1.0};
|
|
c0.setCoefficient(x1, coef0[0]);
|
|
c0.setCoefficient(x2, coef0[1]);
|
|
c0.setCoefficient(x3, coef0[2]);
|
|
final double rhs1 = 600.0;
|
|
final MPConstraint c1 = solver.makeConstraint(-Double.POSITIVE_INFINITY, rhs1, "c1");
|
|
final double[] coef1 = {10.0, 4.0, 5.0};
|
|
c1.setCoefficient(x1, coef1[0]);
|
|
c1.setCoefficient(x2, coef1[1]);
|
|
c1.setCoefficient(x3, coef1[2]);
|
|
final double rhs2 = 300.0;
|
|
final MPConstraint c2 = solver.makeConstraint(-Double.POSITIVE_INFINITY, rhs2);
|
|
final double[] coef2 = {2.0, 2.0, 6.0};
|
|
c2.setCoefficient(x1, coef2[0]);
|
|
c2.setCoefficient(x2, coef2[1]);
|
|
c2.setCoefficient(x3, coef2[2]);
|
|
assertEquals(3, solver.numConstraints());
|
|
assertEquals("c0", c0.name());
|
|
assertEquals("c1", c1.name());
|
|
assertEquals("auto_c_000000002", c2.name());
|
|
|
|
// The problem has an optimal solution.;
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
|
|
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);
|
|
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;
|
|
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;
|
|
assertThat(dualObjectiveValue).isWithin(NUM_TOLERANCE).of(objective.value());
|
|
|
|
// x1 and x2 are basic;
|
|
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());
|
|
assertThat(x3.reducedCost()).isWithin(NUM_TOLERANCE).of(x3ExpectedReducedCost);
|
|
assertEquals(MPSolver.BasisStatus.AT_LOWER_BOUND, x3.basisStatus());
|
|
|
|
if (solver.problemType() == MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING) {
|
|
assertThat(solver.computeExactConditionNumber()).isWithin(NUM_TOLERANCE).of(56.333333);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_firstLinearExample() {
|
|
runFirstLinearExample(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING);
|
|
runFirstLinearExample(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runFirstLinearExample(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING);
|
|
runFirstLinearExample(MPSolver.OptimizationProblemType.GUROBI_LINEAR_PROGRAMMING);
|
|
}
|
|
|
|
private void runFirstMIPExample(MPSolver.OptimizationProblemType problemType) {
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("Solver", problemType);
|
|
assertNotNull(solver);
|
|
|
|
// Integer variables shouldn't have infinite bounds, nor really large bounds:
|
|
// it can make your solver behave erratically. If you have integer variables
|
|
// with a truly large dynamic range you should consider making it non-integer.
|
|
final double upperBound = 1000;
|
|
final MPVariable x1 = solver.makeIntVar(0.0, upperBound, "x1");
|
|
final MPVariable x2 = solver.makeIntVar(0.0, upperBound, "x2");
|
|
|
|
solver.objective().setCoefficient(x1, 1);
|
|
solver.objective().setCoefficient(x2, 2);
|
|
|
|
final MPConstraint ct = solver.makeConstraint(17, Double.POSITIVE_INFINITY);
|
|
ct.setCoefficient(x1, 3);
|
|
ct.setCoefficient(x2, 2);
|
|
|
|
// Check the solution.
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
final double optObjValue = 6.0;
|
|
assertThat(solver.objective().value()).isWithin(1e-6).of(optObjValue);
|
|
assertThat(solver.objective().bestBound()).isWithin(1e-6).of(optObjValue);
|
|
final double optRowActivity = 18.0;
|
|
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);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_firstMIPExample() {
|
|
runFirstMIPExample(MPSolver.OptimizationProblemType.BOP_INTEGER_PROGRAMMING);
|
|
runFirstMIPExample(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runFirstMIPExample(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING);
|
|
runFirstMIPExample(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING);
|
|
runFirstMIPExample(MPSolver.OptimizationProblemType.SAT_INTEGER_PROGRAMMING);
|
|
runFirstMIPExample(MPSolver.OptimizationProblemType.GUROBI_MIXED_INTEGER_PROGRAMMING);
|
|
}
|
|
|
|
private void runSuccessiveObjectives(MPSolver.OptimizationProblemType problemType) {
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("Solver", problemType);
|
|
assertNotNull(solver);
|
|
|
|
final MPVariable x1 = solver.makeNumVar(0, 10, "var1");
|
|
final MPVariable x2 = solver.makeNumVar(0, 10, "var2");
|
|
final MPConstraint ct = solver.makeConstraint(0, 10);
|
|
ct.setCoefficient(x1, 1);
|
|
ct.setCoefficient(x2, 2);
|
|
|
|
final MPObjective objective = solver.objective();
|
|
objective.setCoefficient(x1, 1);
|
|
objective.setCoefficient(x2, 0);
|
|
objective.setOptimizationDirection(true);
|
|
|
|
// Check the solution.
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
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());
|
|
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());
|
|
assertThat(x1.solutionValue()).isWithin(NUM_TOLERANCE).of(10.0);
|
|
assertThat(x2.solutionValue()).isWithin(NUM_TOLERANCE).of(0.0);
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_successiveObjectives() {
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING);
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING);
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.GUROBI_LINEAR_PROGRAMMING);
|
|
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING);
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING);
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.SAT_INTEGER_PROGRAMMING);
|
|
runSuccessiveObjectives(MPSolver.OptimizationProblemType.GUROBI_MIXED_INTEGER_PROGRAMMING);
|
|
}
|
|
|
|
private void runObjectiveOffset(MPSolver.OptimizationProblemType problemType) {
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("Solver", problemType);
|
|
assertNotNull(solver);
|
|
|
|
final MPVariable x1 = solver.makeIntVar(1.0, 10.0, "x1");
|
|
final MPVariable x2 = solver.makeIntVar(1.0, 10.0, "x2");
|
|
|
|
final MPConstraint ct = solver.makeConstraint(0, 4.0);
|
|
ct.setCoefficient(x1, 1);
|
|
ct.setCoefficient(x2, 2);
|
|
|
|
final double objectiveOffset = 10.0;
|
|
// Simple minimization.
|
|
final MPObjective objective = solver.objective();
|
|
objective.setCoefficient(x1, 1.0);
|
|
objective.setCoefficient(x2, 1.0);
|
|
objective.setOffset(objectiveOffset);
|
|
objective.setOptimizationDirection(false);
|
|
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
assertThat(objective.value()).isWithin(1e-6).of(2.0 + objectiveOffset);
|
|
|
|
// Offset is provided in several separate constants.
|
|
objective.setCoefficient(x1, 1.0);
|
|
objective.setCoefficient(x2, 1.0);
|
|
objective.setOffset(-1.0);
|
|
objective.setOffset(objectiveOffset + objective.offset());
|
|
objective.setOffset(1.0 + objective.offset());
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
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());
|
|
assertThat(objective.value()).isWithin(1e-6).of(3.0 + objectiveOffset);
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_objectiveOffset() {
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING);
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runObjectiveOffset(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING);
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.GUROBI_LINEAR_PROGRAMMING);
|
|
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING);
|
|
// TODO(b/312134897): Uncomment this.
|
|
// runObjectiveOffset(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING);
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING);
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.SAT_INTEGER_PROGRAMMING);
|
|
runObjectiveOffset(MPSolver.OptimizationProblemType.GUROBI_MIXED_INTEGER_PROGRAMMING);
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_lazyConstraints() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("testLazyConstraints", problemType);
|
|
assertNotNull(solver);
|
|
|
|
final double infinity = MPSolver.infinity();
|
|
final MPVariable x = solver.makeIntVar(0, infinity, "x");
|
|
final MPVariable y = solver.makeIntVar(0, infinity, "y");
|
|
final MPConstraint ct1 = solver.makeConstraint(0, 10.0);
|
|
ct1.setCoefficient(x, 2.0);
|
|
ct1.setCoefficient(y, 1.0);
|
|
final MPConstraint ct2 = solver.makeConstraint(0, 10.0);
|
|
ct2.setCoefficient(x, 1.0);
|
|
ct2.setCoefficient(y, 2.0);
|
|
ct2.setIsLazy(true);
|
|
assertFalse(ct1.isLazy());
|
|
assertTrue(ct2.isLazy());
|
|
final MPObjective objective = solver.objective();
|
|
objective.setCoefficient(x, 1.0);
|
|
objective.setCoefficient(y, 1.0);
|
|
objective.setOptimizationDirection(true);
|
|
assertEquals(MPSolver.ResultStatus.OPTIMAL, solver.solve());
|
|
assertThat(6.0).isWithin(NUM_TOLERANCE).of(solver.objective().value());
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_sameConstraintName() {
|
|
MPSolver solver = MPSolver.createSolver("GLOP");
|
|
assertNotNull(solver);
|
|
boolean success = true;
|
|
solver.makeConstraint("my_const_name");
|
|
try {
|
|
solver.makeConstraint("my_const_name");
|
|
} catch (Throwable e) {
|
|
System.out.println(e);
|
|
success = false;
|
|
}
|
|
assertTrue(success);
|
|
}
|
|
@Test
|
|
public void testMPSolver_exportModelToProto() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("testExportModelToProto", problemType);
|
|
assertNotNull(solver);
|
|
solver.makeNumVar(0.0, 10.0, "x1");
|
|
solver.makeConstraint(0.0, 0.0);
|
|
solver.objective().setOptimizationDirection(true);
|
|
final MPModelProto model = solver.exportModelToProto();
|
|
assertEquals(1, model.getVariableCount());
|
|
assertEquals(1, model.getConstraintCount());
|
|
assertTrue(model.getMaximize());
|
|
}
|
|
|
|
@Test
|
|
public void testMPsolver_createSolutionResponseProto() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("testCreateSolutionResponseProto", problemType);
|
|
assertNotNull(solver);
|
|
final MPVariable x1 = solver.makeNumVar(0.0, 10.0, "x1");
|
|
solver.objective().setCoefficient(x1, 1.0);
|
|
solver.objective().setOptimizationDirection(true);
|
|
solver.solve();
|
|
final MPSolutionResponse response = solver.createSolutionResponseProto();
|
|
assertEquals(MPSolverResponseStatus.MPSOLVER_OPTIMAL, response.getStatus());
|
|
assertThat(response.getObjectiveValue()).isWithin(1e-6).of(10.0);
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_solveWithProto() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPModelProto.Builder modelBuilder = MPModelProto.newBuilder().setMaximize(true);
|
|
final MPVariableProto variable = MPVariableProto.newBuilder()
|
|
.setLowerBound(0.0)
|
|
.setUpperBound(10.0)
|
|
.setName("x1")
|
|
.setIsInteger(false)
|
|
.setObjectiveCoefficient(1.0)
|
|
.build();
|
|
modelBuilder.addVariable(variable);
|
|
final MPModelRequest request =
|
|
MPModelRequest.newBuilder()
|
|
.setModel(modelBuilder.build())
|
|
.setSolverType(MPModelRequest.SolverType.GLOP_LINEAR_PROGRAMMING)
|
|
.build();
|
|
final MPSolutionResponse response = MPSolver.solveWithProto(request);
|
|
assertEquals(MPSolverResponseStatus.MPSOLVER_OPTIMAL, response.getStatus());
|
|
assertThat(response.getObjectiveValue()).isWithin(1e-6).of(10.0);
|
|
}
|
|
|
|
@Test
|
|
public void testModelExport() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("tesModelExport", problemType);
|
|
assertNotNull(solver);
|
|
final double infinity = MPSolver.infinity();
|
|
// x1, x2 and x3 are continuous non-negative variables.
|
|
final MPVariable x1 = solver.makeNumVar(0.0, infinity, "x1");
|
|
|
|
// Maximize 10 * x1.
|
|
solver.objective().setCoefficient(x1, 10);
|
|
solver.objective().setMinimization();
|
|
|
|
// 5 * x1 <= 30.
|
|
final MPConstraint c0 = solver.makeConstraint(-infinity, 100.0);
|
|
c0.setCoefficient(x1, 5);
|
|
|
|
String out = solver.exportModelAsLpFormat(/* obfuscate= */ true);
|
|
assertThat(out).isNotEmpty();
|
|
out = solver.exportModelAsMpsFormat(/* fixed_format= */ true, /* obfuscate= */ true);
|
|
assertThat(out).isNotEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_wrongModelExport() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("testWrongModelExport", problemType);
|
|
assertNotNull(solver);
|
|
// Test that forbidden names are renamed.
|
|
solver.makeBoolVar("<-%$#!&~-+ ⌂"); // Some illegal name.
|
|
String out = solver.exportModelAsLpFormat(/* obfuscate= */ false);
|
|
assertThat(out).isNotEmpty();
|
|
out = solver.exportModelAsMpsFormat(/* fixed_format= */ true, /* obfuscate= */ true);
|
|
assertThat(out).isNotEmpty();
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_setHint() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("testSetHint", problemType);
|
|
assertNotNull(solver);
|
|
final MPVariable[] variables = {
|
|
solver.makeNumVar(0.0, 10.0, "x1"), solver.makeNumVar(0.0, 10.0, "x2")};
|
|
final double[] values = {5.0, 6.0};
|
|
solver.setHint(variables, values);
|
|
|
|
final MPModelProto model = solver.exportModelToProto();
|
|
final PartialVariableAssignment hint = model.getSolutionHint();
|
|
assertEquals(2, hint.getVarIndexCount());
|
|
assertEquals(2, hint.getVarValueCount());
|
|
assertEquals(0, hint.getVarIndex(0));
|
|
assertThat(hint.getVarValue(0)).isWithin(1e-6).of(5.0);
|
|
assertEquals(1, hint.getVarIndex(1));
|
|
assertThat(hint.getVarValue(1)).isWithin(1e-6).of(6.0);
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_issue132() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("CoinError", problemType);
|
|
assertNotNull(solver);
|
|
final double infinity = MPSolver.infinity();
|
|
final MPVariable x0 = solver.makeNumVar(0.0, 1.0, "x0");
|
|
final MPVariable x1 = solver.makeNumVar(0.0, 0.3, "x1");
|
|
final MPVariable x2 = solver.makeNumVar(0.0, 0.3, "x2");
|
|
final MPVariable x3 = solver.makeNumVar(-infinity, infinity, "x3");
|
|
|
|
final MPObjective obj = solver.objective();
|
|
obj.setCoefficient(x1, 2.655523);
|
|
obj.setCoefficient(x2, -2.70917);
|
|
obj.setCoefficient(x3, 1);
|
|
obj.setMaximization();
|
|
|
|
final MPConstraint c0 = solver.makeConstraint(-infinity, 0.302499);
|
|
c0.setCoefficient(x3, 1);
|
|
c0.setCoefficient(x0, -3.484345);
|
|
|
|
final MPConstraint c1 = solver.makeConstraint(-infinity, 0.507194);
|
|
c1.setCoefficient(x3, 1);
|
|
c1.setCoefficient(x0, -3.074807);
|
|
|
|
final MPConstraint c2 = solver.makeConstraint(0.594, 0.594);
|
|
c2.setCoefficient(x0, 1);
|
|
c2.setCoefficient(x1, 1.01);
|
|
c2.setCoefficient(x2, -0.99);
|
|
|
|
System.out.println("Number of variables = " + solver.numVariables());
|
|
System.out.println("Number of constraints = " + solver.numConstraints());
|
|
|
|
solver.enableOutput();
|
|
System.out.println(solver.exportModelAsLpFormat(/* obfuscate= */ false));
|
|
System.out.println(solver.solve());
|
|
}
|
|
|
|
@Test
|
|
public void testMPSolver_setHintAndSolverGetters() {
|
|
final MPSolver.OptimizationProblemType problemType =
|
|
MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING;
|
|
if (!MPSolver.supportsProblemType(problemType)) {
|
|
return;
|
|
}
|
|
final MPSolver solver = new MPSolver("glop", problemType);
|
|
assertNotNull(solver);
|
|
|
|
// x and y are continuous non-negative variables.
|
|
final MPVariable x = solver.makeIntVar(0.0, Double.POSITIVE_INFINITY, "x");
|
|
final MPVariable y = solver.makeIntVar(0.0, Double.POSITIVE_INFINITY, "y");
|
|
|
|
// Objectif function: Maximize x + 10 * y.
|
|
final MPObjective objective = solver.objective();
|
|
objective.setCoefficient(x, 1);
|
|
objective.setCoefficient(y, 10);
|
|
objective.setMaximization();
|
|
|
|
// x + 7 * y <= 17.5.
|
|
final MPConstraint c0 = solver.makeConstraint(-Double.POSITIVE_INFINITY, 17.5, "c0");
|
|
c0.setCoefficient(x, 1);
|
|
c0.setCoefficient(y, 7);
|
|
|
|
// x <= 3.5.
|
|
final MPConstraint c1 = solver.makeConstraint(-Double.POSITIVE_INFINITY, 3.5, "c1");
|
|
c1.setCoefficient(x, 1);
|
|
c1.setCoefficient(y, 0);
|
|
|
|
// Test solver getters.
|
|
final MPVariable[] variables = solver.variables();
|
|
assertThat(variables).hasLength(2);
|
|
final MPConstraint[] constraints = solver.constraints();
|
|
assertThat(constraints).hasLength(2);
|
|
|
|
// Test API compiles.
|
|
solver.setHint(variables, new double[] {2.0, 3.0});
|
|
assertEquals("y", variables[1].name());
|
|
assertEquals("c0", constraints[0].name());
|
|
// TODO(user): Add API to query the hint.
|
|
|
|
assertFalse(solver.setNumThreads(4));
|
|
}
|
|
}
|