Skip to content

Commit 9fee375

Browse files
committed
Merge branch 'master' of https://github.com/opprop/checker-framework-inference into unsat-verify
2 parents fad7c62 + 17f3a6c commit 9fee375

File tree

2 files changed

+109
-29
lines changed

2 files changed

+109
-29
lines changed

src/checkers/inference/model/serialization/CnfVecIntSerializer.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import checkers.inference.model.LubVariableSlot;
99
import checkers.inference.model.ImplicationConstraint;
1010
import checkers.inference.model.VariableSlot;
11+
import org.checkerframework.javacutil.BugInCF;
1112
import org.sat4j.core.VecInt;
1213

1314
import checkers.inference.SlotManager;
@@ -288,12 +289,37 @@ public VecInt[] serialize(ImplicationConstraint implicationConstraint) {
288289
"MaxSAT backend. Use MaxSATSolver instead!");
289290
}
290291

292+
/**
293+
* Convert all the given mandatory constraints into hard clauses. A BugInCF exception is
294+
* raised if the given constraints contain any {@link PreferenceConstraint}.
295+
*
296+
* For conversion of constraints containing {@link PreferenceConstraint}, use
297+
* {@link CnfVecIntSerializer#convertAll(Iterable, List, List)}
298+
*
299+
* @param constraints the constraints to convert
300+
* @return the output clauses for the given constraints
301+
*/
291302
public List<VecInt> convertAll(Iterable<Constraint> constraints) {
292303
return convertAll(constraints, new LinkedList<VecInt>());
293304
}
294305

306+
/**
307+
* Convert all the given mandatory constraints into hard clauses. A BugInCF exception is
308+
* raised if the given constraints contains any {@link PreferenceConstraint}.
309+
*
310+
* For conversion of constraints containing {@link PreferenceConstraint}, use
311+
* {@link CnfVecIntSerializer#convertAll(Iterable, List, List)}
312+
*
313+
* @param constraints the constraints to convert
314+
* @param results the output clauses for the given constraints
315+
* @return same as {@code results}
316+
*/
295317
public List<VecInt> convertAll(Iterable<Constraint> constraints, List<VecInt> results) {
296318
for (Constraint constraint : constraints) {
319+
if (constraint instanceof PreferenceConstraint) {
320+
throw new BugInCF("CnfVecIntSerializer: adding PreferenceConstraint ( " + constraint +
321+
" ) to hard clauses is forbidden");
322+
}
297323
for (VecInt res : constraint.serialize(this)) {
298324
if (res.size() != 0) {
299325
results.add(res);
@@ -304,6 +330,28 @@ public List<VecInt> convertAll(Iterable<Constraint> constraints, List<VecInt> re
304330
return results;
305331
}
306332

333+
/**
334+
* Convert all the given mandatory constraints to hard clauses, and preference constraints
335+
* to soft clauses.
336+
*
337+
* @param constraints the constraints to convert
338+
* @param hardClauses the output hard clauses for the mandatory constraints
339+
* @param softClauses the output soft clauses for {@link PreferenceConstraint}
340+
*/
341+
public void convertAll(Iterable<Constraint> constraints, List<VecInt> hardClauses, List<VecInt> softClauses) {
342+
for (Constraint constraint : constraints) {
343+
for (VecInt res : constraint.serialize(this)) {
344+
if (res.size() != 0) {
345+
if (constraint instanceof PreferenceConstraint) {
346+
softClauses.add(res);
347+
} else {
348+
hardClauses.add(res);
349+
}
350+
}
351+
}
352+
}
353+
}
354+
307355
protected abstract boolean isTop(ConstantSlot constantSlot);
308356

309357
VecInt asVec(int ... vars) {

src/checkers/inference/solver/MaxSat2TypeSolver.java

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import java.util.Collection;
99
import java.util.HashMap;
10+
import java.util.HashSet;
11+
import java.util.LinkedList;
1012
import java.util.List;
1113
import java.util.Map;
1214

@@ -23,6 +25,8 @@
2325
import checkers.inference.model.Constraint;
2426
import checkers.inference.model.Slot;
2527
import 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

Comments
 (0)