2025-01-10 11:33:35 +01:00
|
|
|
// Copyright 2010-2025 Google LLC
|
2022-04-04 15:08:29 +02:00
|
|
|
// 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.modelbuilder;
|
|
|
|
|
|
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
|
|
|
|
|
|
|
|
import com.google.ortools.Loader;
|
2022-09-09 16:49:24 +02:00
|
|
|
import java.time.Duration;
|
2022-04-04 15:08:29 +02:00
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
|
|
|
|
|
public final class ModelBuilderTest {
|
|
|
|
|
@BeforeEach
|
|
|
|
|
public void setUp() {
|
|
|
|
|
Loader.loadNativeLibraries();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-05 12:41:22 +01:00
|
|
|
@Test
|
|
|
|
|
public void testEnforcedLinearApi() {
|
|
|
|
|
ModelBuilder model = new ModelBuilder();
|
|
|
|
|
model.setName("minimal enforced linear test");
|
|
|
|
|
double infinity = Double.POSITIVE_INFINITY;
|
|
|
|
|
Variable x = model.newNumVar(0.0, infinity, "x");
|
|
|
|
|
Variable y = model.newNumVar(0.0, infinity, "y");
|
|
|
|
|
Variable z = model.newBoolVar("z");
|
|
|
|
|
|
|
|
|
|
assertThat(model.numVariables()).isEqualTo(3);
|
|
|
|
|
|
|
|
|
|
EnforcedLinearConstraint c0 = model.addEnforcedGreaterOrEqual(
|
|
|
|
|
LinearExpr.newBuilder().add(x).addTerm(y, 2.0), 10.0, z, false);
|
|
|
|
|
assertThat(c0.getLowerBound()).isEqualTo(10.0);
|
|
|
|
|
assertThat(c0.getIndicatorVariable().getIndex()).isEqualTo(z.getIndex());
|
|
|
|
|
assertThat(c0.getIndicatorValue()).isFalse();
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-04 15:08:29 +02:00
|
|
|
@Test
|
|
|
|
|
public void runMinimalLinearExample_ok() {
|
2023-11-05 12:41:22 +01:00
|
|
|
final String name = "minimal_linear_example";
|
2022-04-04 15:08:29 +02:00
|
|
|
ModelBuilder model = new ModelBuilder();
|
2023-11-05 12:41:22 +01:00
|
|
|
model.setName(name);
|
2022-09-09 16:49:24 +02:00
|
|
|
double infinity = Double.POSITIVE_INFINITY;
|
2022-04-04 15:08:29 +02:00
|
|
|
Variable x1 = model.newNumVar(0.0, infinity, "x1");
|
|
|
|
|
Variable x2 = model.newNumVar(0.0, infinity, "x2");
|
|
|
|
|
Variable x3 = model.newNumVar(0.0, infinity, "x3");
|
|
|
|
|
|
|
|
|
|
assertThat(model.numVariables()).isEqualTo(3);
|
|
|
|
|
assertThat(x1.getIntegrality()).isFalse();
|
|
|
|
|
assertThat(x1.getLowerBound()).isEqualTo(0.0);
|
|
|
|
|
assertThat(x2.getUpperBound()).isEqualTo(infinity);
|
|
|
|
|
x1.setLowerBound(1.0);
|
|
|
|
|
assertThat(x1.getLowerBound()).isEqualTo(1.0);
|
|
|
|
|
|
|
|
|
|
LinearConstraint c0 = model.addLessOrEqual(LinearExpr.sum(new Variable[] {x1, x2, x3}), 100.0);
|
|
|
|
|
assertThat(c0.getUpperBound()).isEqualTo(100.0);
|
|
|
|
|
LinearConstraint c1 =
|
|
|
|
|
model
|
|
|
|
|
.addLessOrEqual(
|
|
|
|
|
LinearExpr.newBuilder().addTerm(x1, 10.0).addTerm(x2, 4.0).addTerm(x3, 5.0), 600.0)
|
|
|
|
|
.withName("c1");
|
|
|
|
|
assertThat(c1.getName()).isEqualTo("c1");
|
|
|
|
|
LinearConstraint c2 = model.addLessOrEqual(
|
|
|
|
|
LinearExpr.newBuilder().addTerm(x1, 2.0).addTerm(x2, 2.0).addTerm(x3, 6.0), 300.0);
|
|
|
|
|
assertThat(c2.getUpperBound()).isEqualTo(300.0);
|
|
|
|
|
|
|
|
|
|
model.maximize(
|
|
|
|
|
LinearExpr.weightedSum(new Variable[] {x1, x2, x3}, new double[] {10.0, 6, 4.0}));
|
|
|
|
|
assertThat(x3.getObjectiveCoefficient()).isEqualTo(4.0);
|
|
|
|
|
assertThat(model.getObjectiveOffset()).isEqualTo(0.0);
|
|
|
|
|
model.setObjectiveOffset(-5.5);
|
|
|
|
|
assertThat(model.getObjectiveOffset()).isEqualTo(-5.5);
|
|
|
|
|
|
|
|
|
|
ModelSolver solver = new ModelSolver("glop");
|
|
|
|
|
assertThat(solver.solverIsSupported()).isTrue();
|
2022-09-09 16:49:24 +02:00
|
|
|
solver.setTimeLimit(Duration.ofSeconds(1));
|
2022-04-04 15:08:29 +02:00
|
|
|
assertThat(solver.solve(model)).isEqualTo(SolveStatus.OPTIMAL);
|
|
|
|
|
|
|
|
|
|
assertThat(solver.getObjectiveValue())
|
|
|
|
|
.isWithin(1e-5)
|
|
|
|
|
.of(733.333333 + model.getObjectiveOffset());
|
|
|
|
|
assertThat(solver.getValue(x1)).isWithin(1e-5).of(33.333333);
|
|
|
|
|
assertThat(solver.getValue(x2)).isWithin(1e-5).of(66.6666673);
|
|
|
|
|
assertThat(solver.getValue(x3)).isWithin(1e-5).of(0.0);
|
|
|
|
|
|
|
|
|
|
double dualObjectiveValue = solver.getDualValue(c0) * c0.getUpperBound()
|
|
|
|
|
+ solver.getDualValue(c1) * c1.getUpperBound()
|
|
|
|
|
+ solver.getDualValue(c2) * c2.getUpperBound() + model.getObjectiveOffset();
|
|
|
|
|
assertThat(solver.getObjectiveValue()).isWithin(1e-5).of(dualObjectiveValue);
|
|
|
|
|
|
|
|
|
|
assertThat(solver.getReducedCost(x1)).isWithin(1e-5).of(0.0);
|
|
|
|
|
assertThat(solver.getReducedCost(x2)).isWithin(1e-5).of(0.0);
|
|
|
|
|
assertThat(solver.getReducedCost(x3))
|
|
|
|
|
.isWithin(1e-5)
|
|
|
|
|
.of(4.0 - 1.0 * solver.getDualValue(c0) - 5.0 * solver.getDualValue(c1));
|
|
|
|
|
|
2023-03-03 12:12:37 +04:00
|
|
|
assertThat(solver.getActivity(c0)).isWithin(1e-5).of(100.0);
|
|
|
|
|
assertThat(solver.getActivity(c1)).isWithin(1e-5).of(600.0);
|
|
|
|
|
assertThat(solver.getActivity(c2)).isWithin(1e-5).of(200.0);
|
|
|
|
|
|
2023-11-05 12:41:22 +01:00
|
|
|
assertThat(model.exportToLpString(false)).contains(name);
|
|
|
|
|
assertThat(model.exportToMpsString(false)).contains(name);
|
2022-04-04 15:08:29 +02:00
|
|
|
}
|
2022-09-09 16:49:24 +02:00
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void importFromMpsString() {
|
|
|
|
|
ModelBuilder model = new ModelBuilder();
|
|
|
|
|
String mpsData = "* Generated by MPModelProtoExporter\n"
|
|
|
|
|
+ "* Name : SupportedMaximizationProblem\n"
|
|
|
|
|
+ "* Format : Free\n"
|
|
|
|
|
+ "* Constraints : 0\n"
|
|
|
|
|
+ "* Variables : 1\n"
|
|
|
|
|
+ "* Binary : 0\n"
|
|
|
|
|
+ "* Integer : 0\n"
|
|
|
|
|
+ "* Continuous : 1\n"
|
|
|
|
|
+ "NAME SupportedMaximizationProblem\n"
|
|
|
|
|
+ "OBJSENSE\n"
|
|
|
|
|
+ " MAX\n"
|
|
|
|
|
+ "ROWS\n"
|
|
|
|
|
+ " N COST\n"
|
|
|
|
|
+ "COLUMNS\n"
|
|
|
|
|
+ " X_ONE COST 1\n"
|
|
|
|
|
+ "BOUNDS\n"
|
|
|
|
|
+ " UP BOUND X_ONE 4\n"
|
|
|
|
|
+ "ENDATA";
|
|
|
|
|
assertThat(model.importFromMpsString(mpsData)).isTrue();
|
|
|
|
|
assertThat(model.getName()).isEqualTo("SupportedMaximizationProblem");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void importFromLpString() {
|
|
|
|
|
ModelBuilder model = new ModelBuilder();
|
|
|
|
|
String lpData = "min: x + y;\n"
|
|
|
|
|
+ "bin: b1, b2, b3;\n"
|
|
|
|
|
+ "1 <= x <= 42;\n"
|
|
|
|
|
+ "constraint_num1: 5 b1 + 3b2 + x <= 7;\n"
|
|
|
|
|
+ "4 y + b2 - 3 b3 <= 2;\n"
|
|
|
|
|
+ "constraint_num2: -4 b1 + b2 - 3 z <= -2;\n";
|
|
|
|
|
assertThat(model.importFromLpString(lpData)).isTrue();
|
|
|
|
|
assertThat(model.numVariables()).isEqualTo(6);
|
|
|
|
|
assertThat(model.numConstraints()).isEqualTo(3);
|
|
|
|
|
assertThat(model.varFromIndex(0).getLowerBound()).isEqualTo(1.0);
|
|
|
|
|
assertThat(model.varFromIndex(0).getUpperBound()).isEqualTo(42.0);
|
|
|
|
|
assertThat(model.varFromIndex(0).getName()).isEqualTo("x");
|
|
|
|
|
}
|
2022-04-04 15:08:29 +02:00
|
|
|
}
|