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