forked from typetools/checker-framework-inference
-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathLingelingSolver.java
More file actions
154 lines (131 loc) · 5.7 KB
/
Copy pathLingelingSolver.java
File metadata and controls
154 lines (131 loc) · 5.7 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package checkers.inference.solver.backend.lingeling;
import org.sat4j.core.VecInt;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.lang.model.element.AnnotationMirror;
import checkers.inference.model.Constraint;
import checkers.inference.model.Slot;
import checkers.inference.solver.backend.maxsat.MaxSatFormatTranslator;
import checkers.inference.solver.backend.maxsat.MaxSatSolver;
import checkers.inference.solver.frontend.Lattice;
import checkers.inference.solver.util.ExternalSolverUtils;
import checkers.inference.solver.util.SolverEnvironment;
import checkers.inference.solver.util.Statistics;
/**
* LingelingSolver is also a MaxSatSolver but it calls Lingeling SAT solver to solve the clauses. It
* doesn't support soft constraint.
*/
public class LingelingSolver extends MaxSatSolver {
// Ensure the path to Lingeling binary executable file has been added into $PATH.
private final String lingeling = "lingeling";
// record cnf integers in clauses. lingeling solver give the answer for all
// the integers from 1 to the largest one. Some of them may be not in the
// clauses.
private Set<Integer> variableSet = new HashSet<Integer>();
private static AtomicInteger nth = new AtomicInteger(0);
private long serializationStart;
private long serializationEnd;
public LingelingSolver(
SolverEnvironment solverEnvironment,
Collection<Slot> slots,
Collection<Constraint> constraints,
MaxSatFormatTranslator formatTranslator,
Lattice lattice) {
super(solverEnvironment, slots, constraints, formatTranslator, lattice);
}
@Override
public Map<Integer, AnnotationMirror> solve() {
Map<Integer, AnnotationMirror> solutions = null;
this.serializationStart = System.currentTimeMillis();
encodeAllConstraints();
encodeWellFormednessRestriction();
this.serializationEnd = System.currentTimeMillis();
buildCNFInput();
collectVals();
recordData();
int localNth = nth.incrementAndGet();
writeCNFInput("cnfdata" + localNth + ".txt");
this.solvingStart = System.currentTimeMillis();
int[] resultArray = getSolverOutput(localNth);
// TODO What's the value of resultArray if there is no solution? Need to adapt this to
// changes in the PR: https://github.com/opprop/checker-framework-inference/pull/128
// , i.e. set solutions to null if there is no solution
solutions = decode(resultArray);
this.solvingEnd = System.currentTimeMillis();
long solvingTime = solvingEnd - solvingStart;
long serializationTime = serializationEnd - serializationStart;
Statistics.addOrIncrementEntry("sat_serialization_time(ms)", serializationTime);
Statistics.addOrIncrementEntry("sat_solving_time(ms)", solvingTime);
return solutions;
}
/**
* Create Lingeling process, and read output and error.
*
* @param localNth
* @return and int array, which stores truth assignment for CNF predicate.
*/
private int[] getSolverOutput(int localNth) {
String[] command = {lingeling, CNFData.getAbsolutePath() + "/cnfdata" + localNth + ".txt"};
final List<Integer> resultList = new ArrayList<Integer>();
ExternalSolverUtils.runExternalSolver(
command,
stdOut -> parseStdOut(stdOut, resultList),
stdErr -> ExternalSolverUtils.printStdStream(System.err, stdErr));
// Java 8 style of List<Integer> to int[] conversion
return resultList.stream().mapToInt(Integer::intValue).toArray();
}
private void parseStdOut(BufferedReader stdOut, List<Integer> resultList) {
String line;
try {
while ((line = stdOut.readLine()) != null) {
if (line.charAt(0) == 'v') {
for (String retval : line.split(" ")) {
if (!retval.equals("")
&& !retval.equals(" ")
&& !retval.equals("\n")
&& !retval.equals("v")) {
int val = Integer.parseInt(retval);
if (variableSet.contains(Math.abs(val))) {
resultList.add(val);
}
}
}
}
}
} catch (NumberFormatException | IOException e) {
e.printStackTrace();
}
}
/**
* For lingeling solve, it gives the solution from 1 to the largest variable. However, some
* numbers in this range may not has corresponding slot id. This method stores the variables
* that we really care about.
*/
private void collectVals() {
for (VecInt clause : this.hardClauses) {
int[] clauseArray = clause.toArray();
for (int i = 0; i < clauseArray.length; i++) {
variableSet.add(Math.abs(clauseArray[i]));
}
}
}
@Override
protected boolean shouldOutputCNF() {
// We need the CNF output to pass to Lingeling
// and so we unconditionally signal we want CNF output.
return true;
}
private void recordData() {
int totalClauses = hardClauses.size() + softClauses.size();
int totalVariable = variableSet.size();
Statistics.addOrIncrementEntry("cnf_clause_size", totalClauses);
Statistics.addOrIncrementEntry("cnf_variable_size", totalVariable);
}
}