/* * Kodkod -- Copyright (c) 2005-present, Emina Torlak * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package kodkod.engine.satlab; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.sat4j.minisat.SolverFactory; /** * A factory for generating SATSolver instances of a given type. * Built-in support is provided for many solvers, including * <a href="http://www.sat4j.org/">SAT4J</a> * and the <a href="http://www.cs.chalmers.se/Cs/Research/FormalMethods/MiniSat/">MiniSat</a> * solver by Niklas Eén and Niklas Sörensson. * @author Emina Torlak */ public abstract class SATFactory { /** * Constructs a new instance of SATFactory. */ protected SATFactory() {} /** * Returns true iff the given factory generates solvers that * are available for use on this system. * @return true iff the given factory generates solvers that * are available for use on this system. */ public static final boolean available(SATFactory factory) { SATSolver solver = null; try { solver = factory.instance(); solver.addVariables(1); solver.addClause(new int[]{1}); return solver.solve(); } catch (RuntimeException|UnsatisfiedLinkError t) { return false; } finally { if (solver!=null) { solver.free(); } } } /** * The factory that produces instances of the default sat4j solver. * @see org.sat4j.core.ASolverFactory#defaultSolver() */ public static final SATFactory DefaultSAT4J = new SATFactory() { public SATSolver instance() { return new SAT4J(SolverFactory.instance().defaultSolver()); } public String toString() { return "DefaultSAT4J"; } }; /** * The factory that produces instances of the "light" sat4j solver. The * light solver is suitable for solving many small instances of SAT problems. * @see org.sat4j.core.ASolverFactory#lightSolver() */ public static final SATFactory LightSAT4J = new SATFactory() { public SATSolver instance() { return new SAT4J(SolverFactory.instance().lightSolver()); } public String toString() { return "LightSAT4J"; } }; /** * The factory that produces instances of Niklas Eén and Niklas Sörensson's * MiniSat solver. */ public static final SATFactory MiniSat = new SATFactory() { public SATSolver instance() { return new MiniSat(); } public String toString() { return "MiniSat"; } }; /** * The factory the produces {@link SATProver proof logging} * instances of the MiniSat solver. Note that core * extraction can incur a significant time overhead during solving, * so if you do not need this functionality, use the {@link #MiniSat} factory * instead. */ public static final SATFactory MiniSatProver = new SATFactory() { public SATSolver instance() { return new MiniSatProver(); } @Override public boolean prover() { return true; } public String toString() { return "MiniSatProver"; } }; /** * The factory that produces instances of Gilles Audemard and Laurent Simon's * Glucose solver. */ public static final SATFactory Glucose = new SATFactory() { public SATSolver instance() { return new Glucose(); } public String toString() { return "Glucose"; } }; /** * The factory that produces instances of Armin Biere's * Lingeling solver. */ public static final SATFactory Lingeling = new SATFactory() { public SATSolver instance() { return new Lingeling(); } public boolean incremental() { return false; } public String toString() { return "Lingeling"; } }; /** * Returns a SATFactory that produces SATSolver wrappers for Armin Biere's Plingeling * solver. This is a parallel solver that is invoked as an external program rather than * via the Java Native Interface. As a result, it cannot be used incrementally. Its * external factory manages the creation and deletion of temporary files automatically. * A statically compiled version of plingeling is assumed to be available in a * java.library.path directory. The effect of this method is the same as calling * {@link #plingeling(Integer, Boolean) plingeling(null, null)}. * @return SATFactory that produces SATSolver wrappers for the Plingeling solver */ public static final SATFactory plingeling() { return plingeling(null, null); } /** * Returns a SATFactory that produces SATSolver wrappers for Armin Biere's Plingeling * solver. This is a parallel solver that is invoked as an external program rather than * via the Java Native Interface. As a result, it cannot be used incrementally. Its * external factory manages the creation and deletion of temporary files automatically. * A statically compiled version of plingeling is assumed to be available in a * java.library.path directory. * * <p>Plingling takes as input two optional parameters: {@code threads}, specifying how * many worker threads to use, and {@code portfolio}, specifying whether the threads should * run in portfolio mode (no sharing of clauses) or sharing mode. If {@code threads} * is null, the solver uses one worker per core. If {@code portfolio} is null, it is set to * true by default.</p> * * @requires threads != null => numberOfThreads > 0 * * @return SATFactory that produces SATSolver wrappers for the Plingeling solver */ public static final SATFactory plingeling(Integer threads, Boolean portfolio) { final List<String> opts = new ArrayList<String>(3); if (threads!=null) { if (threads < 1) throw new IllegalArgumentException("Number of threads must be at least 1: numberOfThreads=" + threads); opts.add("-t"); opts.add(threads.toString()); } if (portfolio!=null && portfolio) opts.add("-p"); final String executable = findStaticLibrary("plingeling"); return externalFactory(executable==null ? "plingeling" : executable, null, opts.toArray(new String[opts.size()])); } /** * Searches the {@code java.library.path} for an executable with the given name. Returns a fully * qualified path to the first found executable. Otherwise returns null. * @return a fully qualified path to an executable with the given name, or null if no executable * is found. */ private static String findStaticLibrary(String name) { final String[] dirs = System.getProperty("java.library.path").split(System.getProperty("path.separator")); for(int i = dirs.length-1; i >= 0; i--) { final File file = new File(dirs[i]+File.separator+name); if (file.canExecute()) return file.getAbsolutePath(); } return null; } /** * Returns a SATFactory that produces instances of the specified * SAT4J solver. For the list of available SAT4J solvers see * {@link org.sat4j.core.ASolverFactory#solverNames() org.sat4j.core.ASolverFactory#solverNames()}. * @requires solverName is a valid solver name * @return a SATFactory that returns the instances of the specified * SAT4J solver * @see org.sat4j.core.ASolverFactory#solverNames() */ public static final SATFactory sat4jFactory(final String solverName) { return new SATFactory() { @Override public SATSolver instance() { return new SAT4J(SolverFactory.instance().createSolverByName(solverName)); } public String toString() { return solverName; } }; } /** * Returns a SATFactory that produces SATSolver wrappers for the external * SAT solver specified by the executable parameter. The solver's input * and output formats must conform to the * <a href="http://www.satcompetition.org/2011/rules.pdf">SAT competition standards</a>. The solver * will be called with the specified options, and it is expected to write properly formatted * output to standard out. If the {@code cnf} string is non-null, it will be * used as the file name for generated CNF files by all solver instances that the factory generates. * If {@code cnf} null, each solver instance will use an automatically generated temporary file, which * will be deleted when the solver instance is garbage-collected. The {@code cnf} file, if provided, is not * automatically deleted; it is the caller's responsibility to delete it when no longer needed. * External solvers are never incremental. * @return SATFactory that produces SATSolver wrappers for the specified external * SAT solver */ public static final SATFactory externalFactory(final String executable, final String cnf, final String... options) { return new SATFactory() { @Override public SATSolver instance() { if (cnf != null) { return new ExternalSolver(executable, cnf, false, options); } else { try { return new ExternalSolver(executable, File.createTempFile("kodkod", String.valueOf(executable.hashCode())).getAbsolutePath(), true, options); } catch (IOException e) { throw new SATAbortedException("Could not create a temporary file.", e); } } } @Override public boolean incremental() { return false; } public String toString() { return (new File(executable)).getName(); } }; } /** * Returns an instance of a SATSolver produced by this factory. * @return a SATSolver instance */ public abstract SATSolver instance(); /** * Returns true if the solvers returned by this.instance() are * {@link SATProver SATProvers}. Otherwise returns false. * @return true if the solvers returned by this.instance() are * {@link SATProver SATProvers}. Otherwise returns false. */ public boolean prover() { return false; } /** * Returns true if the solvers returned by this.instance() are incremental; * i.e. if clauses/variables can be added to the solver between multiple * calls to solve(). * @return true if the solvers returned by this.instance() are incremental */ public boolean incremental() { return true; } }