View Javadoc

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(&quot;mybench.cnf&quot;);
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 }