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;
31  
32  import java.io.BufferedReader;
33  import java.io.FileNotFoundException;
34  import java.io.IOException;
35  import java.io.InputStreamReader;
36  import java.io.ObjectInputStream;
37  import java.io.PrintWriter;
38  import java.io.Serializable;
39  import java.net.URL;
40  import java.util.Properties;
41  
42  import org.sat4j.core.ASolverFactory;
43  import org.sat4j.reader.ParseFormatException;
44  import org.sat4j.reader.Reader;
45  import org.sat4j.specs.ContradictionException;
46  import org.sat4j.specs.IProblem;
47  import org.sat4j.specs.ISolver;
48  import org.sat4j.specs.TimeoutException;
49  
50  /**
51   * That class is used by launchers used to solve decision problems, i.e.
52   * problems with YES/NO/UNKNOWN answers.
53   * 
54   * @author leberre
55   * 
56   */
57  public abstract class AbstractLauncher implements Serializable {
58  
59      /**
60  	 * 
61  	 */
62      private static final long serialVersionUID = 1L;
63  
64      public static final String SOLUTION_PREFIX = "v "; //$NON-NLS-1$
65  
66      public static final String ANSWER_PREFIX = "s "; //$NON-NLS-1$
67  
68      public static final String COMMENT_PREFIX = "c "; //$NON-NLS-1$
69  
70      protected long beginTime;
71  
72      protected ExitCode exitCode = ExitCode.UNKNOWN;
73  
74      protected Reader reader;
75  
76      protected transient PrintWriter out = new PrintWriter(System.out, true);
77  
78      protected transient Thread shutdownHook = new Thread() {
79          @Override
80          public void run() {
81              displayResult();
82          }
83      };
84  
85      protected ISolver solver;
86  
87      public boolean silent = false;
88  
89      protected boolean prime = System.getProperty("prime") != null;;
90  
91      protected AbstractLauncher() {
92          Runtime.getRuntime().addShutdownHook(this.shutdownHook);
93      }
94  
95      protected void displayResult() {
96          if (this.solver != null) {
97              System.out.flush();
98              this.out.flush();
99              double wallclocktime = (System.currentTimeMillis() - this.beginTime) / 1000.0;
100             this.solver.printStat(this.out, COMMENT_PREFIX);
101             this.solver.printInfos(this.out, COMMENT_PREFIX);
102             this.out.println(ANSWER_PREFIX + this.exitCode);
103             if (this.exitCode == ExitCode.SATISFIABLE) {
104                 int[] model = this.solver.model();
105                 if (this.prime) {
106                     int initiallength = model.length;
107                     log("returning a prime implicant ...");
108                     long beginpi = System.currentTimeMillis();
109                     model = this.solver.primeImplicant();
110                     long endpi = System.currentTimeMillis();
111                     log("removed " + (initiallength - model.length)
112                             + " literals");
113                     log("pi computation time: " + (endpi - beginpi) + " ms");
114                 }
115                 this.out.print(SOLUTION_PREFIX);
116                 this.reader.decode(model, this.out);
117                 this.out.println();
118             }
119             log("Total wall clock time (in seconds) : " + wallclocktime); //$NON-NLS-1$
120         }
121     }
122 
123     public abstract void usage();
124 
125     /**
126      * @throws IOException
127      */
128     protected final void displayHeader() {
129         displayLicense();
130         URL url = AbstractLauncher.class.getResource("/sat4j.version"); //$NON-NLS-1$
131         if (url == null) {
132             log("no version file found!!!"); //$NON-NLS-1$			
133         } else {
134             BufferedReader in = null;
135             try {
136                 in = new BufferedReader(new InputStreamReader(url.openStream()));
137                 log("version " + in.readLine()); //$NON-NLS-1$
138             } catch (IOException e) {
139                 log("c ERROR: " + e.getMessage());
140             } finally {
141                 if (in != null) {
142                     try {
143                         in.close();
144                     } catch (IOException e) {
145                         log("c ERROR: " + e.getMessage());
146                     }
147                 }
148             }
149         }
150         Properties prop = System.getProperties();
151         String[] infoskeys = {
152                 "java.runtime.name", "java.vm.name", "java.vm.version", "java.vm.vendor", "sun.arch.data.model", "java.version", "os.name", "os.version", "os.arch" }; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$//$NON-NLS-5$
153         for (String key : infoskeys) {
154             log(key
155                     + (key.length() < 14 ? "\t\t" : "\t") + prop.getProperty(key)); //$NON-NLS-1$
156         }
157         Runtime runtime = Runtime.getRuntime();
158         log("Free memory \t\t" + runtime.freeMemory()); //$NON-NLS-1$
159         log("Max memory \t\t" + runtime.maxMemory()); //$NON-NLS-1$
160         log("Total memory \t\t" + runtime.totalMemory()); //$NON-NLS-1$
161         log("Number of processors \t" + runtime.availableProcessors()); //$NON-NLS-1$
162     }
163 
164     public void displayLicense() {
165         log("SAT4J: a SATisfiability library for Java (c) 2004-2012 Artois University and CNRS"); //$NON-NLS-1$
166         log("This is free software under the dual EPL/GNU LGPL licenses."); //$NON-NLS-1$
167         log("See www.sat4j.org for details."); //$NON-NLS-1$
168     }
169 
170     /**
171      * Reads a problem file from the command line.
172      * 
173      * @param problemname
174      *            the fully qualified name of the problem.
175      * @return a reference to the problem to solve
176      * @throws FileNotFoundException
177      *             if the file is not found
178      * @throws ParseFormatException
179      *             if the problem is not expressed using the right format
180      * @throws IOException
181      *             for other IO problems
182      * @throws ContradictionException
183      *             if the problem is found trivially unsat
184      */
185     protected IProblem readProblem(String problemname)
186             throws FileNotFoundException, ParseFormatException, IOException,
187             ContradictionException {
188         log("solving " + problemname); //$NON-NLS-1$
189         log("reading problem ... "); //$NON-NLS-1$
190         this.reader = createReader(this.solver, problemname);
191         IProblem problem = this.reader.parseInstance(problemname);
192         log("... done. Wall clock time " //$NON-NLS-1$
193                 + (System.currentTimeMillis() - this.beginTime) / 1000.0 + "s."); //$NON-NLS-1$
194         log("declared #vars     " + problem.nVars()); //$NON-NLS-1$
195         if (this.solver.nVars() < this.solver.realNumberOfVariables()) {
196             log("internal #vars     " + this.solver.realNumberOfVariables()); //$NON-NLS-1$
197         }
198         log("#constraints  " + problem.nConstraints()); //$NON-NLS-1$
199         problem.printInfos(this.out, COMMENT_PREFIX);
200         return problem;
201     }
202 
203     protected abstract Reader createReader(ISolver theSolver, String problemname);
204 
205     public void run(String[] args) {
206 
207         try {
208             displayHeader();
209             this.solver = configureSolver(args);
210             if (this.solver == null) {
211                 usage();
212                 return;
213             }
214             if (!this.silent) {
215                 this.solver.setVerbose(true);
216             }
217             String instanceName = getInstanceName(args);
218             if (instanceName == null) {
219                 usage();
220                 return;
221             }
222             this.beginTime = System.currentTimeMillis();
223             readProblem(instanceName);
224             try {
225                 solve(this.solver);
226             } catch (TimeoutException e) {
227                 log("timeout"); //$NON-NLS-1$
228             }
229         } catch (FileNotFoundException e) {
230             System.err.println("FATAL " + e.getLocalizedMessage());
231         } catch (IOException e) {
232             System.err.println("FATAL " + e.getLocalizedMessage());
233         } catch (ContradictionException e) {
234             this.exitCode = ExitCode.UNSATISFIABLE;
235             log("(trivial inconsistency)"); //$NON-NLS-1$
236         } catch (ParseFormatException e) {
237             System.err.println("FATAL " + e.getLocalizedMessage());
238         }
239     }
240 
241     protected abstract String getInstanceName(String[] args);
242 
243     protected abstract ISolver configureSolver(String[] args);
244 
245     /**
246      * Display messages as comments on STDOUT
247      * 
248      * @param message
249      */
250     public void log(String message) {
251         if (!this.silent) {
252             this.out.println(COMMENT_PREFIX + message);
253         }
254     }
255 
256     protected void solve(IProblem problem) throws TimeoutException {
257         this.exitCode = problem.isSatisfiable() ? ExitCode.SATISFIABLE
258                 : ExitCode.UNSATISFIABLE;
259     }
260 
261     /**
262      * Change the value of the exit code in the Launcher
263      * 
264      * @param exitCode
265      *            the new ExitCode
266      */
267     public final void setExitCode(ExitCode exitCode) {
268         this.exitCode = exitCode;
269     }
270 
271     /**
272      * Get the value of the ExitCode
273      * 
274      * @return the current value of the Exitcode
275      */
276     public final ExitCode getExitCode() {
277         return this.exitCode;
278     }
279 
280     /**
281      * Obtaining the current time spent since the beginning of the solving
282      * process.
283      * 
284      * @return the time signature at the beginning of the run() method.
285      */
286     public final long getBeginTime() {
287         return this.beginTime;
288     }
289 
290     /**
291      * 
292      * @return the reader used to parse the instance
293      */
294     public final Reader getReader() {
295         return this.reader;
296     }
297 
298     /**
299      * To change the output stream on which statistics are displayed. By
300      * default, the solver displays everything on System.out.
301      * 
302      * @param out
303      */
304     public void setLogWriter(PrintWriter out) {
305         this.out = out;
306     }
307 
308     public PrintWriter getLogWriter() {
309         return this.out;
310     }
311 
312     protected void setSilent(boolean b) {
313         this.silent = b;
314     }
315 
316     private void readObject(ObjectInputStream stream) throws IOException,
317             ClassNotFoundException {
318         stream.defaultReadObject();
319         this.out = new PrintWriter(System.out, true);
320         this.shutdownHook = new Thread() {
321             @Override
322             public void run() {
323                 displayResult();
324             }
325         };
326     }
327 
328     protected <T extends ISolver> void showAvailableSolvers(
329             ASolverFactory<T> afactory) {
330         // if (afactory != null) {
331         //			log("Available solvers: "); //$NON-NLS-1$
332         // String[] names = afactory.solverNames();
333         // for (int i = 0; i < names.length; i++) {
334         // log(names[i]);
335         // }
336         // }
337         showAvailableSolvers(afactory, "");
338     }
339 
340     protected <T extends ISolver> void showAvailableSolvers(
341             ASolverFactory<T> afactory, String framework) {
342         if (afactory != null) {
343             if (framework.length() > 0) {
344                 log("Available solvers for " + framework + ": "); //$NON-NLS-1$
345             } else {
346                 log("Available solvers: "); //$NON-NLS-1$
347             }
348             String[] names = afactory.solverNames();
349             for (String name : names) {
350                 log(name);
351             }
352         }
353     }
354 
355 }