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.ILogAble;
47  import org.sat4j.specs.IProblem;
48  import org.sat4j.specs.ISolver;
49  import org.sat4j.specs.ISolverService;
50  import org.sat4j.specs.TimeoutException;
51  import org.sat4j.tools.ModelIteratorToSATAdapter;
52  import org.sat4j.tools.RupSearchListener;
53  import org.sat4j.tools.SearchEnumeratorListener;
54  import org.sat4j.tools.SearchMinOneListener;
55  
56  /**
57   * That class is used by launchers used to solve decision problems, i.e.
58   * problems with YES/NO/UNKNOWN answers.
59   * 
60   * @author leberre
61   * 
62   */
63  public abstract class AbstractLauncher implements Serializable, ILogAble {
64  
65      /**
66  	 * 
67  	 */
68      private static final long serialVersionUID = 1L;
69  
70      public static final String COMMENT_PREFIX = "c "; //$NON-NLS-1$
71  
72      protected long beginTime;
73  
74      protected ExitCode exitCode = ExitCode.UNKNOWN;
75  
76      protected Reader reader;
77  
78      protected transient PrintWriter out = new PrintWriter(System.out, true);
79  
80      private boolean displaySolutionLine = true;
81  
82      protected transient Thread shutdownHook = new Thread() {
83          @Override
84          public void run() {
85              displayResult();
86          }
87      };
88  
89      protected ISolver solver;
90  
91      protected IProblem problem;
92  
93      public boolean silent = false;
94  
95      protected boolean prime = System.getProperty("prime") != null;
96  
97      private ILauncherMode launcherMode = ILauncherMode.DECISION;
98  
99      protected void setLauncherMode(ILauncherMode launcherMode) {
100         this.launcherMode = launcherMode;
101     }
102 
103     protected void setIncomplete(boolean isIncomplete) {
104         this.launcherMode.setIncomplete(isIncomplete);
105     }
106 
107     protected AbstractLauncher() {
108         Runtime.getRuntime().addShutdownHook(this.shutdownHook);
109     }
110 
111     protected void displayResult() {
112         launcherMode.displayResult(solver, problem, this, out, reader,
113                 beginTime, displaySolutionLine);
114     }
115 
116     public abstract void usage();
117 
118     /**
119      * @throws IOException
120      */
121     protected final void displayHeader() {
122         displayLicense();
123         URL url = AbstractLauncher.class.getResource("/sat4j.version"); //$NON-NLS-1$
124         if (url == null) {
125             log("no version file found!!!"); //$NON-NLS-1$                      
126         } else {
127             BufferedReader in = null;
128             try {
129                 in = new BufferedReader(new InputStreamReader(url.openStream()));
130                 log("version " + in.readLine()); //$NON-NLS-1$
131             } catch (IOException e) {
132                 log("c ERROR: " + e.getMessage());
133             } finally {
134                 if (in != null) {
135                     try {
136                         in.close();
137                     } catch (IOException e) {
138                         log("c ERROR: " + e.getMessage());
139                     }
140                 }
141             }
142         }
143         Properties prop = System.getProperties();
144         String[] infoskeys = {
145                 "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$
146         for (String key : infoskeys) {
147             log(key
148                     + (key.length() < 14 ? "\t\t" : "\t") + prop.getProperty(key)); //$NON-NLS-1$
149         }
150         Runtime runtime = Runtime.getRuntime();
151         log("Free memory \t\t" + runtime.freeMemory()); //$NON-NLS-1$
152         log("Max memory \t\t" + runtime.maxMemory()); //$NON-NLS-1$
153         log("Total memory \t\t" + runtime.totalMemory()); //$NON-NLS-1$
154         log("Number of processors \t" + runtime.availableProcessors()); //$NON-NLS-1$
155     }
156 
157     public void displayLicense() {
158         log("SAT4J: a SATisfiability library for Java (c) 2004-2013 Artois University and CNRS"); //$NON-NLS-1$
159         log("This is free software under the dual EPL/GNU LGPL licenses."); //$NON-NLS-1$
160         log("See www.sat4j.org for details."); //$NON-NLS-1$
161     }
162 
163     /**
164      * Reads a problem file from the command line.
165      * 
166      * @param problemname
167      *            the fully qualified name of the problem.
168      * @return a reference to the problem to solve
169      * @throws ParseFormatException
170      *             if the problem is not expressed using the right format
171      * @throws IOException
172      *             for other IO problems
173      * @throws ContradictionException
174      *             if the problem is found trivially unsat
175      */
176     protected IProblem readProblem(String problemname)
177             throws ParseFormatException, IOException, ContradictionException {
178         log("solving " + problemname); //$NON-NLS-1$
179         log("reading problem ... "); //$NON-NLS-1$
180         this.reader = createReader(this.solver, problemname);
181         IProblem aProblem = this.reader.parseInstance(problemname);
182         log("... done. Wall clock time " //$NON-NLS-1$
183                 + (System.currentTimeMillis() - this.beginTime) / 1000.0 + "s."); //$NON-NLS-1$
184         log("declared #vars     " + aProblem.nVars()); //$NON-NLS-1$
185         if (this.solver.nVars() < this.solver.realNumberOfVariables()) {
186             log("internal #vars     " + this.solver.realNumberOfVariables()); //$NON-NLS-1$
187         }
188         log("#constraints  " + aProblem.nConstraints()); //$NON-NLS-1$
189         aProblem.printInfos(this.out);
190         if (System.getProperty("UNSATPROOF") != null) {
191             String proofFile = problemname + ".rupproof";
192             this.solver
193                     .setSearchListener(new RupSearchListener<ISolverService>(
194                             proofFile));
195             if (!this.silent) {
196                 System.out.println(this.solver.getLogPrefix()
197                         + "Generating unsat proof in file " + proofFile);
198             }
199         }
200         return aProblem;
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 all = System.getProperty("all");
218             if (all != null) {
219                 if ("external".equals(all)) {
220                     this.solver = new ModelIteratorToSATAdapter(this.solver,
221                             launcherMode);
222                     System.out.println(this.solver.getLogPrefix()
223                             + "model enumeration using the external way");
224                 } else {
225                     SearchEnumeratorListener enumerator = new SearchEnumeratorListener(
226                             launcherMode);
227                     this.solver.setSearchListener(enumerator);
228                     System.out.println(this.solver.getLogPrefix()
229                             + "model enumeration using the internal way");
230                 }
231             }
232             if (System.getProperty("minone") != null) {
233                 SearchMinOneListener minone = new SearchMinOneListener(
234                         launcherMode);
235                 this.solver.setSearchListener(minone);
236             }
237             String instanceName = getInstanceName(args);
238             if (instanceName == null) {
239                 usage();
240                 return;
241             }
242             this.beginTime = System.currentTimeMillis();
243             this.problem = readProblem(instanceName);
244             try {
245                 solve(this.problem);
246             } catch (TimeoutException e) {
247                 log("timeout"); //$NON-NLS-1$
248             }
249         } catch (FileNotFoundException e) {
250             System.err.println("FATAL " + e.getLocalizedMessage());
251         } catch (IOException e) {
252             System.err.println("FATAL " + e.getLocalizedMessage());
253         } catch (ContradictionException e) {
254             this.exitCode = ExitCode.UNSATISFIABLE;
255             this.launcherMode.setExitCode(ExitCode.UNSATISFIABLE);
256             log("(trivial inconsistency)"); //$NON-NLS-1$
257         } catch (ParseFormatException e) {
258             System.err.println("FATAL " + e.getLocalizedMessage());
259         }
260     }
261 
262     protected abstract String getInstanceName(String[] args);
263 
264     protected abstract ISolver configureSolver(String[] args);
265 
266     /**
267      * Display messages as comments on STDOUT
268      * 
269      * @param message
270      */
271     public void log(String message) {
272         if (!this.silent) {
273             this.out.println(COMMENT_PREFIX + message);
274         }
275     }
276 
277     protected void solve(IProblem problem) throws TimeoutException {
278         launcherMode.solve(problem, reader, this, out, beginTime);
279         this.setExitCode(launcherMode.getCurrentExitCode());
280     }
281 
282     /**
283      * To change the display so that solution line appears or not. Recommended
284      * if solution is very large.
285      * 
286      * @param value
287      */
288     protected void setDisplaySolutionLine(boolean value) {
289         this.displaySolutionLine = value;
290     }
291 
292     /**
293      * Change the value of the exit code in the Launcher
294      * 
295      * @param exitCode
296      *            the new ExitCode
297      */
298     public final void setExitCode(ExitCode exitCode) {
299         this.exitCode = exitCode;
300     }
301 
302     /**
303      * Get the value of the ExitCode
304      * 
305      * @return the current value of the Exitcode
306      */
307     public final ExitCode getExitCode() {
308         return this.exitCode;
309     }
310 
311     /**
312      * Obtaining the current time spent since the beginning of the solving
313      * process.
314      * 
315      * @return the time signature at the beginning of the run() method.
316      */
317     public final long getBeginTime() {
318         return this.beginTime;
319     }
320 
321     /**
322      * 
323      * @return the reader used to parse the instance
324      */
325     public final Reader getReader() {
326         return this.reader;
327     }
328 
329     /**
330      * To change the output stream on which statistics are displayed. By
331      * default, the solver displays everything on System.out.
332      * 
333      * @param out
334      */
335     public void setLogWriter(PrintWriter out) {
336         this.out = out;
337     }
338 
339     public PrintWriter getLogWriter() {
340         return this.out;
341     }
342 
343     protected void setSilent(boolean b) {
344         this.silent = b;
345     }
346 
347     private void readObject(ObjectInputStream stream) throws IOException,
348             ClassNotFoundException {
349         stream.defaultReadObject();
350         this.out = new PrintWriter(System.out, true);
351         this.shutdownHook = new Thread() {
352             @Override
353             public void run() {
354                 displayResult();
355             }
356         };
357     }
358 
359     protected <T extends ISolver> void showAvailableSolvers(
360             ASolverFactory<T> afactory) {
361         // if (afactory != null) {
362         //                      log("Available solvers: "); //$NON-NLS-1$
363         // String[] names = afactory.solverNames();
364         // for (int i = 0; i < names.length; i++) {
365         // log(names[i]);
366         // }
367         // }
368         showAvailableSolvers(afactory, "");
369     }
370 
371     protected <T extends ISolver> void showAvailableSolvers(
372             ASolverFactory<T> afactory, String framework) {
373         if (afactory != null) {
374             if (framework.length() > 0) {
375                 log("Available solvers for " + framework + ": "); //$NON-NLS-1$
376             } else {
377                 log("Available solvers: "); //$NON-NLS-1$
378             }
379             String[] names = afactory.solverNames();
380             for (String name : names) {
381                 log(name);
382             }
383         }
384     }
385 }