1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.sat4j.pb.tools;
21
22 import java.math.BigInteger;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.TreeSet;
29
30 import org.sat4j.core.Vec;
31 import org.sat4j.core.VecInt;
32 import org.sat4j.pb.IPBSolver;
33 import org.sat4j.pb.ObjectiveFunction;
34 import org.sat4j.specs.ContradictionException;
35 import org.sat4j.specs.IConstr;
36 import org.sat4j.specs.IVec;
37 import org.sat4j.specs.IVecInt;
38 import org.sat4j.specs.TimeoutException;
39 import org.sat4j.tools.GateTranslator;
40
41
42
43
44
45
46
47
48
49
50
51
52 public class DependencyHelper<T, C> {
53
54 public final INegator NO_NEGATION = new INegator() {
55
56 public boolean isNegated(Object thing) {
57 return false;
58 }
59
60 public Object unNegate(Object thing) {
61 return thing;
62 }
63 };
64
65 public static final INegator BASIC_NEGATION = new INegator() {
66
67 public boolean isNegated(Object thing) {
68 return (thing instanceof Negation);
69 }
70
71 public Object unNegate(Object thing) {
72 return ((Negation) thing).getThing();
73 }
74 };
75
76 private static final class Negation {
77 private final Object thing;
78
79 Negation(Object thing) {
80 this.thing = thing;
81 }
82
83 Object getThing() {
84 return thing;
85 }
86 }
87
88
89
90
91 private static final long serialVersionUID = 1L;
92
93 private final Map<T, Integer> mapToDimacs = new HashMap<T, Integer>();
94 private final Map<Integer, T> mapToDomain = new HashMap<Integer, T>();
95 final Map<IConstr, C> descs = new HashMap<IConstr, C>();
96
97 private final XplainPB xplain;
98 private final GateTranslator gator;
99 final IPBSolver solver;
100 private INegator negator = BASIC_NEGATION;
101
102 private ObjectiveFunction objFunction;
103 private IVecInt objLiterals;
104 private IVec<BigInteger> objCoefs;
105
106 public boolean explanationEnabled = true;
107 public boolean canonicalOptFunction = true;
108
109
110
111
112
113
114 public DependencyHelper(IPBSolver solver) {
115 this(solver, true);
116 }
117
118
119
120
121
122
123
124
125
126
127
128 public DependencyHelper(IPBSolver solver, boolean explanationEnabled) {
129 this(solver, explanationEnabled, true);
130 }
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 public DependencyHelper(IPBSolver solver, boolean explanationEnabled,
149 boolean canonicalOptFunctionEnabled) {
150 if (explanationEnabled) {
151 this.xplain = new XplainPB(solver);
152 this.solver = this.xplain;
153 } else {
154 this.xplain = null;
155 this.solver = solver;
156 }
157 this.gator = new GateTranslator(this.solver);
158 canonicalOptFunction = canonicalOptFunctionEnabled;
159 }
160
161 public void setNegator(INegator negator) {
162 this.negator = negator;
163 }
164
165
166
167
168
169
170
171
172 int getIntValue(T thing) {
173 return getIntValue(thing, true);
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187 int getIntValue(T thing, boolean create) {
188 T myThing;
189 boolean negated = negator.isNegated(thing);
190 if (negated) {
191 myThing = (T) negator.unNegate(thing);
192 } else {
193 myThing = thing;
194 }
195 Integer intValue = mapToDimacs.get(myThing);
196 if (intValue == null) {
197 if (create) {
198 intValue = solver.nextFreeVarId(true);
199 mapToDomain.put(intValue, myThing);
200 mapToDimacs.put(myThing, intValue);
201 } else {
202 throw new IllegalArgumentException("" + myThing
203 + " is unknown in the solver!");
204 }
205 }
206 if (negated) {
207 return -intValue;
208 }
209 return intValue;
210 }
211
212
213
214
215
216
217
218
219
220
221 public IVec<T> getSolution() {
222 int[] model = solver.model();
223 IVec<T> toInstall = new Vec<T>();
224 if (model != null) {
225 for (int i : model) {
226 if (i > 0) {
227 toInstall.push(mapToDomain.get(i));
228 }
229 }
230 }
231 return toInstall;
232 }
233
234 public BigInteger getSolutionCost() {
235 return objFunction.calculateDegree(solver.model());
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250 public boolean getBooleanValueFor(T t) {
251 return solver.model(getIntValue(t, false));
252 }
253
254
255
256
257
258
259
260 public boolean hasASolution() throws TimeoutException {
261 return solver.isSatisfiable();
262 }
263
264
265
266
267
268
269
270 public boolean hasASolution(IVec<T> assumps) throws TimeoutException {
271 IVecInt assumptions = new VecInt();
272 for (Iterator<T> it = assumps.iterator(); it.hasNext();) {
273 assumptions.push(getIntValue(it.next()));
274 }
275 return solver.isSatisfiable(assumptions);
276 }
277
278
279
280
281
282
283
284 public boolean hasASolution(Collection<T> assumps) throws TimeoutException {
285 IVecInt assumptions = new VecInt();
286 for (T t : assumps) {
287 assumptions.push(getIntValue(t));
288 }
289 return solver.isSatisfiable(assumptions);
290 }
291
292
293
294
295
296
297
298
299
300
301
302 public Set<C> why() throws TimeoutException {
303 if (!explanationEnabled) {
304 throw new UnsupportedOperationException("Explanation not enabled!");
305 }
306 Collection<IConstr> explanation = xplain.explain();
307 Set<C> ezexplain = new TreeSet<C>();
308 for (IConstr constr : explanation) {
309 C desc = descs.get(constr);
310 if (desc != null) {
311 ezexplain.add(desc);
312 }
313 }
314 return ezexplain;
315 }
316
317
318
319
320
321
322
323
324
325 public Set<C> why(T thing) throws TimeoutException {
326 IVecInt assumps = new VecInt();
327 assumps.push(-getIntValue(thing));
328 return why(assumps);
329 }
330
331
332
333
334
335
336
337
338
339 public Set<C> whyNot(T thing) throws TimeoutException {
340 IVecInt assumps = new VecInt();
341 assumps.push(getIntValue(thing));
342 return why(assumps);
343 }
344
345 private Set<C> why(IVecInt assumps) throws TimeoutException {
346 if (xplain.isSatisfiable(assumps)) {
347 return new TreeSet<C>();
348 }
349 return why();
350 }
351
352
353
354
355
356
357
358
359
360
361
362
363
364 public void setTrue(T thing, C name) throws ContradictionException {
365 IConstr constr = gator.gateTrue(getIntValue(thing));
366 if (constr != null) {
367 descs.put(constr, name);
368 }
369 }
370
371
372
373
374
375
376
377
378
379
380
381
382
383 public void setFalse(T thing, C name) throws ContradictionException {
384 IConstr constr = gator.gateFalse(getIntValue(thing));
385
386 if (constr != null) {
387 descs.put(constr, name);
388 }
389
390 }
391
392
393
394
395
396
397
398
399
400 public ImplicationRHS<T, C> implication(T... lhs) {
401 IVecInt clause = new VecInt();
402 for (T t : lhs) {
403 clause.push(-getIntValue(t));
404 }
405 return new ImplicationRHS<T, C>(this, clause);
406 }
407
408 public DisjunctionRHS<T, C> disjunction(T... lhs) {
409 IVecInt literals = new VecInt();
410 for (T t : lhs) {
411 literals.push(-getIntValue(t));
412 }
413 return new DisjunctionRHS<T, C>(this, literals);
414 }
415
416
417
418
419
420
421
422
423
424
425
426
427
428 public void atLeast(C name, int i, T... things)
429 throws ContradictionException {
430 IVecInt literals = new VecInt();
431 for (T t : things) {
432 literals.push(getIntValue(t));
433 }
434 descs.put(solver.addAtLeast(literals, i), name);
435 }
436
437
438
439
440
441
442
443
444
445
446
447
448
449 public ImplicationNamer<T, C> atMost(int i, T... things)
450 throws ContradictionException {
451 IVec<IConstr> toName = new Vec<IConstr>();
452 IVecInt literals = new VecInt();
453 for (T t : things) {
454 literals.push(getIntValue(t));
455 }
456 toName.push(solver.addAtMost(literals, i));
457 return new ImplicationNamer<T, C>(this, toName);
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471
472 public void atMost(C name, int i, T... things)
473 throws ContradictionException {
474 IVecInt literals = new VecInt();
475 for (T t : things) {
476 literals.push(getIntValue(t));
477 }
478 descs.put(solver.addAtMost(literals, i), name);
479 }
480
481
482
483
484
485
486
487
488
489 public void clause(C name, T... things) throws ContradictionException {
490 IVecInt literals = new VecInt(things.length);
491 for (T t : things) {
492 literals.push(getIntValue(t));
493 }
494 IConstr constr = gator.addClause(literals);
495
496 if (constr != null) {
497 descs.put(constr, name);
498 }
499
500 }
501
502
503
504
505
506
507
508
509
510
511
512 public void iff(C name, T thing, T... otherThings)
513 throws ContradictionException {
514 IVecInt literals = new VecInt(otherThings.length);
515 for (T t : otherThings) {
516 literals.push(getIntValue(t));
517 }
518 IConstr[] constrs = gator.iff(getIntValue(thing), literals);
519 for (IConstr constr : constrs) {
520 if (constr == null) {
521 throw new IllegalStateException(
522 "Constraints are not supposed to be null when using the helper");
523 }
524 descs.put(constr, name);
525 }
526 }
527
528
529
530
531
532
533
534
535
536
537 public void and(C name, T thing, T... otherThings)
538 throws ContradictionException {
539 IVecInt literals = new VecInt(otherThings.length);
540 for (T t : otherThings) {
541 literals.push(getIntValue(t));
542 }
543 IConstr[] constrs = gator.and(getIntValue(thing), literals);
544 for (IConstr constr : constrs) {
545 if (constr == null) {
546 throw new IllegalStateException(
547 "Constraints are not supposed to be null when using the helper");
548 }
549 descs.put(constr, name);
550 }
551 }
552
553
554
555
556
557
558
559
560
561
562 public void or(C name, T thing, T... otherThings)
563 throws ContradictionException {
564 IVecInt literals = new VecInt(otherThings.length);
565 for (T t : otherThings) {
566 literals.push(getIntValue(t));
567 }
568 IConstr[] constrs = gator.or(getIntValue(thing), literals);
569 for (IConstr constr : constrs) {
570 if (constr == null) {
571 throw new IllegalStateException(
572 "Constraints are not supposed to be null when using the helper");
573 }
574 descs.put(constr, name);
575 }
576 }
577
578
579
580
581
582
583
584
585
586
587 public void ifThenElse(C name, T thing, T conditionThing, T thenThing,
588 T elseThing) throws ContradictionException {
589 IConstr[] constrs = gator.ite(getIntValue(thing),
590 getIntValue(conditionThing), getIntValue(thenThing),
591 getIntValue(elseThing));
592 for (IConstr constr : constrs) {
593 if (constr == null) {
594 throw new IllegalStateException(
595 "Constraints are not supposed to be null when using the helper");
596 }
597 descs.put(constr, name);
598 }
599 }
600
601
602
603
604
605
606
607
608
609 public void setObjectiveFunction(WeightedObject<T>... wobj) {
610 createObjectivetiveFunctionIfNeeded(wobj.length);
611 for (WeightedObject<T> wo : wobj) {
612 addProperly(wo.thing, wo.getWeight());
613 }
614
615 }
616
617 private void addProperly(T thing, BigInteger weight) {
618 int lit = getIntValue(thing);
619 int index;
620 if (canonicalOptFunction && (index = objLiterals.indexOf(lit)) != -1) {
621 objCoefs.set(index, objCoefs.get(index).add(weight));
622 } else {
623 objLiterals.push(lit);
624 objCoefs.push(weight);
625 }
626 }
627
628 private void createObjectivetiveFunctionIfNeeded(int n) {
629 if (objFunction == null) {
630 objLiterals = new VecInt(n);
631 objCoefs = new Vec<BigInteger>(n);
632 objFunction = new ObjectiveFunction(objLiterals, objCoefs);
633 solver.setObjectiveFunction(objFunction);
634 }
635 }
636
637
638
639
640
641
642
643 public void addToObjectiveFunction(T thing, int weight) {
644 addToObjectiveFunction(thing, BigInteger.valueOf(weight));
645 }
646
647
648
649
650
651
652
653 public void addToObjectiveFunction(T thing, BigInteger weight) {
654 createObjectivetiveFunctionIfNeeded(20);
655 addProperly(thing, weight);
656 }
657
658
659
660
661
662
663
664
665
666
667 public void atLeast(C name, BigInteger degree, WeightedObject<T>... wobj)
668 throws ContradictionException {
669 IVecInt literals = new VecInt(wobj.length);
670 IVec<BigInteger> coeffs = new Vec<BigInteger>(wobj.length);
671 for (WeightedObject<T> wo : wobj) {
672 literals.push(getIntValue(wo.thing));
673 coeffs.push(wo.getWeight());
674 }
675 descs.put(solver.addPseudoBoolean(literals, coeffs, true, degree), name);
676 }
677
678
679
680
681
682
683
684
685
686
687 public void atMost(C name, BigInteger degree, WeightedObject<T>... wobj)
688 throws ContradictionException {
689 IVecInt literals = new VecInt(wobj.length);
690 IVec<BigInteger> coeffs = new Vec<BigInteger>(wobj.length);
691 for (WeightedObject<T> wo : wobj) {
692 literals.push(getIntValue(wo.thing));
693 coeffs.push(wo.getWeight());
694 }
695 descs.put(solver.addPseudoBoolean(literals, coeffs, false, degree),
696 name);
697 }
698
699 public void atMost(C name, int degree, WeightedObject<T>... wobj)
700 throws ContradictionException {
701 atMost(name, BigInteger.valueOf(degree), wobj);
702 }
703
704
705
706
707
708 public void stopSolver() {
709 solver.expireTimeout();
710 }
711
712
713
714
715
716 public void stopExplanation() {
717 if (!explanationEnabled) {
718 throw new UnsupportedOperationException("Explanation not enabled!");
719 }
720 xplain.cancelExplanation();
721 }
722
723 public void discard(IVec<T> things) throws ContradictionException {
724 IVecInt literals = new VecInt(things.size());
725 for (Iterator<T> it = things.iterator(); it.hasNext();) {
726 literals.push(-getIntValue(it.next()));
727 }
728 solver.addBlockingClause(literals);
729 }
730
731 public void discardSolutionsWithObjectiveValueGreaterThan(long value)
732 throws ContradictionException {
733 ObjectiveFunction obj = solver.getObjectiveFunction();
734 IVecInt literals = new VecInt(obj.getVars().size());
735 obj.getVars().copyTo(literals);
736 IVec<BigInteger> coeffs = new Vec<BigInteger>(obj.getCoeffs().size());
737 obj.getCoeffs().copyTo(coeffs);
738 solver.addPseudoBoolean(literals, coeffs, false, BigInteger
739 .valueOf(value));
740 }
741
742 public String getObjectiveFunction() {
743 ObjectiveFunction obj = solver.getObjectiveFunction();
744 StringBuffer stb = new StringBuffer();
745 for (int i = 0; i < obj.getVars().size(); i++) {
746 stb.append(obj.getCoeffs().get(i)
747 + (obj.getVars().get(i) > 0 ? " " : "~")
748 + mapToDomain.get(Math.abs(obj.getVars().get(i))) + " ");
749 }
750 return stb.toString();
751 }
752
753 public int getNumberOfVariables() {
754 return mapToDimacs.size();
755 }
756
757 public int getNumberOfConstraints() {
758 return descs.size();
759 }
760
761 public Map<Integer, T> getMappingToDomain() {
762 return mapToDomain;
763 }
764
765 public Object not(T thing) {
766 return new Negation(thing);
767 }
768
769
770
771
772
773 public IPBSolver getSolver() {
774 return solver;
775 }
776 }