77
88import java .util .Collection ;
99import java .util .HashMap ;
10+ import java .util .HashSet ;
11+ import java .util .LinkedList ;
1012import java .util .List ;
1113import java .util .Map ;
1214
2325import checkers .inference .model .Constraint ;
2426import checkers .inference .model .Slot ;
2527import checkers .inference .model .serialization .CnfVecIntSerializer ;
28+ import org .sat4j .specs .ContradictionException ;
29+ import org .sat4j .specs .TimeoutException ;
2630
2731/**
2832 * This solver is used to convert any constraint set using a type system with only 2 types (Top/Bottom),
@@ -69,9 +73,10 @@ protected boolean isTop(ConstantSlot constantSlot) {
6973 }
7074
7175 public InferenceResult solve () {
72- final Map <Integer , AnnotationMirror > solutions = new HashMap <>();
7376
74- final List <VecInt > clauses = serializer .convertAll (constraints );
77+ final List <VecInt > softClauses = new LinkedList <>();
78+ final List <VecInt > hardClauses = new LinkedList <>();
79+ serializer .convertAll (constraints , hardClauses , softClauses );
7580
7681 // nextId describes the LARGEST id that might be found in a variable
7782 // if an exception occurs while creating a variable the id might be incremented
@@ -82,7 +87,7 @@ public InferenceResult solve() {
8287 // TODO: thus here the value of totalVars is the real slots number stored in slotManager, and plus the
8388 // TODO: "fake" slots number stored in existentialToPotentialVar
8489 final int totalVars = slotManager .getNumberOfSlots () + serializer .getExistentialToPotentialVar ().size ();
85- final int totalClauses = clauses .size ();
90+ final int totalClauses = hardClauses . size () + softClauses .size ();
8691
8792
8893 // When .newBoth is called, SAT4J will run two solvers and return the result of the first to halt
@@ -96,42 +101,69 @@ public InferenceResult solve() {
96101
97102 VecInt lastClause = null ;
98103 try {
99- for (VecInt clause : clauses ) {
104+ for (VecInt clause : hardClauses ) {
105+ lastClause = clause ;
106+ solver .addHardClause (clause );
107+ }
108+ for (VecInt clause : softClauses ) {
100109
101110 lastClause = clause ;
102111 solver .addSoftClause (clause );
103112 }
104113
105- // isSatisfiable launches the solvers and waits until one of them finishes
106- if (solver .isSatisfiable ()) {
107- final Map <Integer , Integer > existentialToPotentialIds = serializer .getExistentialToPotentialVar ();
108- int [] solution = solver .model ();
109-
110- for (Integer var : solution ) {
111- boolean isTop = var < 0 ;
112- if (isTop ) {
113- var = -var ;
114- }
115-
116- Integer potential = existentialToPotentialIds .get (var );
117- if (potential != null ) {
118- // idToExistence.put(potential, !isTop);
119- // TODO: which AnnotationMirror should be used?
120- solutions .put (potential , bottom );
121- } else {
122- solutions .put (var , isTop ? top : bottom );
123- }
114+ } catch (ContradictionException ce ) {
115+ // This happens when adding a clause causes trivial contradiction, such as adding -1 to {1}
116+ System .out .println ("Not solvable! Contradiction exception " +
117+ "when adding clause: " + lastClause + "." );
118+
119+ // pass empty set as the unsat explanation
120+ // TODO: explain UNSAT possibly by reusing MaxSatSolver.MaxSATUnsatisfiableConstraintExplainer
121+ return new DefaultInferenceResult (new HashSet <>());
122+ }
124123
124+ boolean isSatisfiable ;
125+ try {
126+ // isSatisfiable() launches the solvers and waits until one of them finishes
127+ isSatisfiable = solver .isSatisfiable ();
128+
129+ } catch (TimeoutException te ) {
130+ throw new RuntimeException ("MAX-SAT solving timeout! " );
131+ }
132+
133+ if (!isSatisfiable ) {
134+ System .out .println ("Not solvable!" );
135+ // pass empty set as the unsat explanation
136+ // TODO: explain UNSAT possibly by reusing MaxSatSolver.MaxSATUnsatisfiableConstraintExplainer
137+ return new DefaultInferenceResult (new HashSet <>());
138+ }
139+
140+ int [] solution = solver .model ();
141+ // The following code decodes VecInt solution to the slot-annotation mappings
142+ final Map <Integer , AnnotationMirror > decodedSolution = new HashMap <>();
143+ final Map <Integer , Integer > existentialToPotentialIds = serializer .getExistentialToPotentialVar ();
144+
145+ for (Integer var : solution ) {
146+ Integer potential = existentialToPotentialIds .get (Math .abs (var ));
147+ if (potential != null ) {
148+ // Assume the 'solution' output by the solver is already sorted in the ascending order
149+ // of their absolute values. So the existential variables come after the potential variables,
150+ // which means the potential slot corresponding to the current existential variable is
151+ // already inserted into 'solutions'
152+ assert decodedSolution .containsKey (potential );
153+ if (var < 0 ) {
154+ // The existential variable is false, so the potential variable should not be inserted.
155+ // Remove it from the solution.
156+ decodedSolution .remove (potential );
125157 }
126158 } else {
127- System .out .println ("Not solvable!" );
159+ boolean isTop = var < 0 ;
160+ if (isTop ) {
161+ var = -var ;
162+ }
163+ decodedSolution .put (var , isTop ? top : bottom );
128164 }
129-
130- } catch (Throwable th ) {
131- throw new RuntimeException ("Error MAX-SAT solving! " + lastClause , th );
132165 }
133166
134-
135- return new DefaultInferenceResult (solutions );
167+ return new DefaultInferenceResult (decodedSolution );
136168 }
137169}
0 commit comments