Java Reference

Java Reference

CpSolverTest.java
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 package com.google.ortools.sat;
15 
16 import static com.google.common.truth.Truth.assertThat;
17 import static org.junit.jupiter.api.Assertions.assertEquals;
18 import static org.junit.jupiter.api.Assertions.assertNotNull;
19 
20 import com.google.ortools.Loader;
21 import com.google.ortools.sat.CpSolverStatus;
22 import com.google.ortools.util.Domain;
23 import java.util.function.Consumer;
24 import org.junit.jupiter.api.BeforeEach;
25 import org.junit.jupiter.api.Test;
26 
28 public final class CpSolverTest {
29  @BeforeEach
30  public void setUp() {
32  }
33 
34  static class SolutionCounter extends CpSolverSolutionCallback {
35  public SolutionCounter() {}
36 
37  @Override
38  public void onSolutionCallback() {
39  solutionCount++;
40  }
41 
42  private int solutionCount;
43 
44  public int getSolutionCount() {
45  return solutionCount;
46  }
47  }
48 
49  static class LogToString {
50  public LogToString() {
51  logBuilder = new StringBuilder();
52  }
53 
54  public void newMessage(String message) {
55  logBuilder.append(message).append("\n");
56  }
57 
58  private final StringBuilder logBuilder;
59 
60  public String getLog() {
61  return logBuilder.toString();
62  }
63  }
64 
65  @Test
66  public void testCpSolver_solve() throws Exception {
67  System.out.println("testCpSolver_solve");
68  final CpModel model = new CpModel();
69  assertNotNull(model);
70  // Creates the variables.
71  int numVals = 3;
72 
73  final IntVar x = model.newIntVar(0, numVals - 1, "x");
74  final IntVar y = model.newIntVar(0, numVals - 1, "y");
75  // Creates the constraints.
76  model.addDifferent(x, y);
77 
78  // Creates a solver and solves the model.
79  final CpSolver solver = new CpSolver();
80  assertNotNull(solver);
81  final CpSolverStatus status = solver.solve(model);
82 
83  assertThat(status).isEqualTo(CpSolverStatus.OPTIMAL);
84  assertThat(solver.value(x)).isNotEqualTo(solver.value(y));
85  final String stats = solver.responseStats();
86  assertThat(stats).isNotEmpty();
87  }
88 
89  @Test
90  public void testCpSolver_invalidModel() throws Exception {
91  System.out.println("testCpSolver_invalidModel");
92  final CpModel model = new CpModel();
93  assertNotNull(model);
94  // Creates the variables.
95  int numVals = 3;
96 
97  final IntVar x = model.newIntVar(0, -1, "x");
98  final IntVar y = model.newIntVar(0, numVals - 1, "y");
99  // Creates the constraints.
100  model.addDifferent(x, y);
101 
102  // Creates a solver and solves the model.
103  final CpSolver solver = new CpSolver();
104  assertNotNull(solver);
105  final CpSolverStatus status = solver.solve(model);
106 
107  assertThat(status).isEqualTo(CpSolverStatus.MODEL_INVALID);
108  assertEquals("var #0 has no domain(): name: \"x\"", solver.getSolutionInfo());
109  }
110 
111  @Test
112  public void testCpSolver_hinting() throws Exception {
113  System.out.println("testCpSolver_hinting");
114  final CpModel model = new CpModel();
115  assertNotNull(model);
116  final IntVar x = model.newIntVar(0, 5, "x");
117  final IntVar y = model.newIntVar(0, 6, "y");
118  // Creates the constraints.
119  model.addEquality(LinearExpr.newBuilder().add(x).add(y), 6);
120 
121  // Add hints.
122  model.addHint(x, 2);
123  model.addHint(y, 4);
124 
125  // Creates a solver and solves the model.
126  final CpSolver solver = new CpSolver();
127  assertNotNull(solver);
128  solver.getParameters().setCpModelPresolve(false);
129  final CpSolverStatus status = solver.solve(model);
130 
131  assertThat(status).isEqualTo(CpSolverStatus.OPTIMAL);
132  assertThat(solver.value(x)).isEqualTo(2);
133  assertThat(solver.value(y)).isEqualTo(4);
134  }
135 
136  @Test
137  public void testCpSolver_booleanValue() throws Exception {
138  System.out.println("testCpSolver_booleanValue");
139  final CpModel model = new CpModel();
140  assertNotNull(model);
141  final BoolVar x = model.newBoolVar("x");
142  final BoolVar y = model.newBoolVar("y");
143  model.addBoolOr(new Literal[] {x, y.not()});
144 
145  // Creates a solver and solves the model.
146  final CpSolver solver = new CpSolver();
147  assertNotNull(solver);
148  final CpSolverStatus status = solver.solve(model);
149 
150  assertEquals(CpSolverStatus.OPTIMAL, status);
151  assertThat(solver.booleanValue(x) || solver.booleanValue(y.not())).isTrue();
152  }
153 
154  @Test
155  public void testCpSolver_searchAllSolutions() throws Exception {
156  System.out.println("testCpSolver_searchAllSolutions");
157  final CpModel model = new CpModel();
158  assertNotNull(model);
159  // Creates the variables.
160  int numVals = 3;
161  final IntVar x = model.newIntVar(0, numVals - 1, "x");
162  final IntVar y = model.newIntVar(0, numVals - 1, "y");
163  final IntVar unusedZ = model.newIntVar(0, numVals - 1, "z");
164  // Creates the constraints.
165  model.addDifferent(x, y);
166 
167  // Creates a solver and solves the model.
168  final CpSolver solver = new CpSolver();
169  assertNotNull(solver);
170  solver.getParameters().setEnumerateAllSolutions(true);
171  final SolutionCounter cb = new SolutionCounter();
172  solver.solve(model, cb);
173 
174  assertThat(cb.getSolutionCount()).isEqualTo(18);
175  assertThat(solver.numBranches()).isGreaterThan(0L);
176  }
177 
178  @Test
179  public void testCpSolver_objectiveValue() throws Exception {
180  System.out.println("testCpSolver_objectiveValue");
181  final CpModel model = new CpModel();
182  assertNotNull(model);
183  // Creates the variables.
184  final int numVals = 3;
185  final IntVar x = model.newIntVar(0, numVals - 1, "x");
186  final IntVar y = model.newIntVar(0, numVals - 1, "y");
187  final IntVar z = model.newIntVar(0, numVals - 1, "z");
188  // Creates the constraints.
189  model.addDifferent(x, y);
190 
191  // Maximizes a linear combination of variables.
192  model.maximize(LinearExpr.newBuilder().add(x).addTerm(y, 2).addTerm(z, 3));
193 
194  // Creates a solver and solves the model.
195  final CpSolver solver = new CpSolver();
196  assertNotNull(solver);
197  CpSolverStatus status = solver.solve(model);
198 
199  assertThat(status).isEqualTo(CpSolverStatus.OPTIMAL);
200  assertThat(solver.objectiveValue()).isEqualTo(11.0);
201  assertThat(solver.value(LinearExpr.newBuilder().addSum(new IntVar[] {x, y, z}).build()))
202  .isEqualTo(solver.value(x) + solver.value(y) + solver.value(z));
203  }
204 
205  @Test
206  public void testCpModel_crashPresolve() throws Exception {
207  System.out.println("testCpModel_crashPresolve");
208  final CpModel model = new CpModel();
209  assertNotNull(model);
210  // Create decision variables
211  final IntVar x = model.newIntVar(0, 5, "x");
212  final IntVar y = model.newIntVar(0, 5, "y");
213 
214  // Create a linear constraint which enforces that only x or y can be greater than 0.
215  model.addLinearConstraint(LinearExpr.newBuilder().add(x).add(y), 0, 1);
216 
217  // Create the objective variable
218  final IntVar obj = model.newIntVar(0, 3, "obj");
219  // Cut the domain of the objective variable
220  model.addGreaterOrEqual(obj, 2);
221  // Set a constraint that makes the problem infeasible
222  model.addMaxEquality(obj, new IntVar[] {x, y});
223  // Optimize objective
224  model.minimize(obj);
225 
226  // Create a solver and solve the model.
227  final CpSolver solver = new CpSolver();
228  assertNotNull(solver);
229  com.google.ortools.sat.CpSolverStatus status = solver.solve(model);
230  assertThat(status).isEqualTo(CpSolverStatus.INFEASIBLE);
231  }
232 
233  @Test
234  public void testCpSolver_customLog() throws Exception {
235  System.out.println("testCpSolver_customLog");
236  final CpModel model = new CpModel();
237  assertNotNull(model);
238  // Creates the variables.
239  final int numVals = 3;
240  final IntVar x = model.newIntVar(0, numVals - 1, "x");
241  final IntVar y = model.newIntVar(0, numVals - 1, "y");
242  // Creates the constraints.
243  model.addDifferent(x, y);
244 
245  // Creates a solver and solves the model.
246  final CpSolver solver = new CpSolver();
247  assertNotNull(solver);
248  StringBuilder logBuilder = new StringBuilder();
249  Consumer<String> appendToLog = (String message) -> logBuilder.append(message).append('\n');
250  solver.setLogCallback(appendToLog);
251  solver.getParameters().setLogToStdout(false).setLogSearchProgress(true);
252  CpSolverStatus status = solver.solve(model);
253 
254  assertThat(status).isEqualTo(CpSolverStatus.OPTIMAL);
255  String log = logBuilder.toString();
256  assertThat(log).isNotEmpty();
257  assertThat(log).contains("Parameters");
258  assertThat(log).contains("log_to_stdout: false");
259  assertThat(log).contains("OPTIMAL");
260  }
261 
262  @Test
264  System.out.println("testCpSolver_customLogMultiThread");
265  final CpModel model = new CpModel();
266  assertNotNull(model);
267  // Creates the variables.
268  int numVals = 3;
269 
270  IntVar x = model.newIntVar(0, numVals - 1, "x");
271  IntVar y = model.newIntVar(0, numVals - 1, "y");
272  // Creates the constraints.
273  model.addDifferent(x, y);
274 
275  // Creates a solver and solves the model.
276  final CpSolver solver = new CpSolver();
277  assertNotNull(solver);
278  StringBuilder logBuilder = new StringBuilder();
279  Consumer<String> appendToLog = (String message) -> logBuilder.append(message).append('\n');
280  solver.setLogCallback(appendToLog);
281  solver.getParameters().setLogToStdout(false).setLogSearchProgress(true).setNumSearchWorkers(12);
282  CpSolverStatus status = solver.solve(model);
283 
284  assertThat(status).isEqualTo(CpSolverStatus.OPTIMAL);
285  String log = logBuilder.toString();
286  assertThat(log).isNotEmpty();
287  assertThat(log).contains("Parameters");
288  assertThat(log).contains("log_to_stdout: false");
289  assertThat(log).contains("OPTIMAL");
290  }
291 
292  @Test
293  public void issue3108() {
294  System.out.println("issue3108");
295  final CpModel model = new CpModel();
296  final IntVar var1 = model.newIntVar(0, 1, "CONTROLLABLE__C1[0]");
297  final IntVar var2 = model.newIntVar(0, 1, "CONTROLLABLE__C1[1]");
298  capacityConstraint(model, new IntVar[] {var1, var2}, new long[] {0L, 1L},
299  new long[][] {new long[] {1L, 1L}}, new long[][] {new long[] {1L, 1L}});
300  boolean unused = model.exportToFile("/tmp/issue3108.pb.txt");
301  final CpSolver solver = new CpSolver();
302  solver.getParameters().setLogSearchProgress(true);
303  solver.getParameters().setCpModelProbingLevel(0);
304  solver.getParameters().setNumSearchWorkers(4);
305  solver.getParameters().setMaxTimeInSeconds(1);
306  final CpSolverStatus status = solver.solve(model);
307  assertEquals(status, CpSolverStatus.OPTIMAL);
308  }
309 
310  private static void capacityConstraint(final CpModel model, final IntVar[] varsToAssign,
311  final long[] domainArr, final long[][] demands, final long[][] capacities) {
312  final int numTasks = varsToAssign.length;
313  final int numResources = demands.length;
314  final IntervalVar[] tasksIntervals = new IntervalVar[numTasks + capacities[0].length];
315 
316  final Domain domainT = Domain.fromValues(domainArr);
317  final Domain intervalRange =
318  Domain.fromFlatIntervals(new long[] {domainT.min() + 1, domainT.max() + 1});
319  final int unitIntervalSize = 1;
320  for (int i = 0; i < numTasks; i++) {
321  final BoolVar presence = model.newBoolVar("");
322  model.addLinearExpressionInDomain(varsToAssign[i], domainT).onlyEnforceIf(presence);
323  model.addLinearExpressionInDomain(varsToAssign[i], domainT.complement())
324  .onlyEnforceIf(presence.not());
325  // interval with start as taskToNodeAssignment and size of 1
326  tasksIntervals[i] =
327  model.newOptionalFixedSizeIntervalVar(varsToAssign[i], unitIntervalSize, presence, "");
328  }
329 
330  // Create dummy intervals
331  for (int i = numTasks; i < tasksIntervals.length; i++) {
332  final int nodeIndex = i - numTasks;
333  tasksIntervals[i] = model.newFixedInterval(domainArr[nodeIndex], 1, "");
334  }
335 
336  // Convert to list of arrays
337  final long[][] nodeCapacities = new long[numResources][];
338  final long[] maxCapacities = new long[numResources];
339 
340  for (int i = 0; i < capacities.length; i++) {
341  final long[] capacityArr = capacities[i];
342  long maxCapacityValue = Long.MIN_VALUE;
343  for (int j = 0; j < capacityArr.length; j++) {
344  maxCapacityValue = Math.max(maxCapacityValue, capacityArr[j]);
345  }
346  nodeCapacities[i] = capacityArr;
347  maxCapacities[i] = maxCapacityValue;
348  }
349 
350  // For each resource, create dummy demands to accommodate heterogeneous capacities
351  final long[][] updatedDemands = new long[numResources][];
352  for (int i = 0; i < numResources; i++) {
353  final long[] demand = new long[numTasks + capacities[0].length];
354 
355  // copy ver task demands
356  int iter = 0;
357  for (final long taskDemand : demands[i]) {
358  demand[iter] = taskDemand;
359  iter++;
360  }
361 
362  // copy over dummy demands
363  final long maxCapacity = maxCapacities[i];
364  for (final long nodeHeterogeneityAdjustment : nodeCapacities[i]) {
365  demand[iter] = maxCapacity - nodeHeterogeneityAdjustment;
366  iter++;
367  }
368  updatedDemands[i] = demand;
369  }
370 
371  // 2. Capacity constraints
372  for (int i = 0; i < numResources; i++) {
373  model.addCumulative(maxCapacities[i]).addDemands(tasksIntervals, updatedDemands[i]);
374  }
375 
376  // Cumulative score
377  for (int i = 0; i < numResources; i++) {
378  final IntVar max = model.newIntVar(0, maxCapacities[i], "");
379  model.addCumulative(max).addDemands(tasksIntervals, updatedDemands[i]);
380  model.minimize(max);
381  }
382  }
383 }
Load native libraries needed for using ortools-java.
Definition: Loader.java:33
static synchronized void loadNativeLibraries()
Definition: Loader.java:104
An Boolean variable.
Definition: BoolVar.java:20
Literal not()
Returns the negation of a boolean variable.
Definition: BoolVar.java:33
void onlyEnforceIf(Literal lit)
Adds a literal to the constraint.
Definition: Constraint.java:32
Main modeling class.
Definition: CpModel.java:42
Constraint addLinearExpressionInDomain(LinearArgument expr, Domain domain)
Adds.
Definition: CpModel.java:217
Constraint addEquality(LinearArgument expr, long value)
Adds.
Definition: CpModel.java:241
CumulativeConstraint addCumulative(LinearArgument capacity)
Adds.
Definition: CpModel.java:863
IntervalVar newOptionalFixedSizeIntervalVar(LinearArgument start, long size, Literal isPresent, String name)
Creates an optional interval variable from an affine expression start, and a fixed size.
Definition: CpModel.java:790
Boolean exportToFile(String file)
Write the model as a protocol buffer to 'file'.
Definition: CpModel.java:997
BoolVar newBoolVar(String name)
Creates a Boolean variable with the given name.
Definition: CpModel.java:88
void maximize(LinearArgument expr)
Adds a maximization objective of a linear expression.
Definition: CpModel.java:934
void addHint(IntVar var, long value)
Adds hinting to a variable.
Definition: CpModel.java:883
IntervalVar newFixedInterval(long start, long size, String name)
Creates a fixed interval from its start and its size.
Definition: CpModel.java:743
Constraint addMaxEquality(LinearArgument target, LinearArgument[] exprs)
Adds.
Definition: CpModel.java:608
Constraint addGreaterOrEqual(LinearArgument expr, long value)
Adds.
Definition: CpModel.java:280
IntVar newIntVar(long lb, long ub, String name)
Creates an integer variable with domain [lb, ub].
Definition: CpModel.java:72
Constraint addDifferent(LinearArgument expr, long value)
Adds.
Definition: CpModel.java:306
void minimize(LinearArgument expr)
Adds a minimization objective of a linear expression.
Definition: CpModel.java:913
Constraint addBoolOr(Literal[] literals)
Adds.
Definition: CpModel.java:125
Constraint addLinearConstraint(LinearArgument expr, long lb, long ub)
Adds.
Definition: CpModel.java:236
Parent class to create a callback called at each solution.
Tests the CpSolver java interface.
Wrapper around the SAT solver.
Definition: CpSolver.java:28
double objectiveValue()
Returns the best objective value found during search.
Definition: CpSolver.java:117
long value(LinearArgument expr)
Returns the value of a linear expression in the last solution found.
Definition: CpSolver.java:130
long numBranches()
Returns the number of branches explored during search.
Definition: CpSolver.java:155
CpSolverStatus solve(CpModel model)
Solves the given model, and returns the solve status.
Definition: CpSolver.java:37
String getSolutionInfo()
Returns some information on how the solution was found, or the reason why the model or the parameters...
Definition: CpSolver.java:197
SatParameters.Builder getParameters()
Returns the builder of the parameters of the SAT solver for modification.
Definition: CpSolver.java:179
void setLogCallback(Consumer< String > cb)
Sets the log callback for the solver.
Definition: CpSolver.java:184
Boolean booleanValue(Literal var)
Returns the Boolean value of a literal in the last solution found.
Definition: CpSolver.java:140
String responseStats()
Returns some statistics on the solution found as a string.
Definition: CpSolver.java:189
CumulativeConstraint addDemands(IntervalVar[] intervals, LinearArgument[] demands)
Adds all pairs (intervals[i], demands[i]) to the constraint.
An integer variable.
Definition: IntVar.java:21
LinearExprBuilder addSum(LinearArgument[] exprs)
LinearExprBuilder add(LinearArgument expr)
LinearExpr build()
Builds a linear expression.
LinearExprBuilder addTerm(LinearArgument expr, long coeff)
A linear expression (sum (ai * xi) + b).
static LinearExprBuilder newBuilder()
Returns a builder.
Interface to describe a boolean variable or its negation.
Definition: Literal.java:17