Java Reference

Java Reference

ConstraintSolverTest.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.constraintsolver;
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.assertFalse;
19 import static org.junit.jupiter.api.Assertions.assertNotNull;
20 import static org.junit.jupiter.api.Assertions.assertTrue;
21 
22 import com.google.common.collect.Iterables;
23 import com.google.ortools.Loader;
24 import com.google.ortools.constraintsolver.ConstraintSolverParameters;
25 import com.google.ortools.constraintsolver.RegularLimitParameters;
26 import java.util.ArrayList;
27 import java.util.concurrent.atomic.AtomicInteger;
28 import java.util.function.Consumer;
29 import java.util.function.Supplier;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Test;
32 
34 public final class ConstraintSolverTest {
35  @BeforeEach
36  public void setUp() {
38  }
39 
40  @Test
41  public void testSolverCtor() {
42  final Solver solver = new Solver("TestSolver");
43  assertNotNull(solver);
44  assertEquals("TestSolver", solver.model_name());
45  assertNotNull(solver.toString());
46  }
47 
48  @Test
49  public void testIntVar() {
50  final Solver solver = new Solver("Solver");
51  final IntVar var = solver.makeIntVar(3, 11, "IntVar");
52  assertEquals(3, var.min());
53  assertEquals(11, var.max());
54  }
55 
56  @Test
57  public void testIntVarArray() {
58  final Solver solver = new Solver("Solver");
59  final IntVar[] vars = solver.makeIntVarArray(7, 3, 5, "vars");
60  assertThat(vars).hasLength(7);
61  for (IntVar var : vars) {
62  assertEquals(3, var.min());
63  assertEquals(5, var.max());
64  }
65  }
66 
67  @Test
68  public void testRabbitsPheasants() {
69  final Solver solver = new Solver("testRabbitsPheasants");
70  final IntVar rabbits = solver.makeIntVar(0, 100, "rabbits");
71  final IntVar pheasants = solver.makeIntVar(0, 100, "pheasants");
72  solver.addConstraint(solver.makeEquality(solver.makeSum(rabbits, pheasants), 20));
73  solver.addConstraint(solver.makeEquality(
74  solver.makeSum(solver.makeProd(rabbits, 4), solver.makeProd(pheasants, 2)), 56));
75  final DecisionBuilder db =
76  solver.makePhase(rabbits, pheasants, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE);
77  solver.newSearch(db);
78  solver.nextSolution();
79  assertEquals(8, rabbits.value());
80  assertEquals(12, pheasants.value());
81  solver.endSearch();
82  }
83 
84  // A Decision builder that does nothing.
85  static class DummyDecisionBuilder extends JavaDecisionBuilder {
86  @Override
87  public Decision next(Solver solver) throws Solver.FailException {
88  System.out.println("In Dummy Decision Builder");
89  return null;
90  }
91 
92  @Override
93  public String toString() {
94  return "DummyDecisionBuilder";
95  }
96  }
97 
98  @Test
99  public void testDummyDecisionBuilder() {
100  final Solver solver = new Solver("testDummyDecisionBuilder");
101  final DecisionBuilder db = new DummyDecisionBuilder();
102  assertTrue(solver.solve(db));
103  }
104 
108  static class FailDecisionBuilder extends JavaDecisionBuilder {
109  @Override
110  public Decision next(Solver solver) throws Solver.FailException {
111  System.out.println("In Fail Decision Builder");
112  solver.fail();
113  return null;
114  }
115  }
116 
117  @Test
118  public void testFailDecisionBuilder() {
119  final Solver solver = new Solver("testFailDecisionBuilder");
120  final DecisionBuilder db = new FailDecisionBuilder();
121  assertFalse(solver.solve(db));
122  }
123 
125  @Test
126  public void testGolombRuler() {
127  final int m = 8;
128  final Solver solver = new Solver("GR " + m);
129 
130  final IntVar[] ticks = solver.makeIntVarArray(m, 0, (1 << (m + 1)) - 1, "ticks");
131 
132  solver.addConstraint(solver.makeEquality(ticks[0], 0));
133 
134  for (int i = 0; i < ticks.length - 1; i++) {
135  solver.addConstraint(solver.makeLess(ticks[i], ticks[i + 1]));
136  }
137 
138  final ArrayList<IntVar> diff = new ArrayList<>();
139  for (int i = 0; i < m - 1; i++) {
140  for (int j = i + 1; j < m; j++) {
141  diff.add(solver.makeDifference(ticks[j], ticks[i]).var());
142  }
143  }
144 
145  solver.addConstraint(solver.makeAllDifferent(diff.toArray(new IntVar[0]), true));
146 
147  // break symetries
148  if (m > 2) {
149  solver.addConstraint(solver.makeLess(diff.get(0), Iterables.getLast(diff)));
150  }
151 
152  final OptimizeVar opt = solver.makeMinimize(ticks[m - 1], 1);
153  final DecisionBuilder db =
154  solver.makePhase(ticks, Solver.CHOOSE_MIN_SIZE_LOWEST_MIN, Solver.ASSIGN_MIN_VALUE);
155  final SearchMonitor log = solver.makeSearchLog(10000, opt);
156  assertTrue(solver.solve(db, opt, log));
157  assertEquals(34, opt.best());
158  }
159 
160  @Test
161  public void testElementFunction() {
162  final Solver solver = new Solver("testElementFunction");
163  final IntVar index = solver.makeIntVar(0, 10, "index");
164  final IntExpr element = solver.makeElement((long x) -> x * 2, index);
165  assertEquals(0, element.min());
166  assertEquals(20, element.max());
167  }
168 
169  @Test
170  public void testSolverParameters() {
171  final ConstraintSolverParameters parameters =
172  Solver.defaultSolverParameters().toBuilder().setTraceSearch(true).build();
173  final Solver solver = new Solver("testSolverParameters", parameters);
174  final ConstraintSolverParameters stored = solver.parameters();
175  assertTrue(stored.getTraceSearch());
176  }
177 
178  @Test
180  final Solver solver = new Solver("testRegularLimitParameters");
181  final RegularLimitParameters protoLimit =
182  solver.makeDefaultRegularLimitParameters().toBuilder().setFailures(20000).build();
183  assertEquals(20000, protoLimit.getFailures());
184  final SearchLimit limit = solver.makeLimit(protoLimit);
185  assertNotNull(limit);
186  }
187 
188  // verify Closure in Decision.
189  @Test
190  public void testClosureDecision() {
191  final StringProperty call = new StringProperty("");
192  final Solver solver = new Solver("ClosureDecisionTest");
193  final Decision decision = solver.makeDecision(
194  (Solver s) -> call.setValue("Apply"), (Solver s) -> call.setValue("Refute"));
195  System.gc();
196 
197  decision.apply(solver);
198  assertEquals("Apply", call.toString());
199 
200  decision.refute(solver);
201  assertEquals("Refute", call.toString());
202  }
203 
204  @Test
206  final Solver solver = new Solver("SolverTestName");
207  final String modelName = solver.model_name();
208  // Lambda can only capture final or implicit final.
209  final AtomicInteger countApply = new AtomicInteger(0);
210  final AtomicInteger countRefute = new AtomicInteger(0);
211  assertEquals(0, countApply.intValue());
212  assertEquals(0, countRefute.intValue());
213  final Decision decision = solver.makeDecision(
214  (Solver s)
215  -> {
216  assertEquals(s.model_name(), modelName);
217  countApply.addAndGet(1);
218  },
219  (Solver s) -> {
220  assertEquals(s.model_name(), modelName);
221  countRefute.addAndGet(1);
222  });
223  System.gc(); // verify lambda are kept alive
224 
225  decision.apply(solver);
226  assertEquals(1, countApply.intValue());
227  assertEquals(0, countRefute.intValue());
228 
229  decision.refute(solver);
230  assertEquals(1, countApply.intValue());
231  assertEquals(1, countRefute.intValue());
232  }
233 
234  // A Decision builder that does nothing
235  public static class ActionDecisionBuilder extends JavaDecisionBuilder {
236  Consumer<Solver> apply;
237  Consumer<Solver> refute;
238  boolean passed;
239 
240  public ActionDecisionBuilder(Consumer<Solver> a, Consumer<Solver> r) {
241  apply = a;
242  refute = r;
243  passed = false;
244  }
245 
246  @Override
247  public Decision next(Solver solver) throws Solver.FailException {
248  if (passed) {
249  return null;
250  }
251  passed = true;
252  return solver.makeDecision(apply, refute);
253  }
254 
255  @Override
256  public String toString() {
257  return "ActionDecisionBuilder";
258  }
259  }
260 
261  // Tests the ActionDecisionBuilder.
262  @Test
264  final Solver solver = new Solver("testActionDecisionBuilder");
265  // Lambda can only capture final or implicit final
266  final AtomicInteger countApply = new AtomicInteger(0);
267  final AtomicInteger countRefute = new AtomicInteger(0);
268  assertEquals(0, countApply.intValue());
269  assertEquals(0, countRefute.intValue());
270  final DecisionBuilder db = new ActionDecisionBuilder(
271  (Solver s) -> countApply.addAndGet(1), (Solver s) -> countRefute.addAndGet(1));
272  solver.newSearch(db);
273  assertTrue(solver.nextSolution());
274  assertEquals(1, countApply.intValue());
275  assertEquals(0, countRefute.intValue());
276  assertTrue(solver.nextSolution());
277  assertEquals(1, countApply.intValue());
278  assertEquals(1, countRefute.intValue());
279  solver.endSearch();
280  }
281 
282  // ----- LocalSearch Test -----
283  private static class MoveOneVar extends IntVarLocalSearchOperator {
284  public MoveOneVar(IntVar[] variables) {
285  super(variables);
286  variableIndex = 0;
287  moveUp = false;
288  }
289 
290  @Override
291  protected boolean oneNeighbor() {
292  long currentValue = oldValue(variableIndex);
293  if (moveUp) {
294  setValue(variableIndex, currentValue + 1);
295  variableIndex = (variableIndex + 1) % size();
296  } else {
297  setValue(variableIndex, currentValue - 1);
298  }
299  moveUp = !moveUp;
300  return true;
301  }
302 
303  @Override
304  public void onStart() {}
305 
306  // Index of the next variable to try to restore
307  private long variableIndex;
308  // Direction of the modification.
309  private boolean moveUp;
310  }
311 
312  @Test
313  public void testSolver() {
314  final Solver solver = new Solver("Solver");
315  final IntVar[] vars = solver.makeIntVarArray(4, 0, 4, "vars");
316  final IntVar sumVar = solver.makeSum(vars).var();
317  final OptimizeVar obj = solver.makeMinimize(sumVar, 1);
318  final DecisionBuilder db =
319  solver.makePhase(vars, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MAX_VALUE);
320  final MoveOneVar moveOneVar = new MoveOneVar(vars);
321  final LocalSearchPhaseParameters lsParams =
322  solver.makeLocalSearchPhaseParameters(sumVar, moveOneVar, db);
323  final DecisionBuilder ls = solver.makeLocalSearchPhase(vars, db, lsParams);
324  final SolutionCollector collector = solver.makeLastSolutionCollector();
325  collector.addObjective(sumVar);
326  final SearchMonitor log = solver.makeSearchLog(1000, obj);
327 
328  assertTrue(solver.solve(ls, collector, obj, log));
329  }
330 
331  private static class SumFilter extends IntVarLocalSearchFilter {
332  public SumFilter(IntVar[] vars) {
333  super(vars);
334  sum = 0;
335  }
336 
337  @Override
338  protected void onSynchronize(Assignment unusedDelta) {
339  sum = 0;
340  for (int index = 0; index < size(); ++index) {
341  sum += value(index);
342  }
343  }
344 
345  @Override
346  public boolean accept(Assignment delta, Assignment unusedDeltadelta, long unusedObjectiveMin,
347  long unusedObjectiveMax) {
348  AssignmentIntContainer solutionDelta = delta.intVarContainer();
349  int solutionDeltaSize = solutionDelta.size();
350 
351  for (int i = 0; i < solutionDeltaSize; ++i) {
352  if (!solutionDelta.element(i).activated()) {
353  return true;
354  }
355  }
356  long newSum = sum;
357  for (int index = 0; index < solutionDeltaSize; ++index) {
358  int touchedVar = index(solutionDelta.element(index).var());
359  long oldValue = value(touchedVar);
360  long newValue = solutionDelta.element(index).value();
361  newSum += newValue - oldValue;
362  }
363  return newSum < sum;
364  }
365  private long sum;
366  }
367 
368  private static class StringProperty {
369  public StringProperty(String initialValue) {
370  value = initialValue;
371  }
372  public void setValue(String newValue) {
373  value = newValue;
374  }
375 
376  @Override
377  public String toString() {
378  return value;
379  }
380  private String value;
381  }
382 
383  @Test
385  final Solver solver = new Solver("Solver");
386  final IntVar[] vars = solver.makeIntVarArray(4, 0, 4, "vars");
387  final IntVar sumVar = solver.makeSum(vars).var();
388  final OptimizeVar obj = solver.makeMinimize(sumVar, 1);
389  final DecisionBuilder db =
390  solver.makePhase(vars, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MAX_VALUE);
391  MoveOneVar moveOneVar = new MoveOneVar(vars);
392  SumFilter filter = new SumFilter(vars);
393  IntVarLocalSearchFilter[] filters = new IntVarLocalSearchFilter[1];
394  filters[0] = filter;
395  LocalSearchFilterManager filterManager = new LocalSearchFilterManager(filters);
396  LocalSearchPhaseParameters lsParams =
397  solver.makeLocalSearchPhaseParameters(sumVar, moveOneVar, db, null, filterManager);
398  DecisionBuilder ls = solver.makeLocalSearchPhase(vars, db, lsParams);
399  SolutionCollector collector = solver.makeLastSolutionCollector();
400  collector.addObjective(sumVar);
401  SearchMonitor log = solver.makeSearchLog(1000, obj);
402  solver.solve(ls, collector, obj, log);
403  }
404 
405  private static class OneVarLns extends BaseLns {
406  public OneVarLns(IntVar[] vars) {
407  super(vars);
408  }
409 
410  @Override
411  public void initFragments() {
412  index = 0;
413  }
414 
415  @Override
416  public boolean nextFragment() {
417  int size = size();
418  if (index < size) {
419  appendToFragment(index);
420  ++index;
421  return true;
422  } else {
423  return false;
424  }
425  }
426  private int index;
427  }
428 
429  @Test
430  public void testSolverLns() {
431  final Solver solver = new Solver("Solver");
432  final IntVar[] vars = solver.makeIntVarArray(4, 0, 4, "vars");
433  final IntVar sumVar = solver.makeSum(vars).var();
434  final OptimizeVar obj = solver.makeMinimize(sumVar, 1);
435  final DecisionBuilder db =
436  solver.makePhase(vars, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MAX_VALUE);
437  final OneVarLns oneVarLns = new OneVarLns(vars);
438  final LocalSearchPhaseParameters lsParams =
439  solver.makeLocalSearchPhaseParameters(sumVar, oneVarLns, db);
440  final DecisionBuilder ls = solver.makeLocalSearchPhase(vars, db, lsParams);
441  final SolutionCollector collector = solver.makeLastSolutionCollector();
442  collector.addObjective(sumVar);
443  final SearchMonitor log = solver.makeSearchLog(1000, obj);
444  solver.solve(ls, collector, obj, log);
445  }
446 
447  // ----- SearchLog Test -----
448  // TODO(user): Improve search log tests; currently only tests coverage and callback.
449  // note: this is more or less what is done in search_test.cc
450  private static void runSearchLog(SearchMonitor searchlog) {
451  searchlog.enterSearch();
452  searchlog.exitSearch();
453  searchlog.acceptSolution();
454  searchlog.atSolution();
455  searchlog.beginFail();
456  searchlog.noMoreSolutions();
457  searchlog.beginInitialPropagation();
458  searchlog.endInitialPropagation();
459  }
460 
461  // Simple Coverage test...
462  @Test
463  public void testSearchLog() {
464  final Solver solver = new Solver("TestSearchLog");
465  final IntVar var = solver.makeIntVar(1, 1, "Variable");
466  solver.makeMinimize(var, 1);
467  final SearchMonitor searchlog = solver.makeSearchLog(0);
468  runSearchLog(searchlog);
469  }
470 
471  private static class SearchCount implements Supplier<String> {
472  public SearchCount(AtomicInteger initialCount) {
473  count = initialCount;
474  }
475  @Override
476  public String get() {
477  count.addAndGet(1);
478  return "display callback called...";
479  }
480  private final AtomicInteger count;
481  }
482 
483  @Test
485  final Solver solver = new Solver("TestSearchLog");
486  final AtomicInteger count = new AtomicInteger(0);
487  final SearchMonitor searchlog = solver.makeSearchLog(0, // branch period
488  new SearchCount(count));
489  System.gc(); // verify SearchCount is kept alive by the searchlog
490  runSearchLog(searchlog);
491  assertEquals(1, count.intValue());
492  }
493 
494  @Test
496  final Solver solver = new Solver("TestSearchLog");
497  final AtomicInteger count = new AtomicInteger(0);
498  final SearchMonitor searchlog = solver.makeSearchLog(0, // branch period
499  () -> {
500  count.addAndGet(1);
501  return "display callback called...";
502  });
503  System.gc(); // verify lambda is kept alive by the searchlog
504  runSearchLog(searchlog);
505  assertEquals(1, count.intValue());
506  }
507 
508  @Test
510  final Solver solver = new Solver("TestSearchLog");
511  final IntVar var = solver.makeIntVar(1, 1, "Variable");
512  final AtomicInteger count = new AtomicInteger(0);
513  final SearchMonitor searchlog = solver.makeSearchLog(0, // branch period
514  var, // IntVar to monitor
515  new SearchCount(count));
516  System.gc();
517  runSearchLog(searchlog);
518  assertEquals(1, count.intValue());
519  }
520 
521  @Test
523  final Solver solver = new Solver("TestSearchLog");
524  final IntVar var = solver.makeIntVar(1, 1, "Variable");
525  final OptimizeVar objective = solver.makeMinimize(var, 1);
526  final AtomicInteger count = new AtomicInteger(0);
527  final SearchMonitor searchlog = solver.makeSearchLog(0, // branch period
528  objective, // objective var to monitor
529  new SearchCount(count));
530  System.gc();
531  runSearchLog(searchlog);
532  assertEquals(1, count.intValue());
533  }
534 }
Load native libraries needed for using ortools-java.
Definition: Loader.java:33
static synchronized void loadNativeLibraries()
Definition: Loader.java:104
Decision next(Solver solver)
This is the new method to subclass when defining a java decision builder.
This class acts as a intermediate step between a c++ decision builder and a java one.