/* * 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; /** * A skeleton implementation of a wrapper for a sat solver * accessed through JNI. * * @author Emina Torlak */ abstract class NativeSolver implements SATSolver { /** * The memory address of the native instance wrapped by this wrapper. */ private long peer; private Boolean sat; private int clauses, vars; /** * Constructs a new wrapper for the given * instance of the native solver. */ NativeSolver(long peer) { this.peer = peer; this.clauses = this.vars = 0; this.sat = null; // System.out.println("created " + peer); } /** * Loads the JNI library for the given class. * It first attempts to load the library named library.getSimpleName().toLowerCase(). * If that fails, it attempts to load library.getSimpleName().toLowerCase()+suffix * where suffix ranges over the path-separator delimited list obtained by * calling System.getProperty("kodkod." + library.getSimpleName().toLowerCase()). */ static void loadLibrary(Class<? extends NativeSolver> library) { final String name = library.getSimpleName().toLowerCase(); try { System.loadLibrary(name); } catch (UnsatisfiedLinkError e) { final String versions = System.getProperty("kodkod." + name); if (versions != null) { for(String suffix : versions.split(File.pathSeparator)) { try { System.loadLibrary(name+suffix); return; } catch (UnsatisfiedLinkError e1) { } } } throw new UnsatisfiedLinkError("Could not load the library " + System.mapLibraryName(name) + " or any of its variants:" + e.getMessage()); } } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#numberOfVariables() */ public final int numberOfVariables() { return vars; } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#numberOfClauses() */ public final int numberOfClauses() { return clauses; } /** * Adjusts the internal clause count so that the next call to {@linkplain #numberOfClauses()} * will return the given value. * @requires clauseCount >= 0 * @ensures adjusts the internal clause so that the next call to {@linkplain #numberOfClauses()} * will return the given value. */ final void adjustClauseCount(int clauseCount) { assert clauseCount >= 0; clauses = clauseCount; } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#addVariables(int) * @see #addVariables(long, int) */ public final void addVariables(int numVars) { if (numVars < 0) throw new IllegalArgumentException("vars < 0: " + numVars); else if (numVars > 0) { vars += numVars; addVariables(peer, numVars); } } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#addClause(int[]) * @see #addClause(long, int[]) */ public final boolean addClause(int[] lits) { if (addClause(peer, lits)) { // for(int i : lits) { // System.out.print(i + " "); // } // System.out.println(0); clauses++; return true; } return false; } /** * Returns a pointer to the C++ peer class (the native instance wrapped by this object). * @return a pointer to the C++ peer class (the native instance wrapped by this object). */ final long peer() { return peer; } /** * Returns the current sat of the solver. * @return null if the sat is unknown, TRUE if the last * call to solve() yielded SAT, and FALSE if the last call to * solve() yielded UNSAT. */ final Boolean status() { return sat; } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#solve() * @see #solve(long) */ public final boolean solve() { if (sat == Boolean.FALSE) return sat; else return (sat = Boolean.valueOf(solve(peer))); } /** * Throws an IllegalArgumentException if variable !in this.variables. * Otherwise does nothing. * @throws IllegalArgumentException variable !in this.variables */ final void validateVariable(int variable) { if (variable < 1 || variable > vars) throw new IllegalArgumentException(variable + " !in [1.." + vars+"]"); } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#valueOf(int) */ public final boolean valueOf(int variable) { if (sat != Boolean.TRUE) throw new IllegalStateException(); validateVariable(variable); return valueOf(peer, variable); } /** * {@inheritDoc} * @see kodkod.engine.satlab.SATSolver#free() */ public synchronized final void free() { if (peer!=0) { // System.out.println("freeing " + peer + " " + getClass()); free(peer); peer = 0; } // already freed } /** * Releases the resources used by this native solver. */ protected final void finalize() throws Throwable { super.finalize(); free(); } /** * Releases the resources associated with * the native solver at the given memory address. * This method must be called when the object holding the * given reference goes out of scope to avoid * memory leaks. * @ensures releases the resources associated * with the given native peer */ abstract void free(long peer); /** * Adds the specified number of variables to the given native peer. * @ensures increases the vocabulary of the given native peer by * the specified number of variables */ abstract void addVariables(long peer, int numVariables); /** * Ensures that the given native peer logically contains the * specified clause and returns true if the solver's clause database * changed as a result of the call. * @requires all i: [0..lits.length) | abs(lits[i]) in this.variables * @requires all disj i,j: [0..lits.length) | abs(lits[i]) != abs(lits[j]) * @ensures ensures that the given native peer logically contains the specified clause * @return true if the peer's clause database changed as a result of the call; a negative integer if not. */ abstract boolean addClause(long peer, int[] lits); /** * Calls the solve method on the given native peer. * @return true if the clauses in the solver are SAT; * otherwise returns false. */ abstract boolean solve(long peer); /** * Returns the assignment for the given literal * by the specified native peer * @requires the last call to {@link #solve(long) solve(peer)} returned SATISFIABLE * @return the assignment for the given literal */ abstract boolean valueOf(long peer, int literal); }