1 /*******************************************************************************
2 * SAT4J: a SATisfiability library for Java Copyright (C) 2004, 2012 Artois University and CNRS
3 *
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Alternatively, the contents of this file may be used under the terms of
10 * either the GNU Lesser General Public License Version 2.1 or later (the
11 * "LGPL"), in which case the provisions of the LGPL are applicable instead
12 * of those above. If you wish to allow use of your version of this file only
13 * under the terms of the LGPL, and not to allow others to use your version of
14 * this file under the terms of the EPL, indicate your decision by deleting
15 * the provisions above and replace them with the notice and other provisions
16 * required by the LGPL. If you do not delete the provisions above, a recipient
17 * may use your version of this file under the terms of the EPL or the LGPL.
18 *
19 * Based on the original MiniSat specification from:
20 *
21 * An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the
22 * Sixth International Conference on Theory and Applications of Satisfiability
23 * Testing, LNCS 2919, pp 502-518, 2003.
24 *
25 * See www.minisat.se for the original solver in C++.
26 *
27 * Contributors:
28 * CRIL - initial API and implementation
29 *******************************************************************************/
30 package org.sat4j.reader;
31
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.PrintWriter;
35 import java.io.Serializable;
36
37 import org.sat4j.core.VecInt;
38 import org.sat4j.specs.ContradictionException;
39 import org.sat4j.specs.IProblem;
40 import org.sat4j.specs.ISolver;
41 import org.sat4j.specs.IVecInt;
42
43 /**
44 * Very simple Dimacs file parser. Allow solvers to read the constraints from a
45 * Dimacs formatted file. It should be used that way:
46 *
47 * <pre>
48 * DimacsReader solver = new DimacsReader(SolverFactory.OneSolver());
49 * solver.readInstance("mybench.cnf");
50 * if (solver.isSatisfiable()) {
51 * // SAT case
52 * } else {
53 * // UNSAT case
54 * }
55 * </pre>
56 *
57 * That parser is not used for efficiency reasons. It will be updated with Java
58 * 1.5 scanner feature.
59 *
60 * @version 1.0
61 * @author dlb
62 * @author or
63 */
64 public class DimacsReader extends Reader implements Serializable {
65
66 private static final long serialVersionUID = 1L;
67
68 protected int expectedNbOfConstr; // as announced on the p cnf line
69
70 protected final ISolver solver;
71
72 private boolean checkConstrNb = true;
73
74 protected final String formatString;
75
76 /**
77 * @since 2.1
78 */
79 protected EfficientScanner scanner;
80
81 public DimacsReader(ISolver solver) {
82 this(solver, "cnf");
83 }
84
85 public DimacsReader(ISolver solver, String format) {
86 this.solver = solver;
87 this.formatString = format;
88 }
89
90 public void disableNumberOfConstraintCheck() {
91 this.checkConstrNb = false;
92 }
93
94 /**
95 * Skip comments at the beginning of the input stream.
96 *
97 * @param in
98 * the input stream
99 * @throws IOException
100 * if an IO problem occurs.
101 * @since 2.1
102 */
103 protected void skipComments() throws IOException {
104 this.scanner.skipComments();
105 }
106
107 /**
108 * @param in
109 * the input stream
110 * @throws IOException
111 * iff an IO occurs
112 * @throws ParseFormatException
113 * if the input stream does not comply with the DIMACS format.
114 * @since 2.1
115 */
116 protected void readProblemLine() throws IOException, ParseFormatException {
117
118 String line = this.scanner.nextLine().trim();
119
120 if (line == null) {
121 throw new ParseFormatException(
122 "premature end of file: <p cnf ...> expected");
123 }
124 String[] tokens = line.split("\\s+");
125 if (tokens.length < 4 || !"p".equals(tokens[0])
126 || !this.formatString.equals(tokens[1])) {
127 throw new ParseFormatException("problem line expected (p cnf ...)");
128 }
129
130 int vars;
131
132 // reads the max var id
133 vars = Integer.parseInt(tokens[2]);
134 assert vars > 0;
135 this.solver.newVar(vars);
136 // reads the number of clauses
137 this.expectedNbOfConstr = Integer.parseInt(tokens[3]);
138 assert this.expectedNbOfConstr > 0;
139 this.solver.setExpectedNumberOfClauses(this.expectedNbOfConstr);
140 }
141
142 /**
143 * @since 2.1
144 */
145 protected IVecInt literals = new VecInt();
146
147 /**
148 * @param in
149 * the input stream
150 * @throws IOException
151 * iff an IO problems occurs
152 * @throws ParseFormatException
153 * if the input stream does not comply with the DIMACS format.
154 * @throws ContradictionException
155 * si le probl?me est trivialement inconsistant.
156 * @since 2.1
157 */
158 protected void readConstrs() throws IOException, ParseFormatException,
159 ContradictionException {
160 int realNbOfConstr = 0;
161
162 this.literals.clear();
163 boolean needToContinue = true;
164
165 while (needToContinue) {
166 boolean added = false;
167 if (this.scanner.eof()) {
168 // end of file
169 if (this.literals.size() > 0) {
170 // no 0 end the last clause
171 flushConstraint();
172 added = true;
173 }
174 needToContinue = false;
175 } else {
176 if (this.scanner.currentChar() == 'c') {
177 // ignore comment line
178 this.scanner.skipRestOfLine();
179 continue;
180 }
181 if (this.scanner.currentChar() == '%'
182 && this.expectedNbOfConstr == realNbOfConstr) {
183 if (this.solver.isVerbose()) {
184 System.out
185 .println("Ignoring the rest of the file (SATLIB format");
186 }
187 break;
188 }
189 added = handleLine();
190 }
191 if (added) {
192 realNbOfConstr++;
193 }
194 }
195 if (this.checkConstrNb && this.expectedNbOfConstr != realNbOfConstr) {
196 throw new ParseFormatException("wrong nbclauses parameter. Found "
197 + realNbOfConstr + ", " + this.expectedNbOfConstr
198 + " expected");
199 }
200 }
201
202 /**
203 *
204 * @throws ContradictionException
205 * @since 2.1
206 */
207 protected void flushConstraint() throws ContradictionException {
208 try {
209 this.solver.addClause(this.literals);
210 } catch (IllegalArgumentException ex) {
211 if (isVerbose()) {
212 System.err.println("c Skipping constraint " + this.literals);
213 }
214 }
215 }
216
217 /**
218 * @since 2.1
219 */
220 protected boolean handleLine() throws ContradictionException, IOException,
221 ParseFormatException {
222 int lit;
223 boolean added = false;
224 while (!this.scanner.eof()) {
225 lit = this.scanner.nextInt();
226 if (lit == 0) {
227 if (this.literals.size() > 0) {
228 flushConstraint();
229 this.literals.clear();
230 added = true;
231 }
232 break;
233 }
234 this.literals.push(lit);
235 }
236 return added;
237 }
238
239 @Override
240 public IProblem parseInstance(InputStream in) throws ParseFormatException,
241 ContradictionException, IOException {
242 this.scanner = new EfficientScanner(in);
243 return parseInstance();
244 }
245
246 /**
247 * @param in
248 * the input stream
249 * @throws ParseFormatException
250 * if the input stream does not comply with the DIMACS format.
251 * @throws ContradictionException
252 * si le probl?me est trivialement inconsitant
253 */
254 private IProblem parseInstance() throws ParseFormatException,
255 ContradictionException {
256 this.solver.reset();
257 try {
258 skipComments();
259 readProblemLine();
260 readConstrs();
261 this.scanner.close();
262 return this.solver;
263 } catch (IOException e) {
264 throw new ParseFormatException(e);
265 } catch (NumberFormatException e) {
266 throw new ParseFormatException("integer value expected ");
267 }
268 }
269
270 @Override
271 public String decode(int[] model) {
272 StringBuffer stb = new StringBuffer();
273 for (int element : model) {
274 stb.append(element);
275 stb.append(" ");
276 }
277 stb.append("0");
278 return stb.toString();
279 }
280
281 @Override
282 public void decode(int[] model, PrintWriter out) {
283 for (int element : model) {
284 out.print(element);
285 out.print(" ");
286 }
287 out.print("0");
288 }
289
290 protected ISolver getSolver() {
291 return this.solver;
292 }
293 }