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