/******************************************************************************* * SAT4J: a SATisfiability library for Java Copyright (C) 2004-2008 Daniel Le Berre * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Alternatively, the contents of this file may be used under the terms of * either the GNU Lesser General Public License Version 2.1 or later (the * "LGPL"), in which case the provisions of the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of the LGPL, and not to allow others to use your version of * this file under the terms of the EPL, indicate your decision by deleting * the provisions above and replace them with the notice and other provisions * required by the LGPL. If you do not delete the provisions above, a recipient * may use your version of this file under the terms of the EPL or the LGPL. * * Based on the original MiniSat specification from: * * An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the * Sixth International Conference on Theory and Applications of Satisfiability * Testing, LNCS 2919, pp 502-518, 2003. * * See www.minisat.se for the original solver in C++. * * The reason simplification methods are coming from MiniSAT 1.14 released under * the MIT license: * MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson * * 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 org.sat4j.minisat.core; import static org.sat4j.core.LiteralsUtils.var; import static org.sat4j.core.LiteralsUtils.toDimacs; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; import java.lang.reflect.Field; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import org.sat4j.core.Vec; import org.sat4j.core.VecInt; import org.sat4j.specs.ContradictionException; import org.sat4j.specs.IConstr; import org.sat4j.specs.ISolver; import org.sat4j.specs.IVec; import org.sat4j.specs.IVecInt; import org.sat4j.specs.IteratorInt; import org.sat4j.specs.TimeoutException; /** * The backbone of the library providing the modular implementation of a MiniSAT * (Chaff) like solver. * * @author leberre */ public class Solver<L extends ILits, D extends DataStructureFactory<L>> implements ISolver, UnitPropagationListener, ActivityListener, Learner { private static final long serialVersionUID = 1L; private static final double CLAUSE_RESCALE_FACTOR = 1e-20; private static final double CLAUSE_RESCALE_BOUND = 1 / CLAUSE_RESCALE_FACTOR; /** * List des contraintes du probl?me. */ private final IVec<Constr> constrs = new Vec<Constr>(); // Constr /** * Liste des clauses apprises. */ private final IVec<Constr> learnts = new Vec<Constr>(); // Clause /** * incr?ment pour l'activit? des clauses. */ private double claInc = 1.0; /** * decay factor pour l'activit? des clauses. */ private double claDecay = 1.0; /** * Queue de propagation */ // private final IntQueue propQ = new IntQueue(); // Lit // head of the queue in trail ... (taken from MiniSAT 1.14) private int qhead = 0; // queue /** * affectation en ordre chronologique */ protected final IVecInt trail = new VecInt(); // lit // vector /** * indice des s?parateurs des diff?rents niveau de d?cision dans trail */ protected final IVecInt trailLim = new VecInt(); // int // vector /** * S?pare les hypoth?ses incr?mentale et recherche */ protected int rootLevel; private int[] model = null; protected L voc; private IOrder<L> order; private final ActivityComparator comparator = new ActivityComparator(); private final SolverStats stats = new SolverStats(); private final LearningStrategy<L, D> learner; protected final AssertingClauseGenerator analyzer; private volatile boolean undertimeout; private long timeout = Integer.MAX_VALUE; private boolean timeBasedTimeout = true; protected D dsfactory; private SearchParams params; private final IVecInt __dimacs_out = new VecInt(); private SearchListener slistener = new NullSearchListener(); private RestartStrategy restarter; private final Map<String, Counter> constrTypes = new HashMap<String, Counter>(); private boolean isDBSimplificationAllowed = false; private int learnedLiterals = 0; protected IVecInt dimacs2internal(IVecInt in) { // if (voc.nVars() == 0) { // throw new RuntimeException( // "Please set the number of variables (solver.newVar() or solver.newVar(maxvar)) before adding constraints!" // ); // } __dimacs_out.clear(); __dimacs_out.ensure(in.size()); for (int i = 0; i < in.size(); i++) { assert (in.get(i) != 0); // && (Math.abs(in.get(i)) <= voc.nVars()); __dimacs_out.unsafePush(voc.getFromPool(in.get(i))); } return __dimacs_out; } /** * creates a Solver without LearningListener. A learningListener must be * added to the solver, else it won't backtrack!!! A data structure factory * must be provided, else it won't work either. * * @param acg * an asserting clause generator */ public Solver(AssertingClauseGenerator acg, LearningStrategy<L, D> learner, D dsf, IOrder<L> order, RestartStrategy restarter) { this(acg, learner, dsf, new SearchParams(), order, restarter); } public Solver(AssertingClauseGenerator acg, LearningStrategy<L, D> learner, D dsf, SearchParams params, IOrder<L> order, RestartStrategy restarter) { analyzer = acg; this.learner = learner; this.order = order; this.params = params; setDataStructureFactory(dsf); this.restarter = restarter; } /** * Change the internal representation of the contraints. Note that the * heuristics must be changed prior to calling that method. * * @param dsf * the internal factory */ public final void setDataStructureFactory(D dsf) { dsfactory = dsf; dsfactory.setUnitPropagationListener(this); dsfactory.setLearner(this); voc = dsf.getVocabulary(); order.setLits(voc); } public void setSearchListener(SearchListener sl) { slistener = sl; } public void setTimeout(int t) { timeout = t * 1000L; timeBasedTimeout = true; } public void setTimeoutMs(long t) { timeout = t; timeBasedTimeout = true; } public void setTimeoutOnConflicts(int count) { timeout = count; timeBasedTimeout = false; } public void setSearchParams(SearchParams sp) { params = sp; } public void setRestartStrategy(RestartStrategy restarter) { this.restarter = restarter; } public void expireTimeout() { undertimeout = false; } protected int nAssigns() { return trail.size(); } public int nConstraints() { return constrs.size() + trail.size() - learnedLiterals; } public void learn(Constr c) { learnts.push(c); c.setLearnt(); c.register(); stats.learnedclauses++; switch (c.size()) { case 2: stats.learnedbinaryclauses++; break; case 3: stats.learnedternaryclauses++; break; default: // do nothing } } public int decisionLevel() { return trailLim.size(); } @Deprecated public int newVar() { int index = voc.nVars() + 1; voc.ensurePool(index); return index; } public int newVar(int howmany) { voc.ensurePool(howmany); return voc.nVars(); } public IConstr addClause(IVecInt literals) throws ContradictionException { IVecInt vlits = dimacs2internal(literals); return addConstr(dsfactory.createClause(vlits)); } public boolean removeConstr(IConstr co) { if (co == null) { throw new IllegalArgumentException( "Reference to the constraint to remove needed!"); //$NON-NLS-1$ } Constr c = (Constr) co; c.remove(); constrs.remove(c); clearLearntClauses(); cancelLearntLiterals(); return true; } public void addAllClauses(IVec<IVecInt> clauses) throws ContradictionException { for (Iterator<IVecInt> iterator = clauses.iterator(); iterator .hasNext();) { addClause(iterator.next()); } } public IConstr addAtMost(IVecInt literals, int degree) throws ContradictionException { int n = literals.size(); IVecInt opliterals = new VecInt(n); for (IteratorInt iterator = literals.iterator(); iterator.hasNext();) { opliterals.push(-iterator.next()); } return addAtLeast(opliterals, n - degree); } public IConstr addAtLeast(IVecInt literals, int degree) throws ContradictionException { IVecInt vlits = dimacs2internal(literals); return addConstr(dsfactory.createCardinalityConstraint(vlits, degree)); } @SuppressWarnings("unchecked") public boolean simplifyDB() { // aucune raison de recommencer un propagate? // if (propagate() != null) { // // Un conflit est d?couvert, la base est inconsistante // return false; // } // Simplifie la base de clauses apres la premiere propagation des // clauses unitaires IVec<Constr>[] cs = new IVec[] { constrs, learnts }; for (int type = 0; type < 2; type++) { int j = 0; for (int i = 0; i < cs[type].size(); i++) { if (cs[type].get(i).simplify()) { // enleve les contraintes satisfaites de la base cs[type].get(i).remove(); } else { cs[type].moveTo(j++, i); } } cs[type].shrinkTo(j); } return true; } /** * Si un mod?le est trouv?, ce vecteur contient le mod?le. * * @return un mod?le de la formule. */ public int[] model() { if (model == null) { throw new UnsupportedOperationException( "Call the solve method first!!!"); //$NON-NLS-1$ } int[] nmodel = new int[model.length]; System.arraycopy(model, 0, nmodel, 0, model.length); return nmodel; } /** * Satisfait un litt?ral * * @param p * le litt?ral * @return true si tout se passe bien, false si un conflit appara?t. */ public boolean enqueue(int p) { return enqueue(p, null); } /** * Put the literal on the queue of assignments to be done. * * @param p * the literal. * @param from * the reason to propagate that literal, else null * @return true if the asignment can be made, false if a conflict is * detected. */ public boolean enqueue(int p, Constr from) { assert p > 1; if (voc.isSatisfied(p)) { // literal is already satisfied. Skipping. return true; } if (voc.isFalsified(p)) { // conflicting enqueued assignment return false; } // new fact, store it voc.satisfies(p); voc.setLevel(p, decisionLevel()); voc.setReason(p, from); trail.push(p); return true; } private boolean[] mseen = new boolean[0]; private final IVecInt preason = new VecInt(); private final IVecInt outLearnt = new VecInt(); public void analyze(Constr confl, Pair results) { assert confl != null; outLearnt.clear(); final boolean[] seen = mseen; assert outLearnt.size() == 0; for (int i = 0; i < seen.length; i++) { seen[i] = false; } analyzer.initAnalyze(); int p = ILits.UNDEFINED; outLearnt.push(ILits.UNDEFINED); // reserve de la place pour le litteral falsifie int outBtlevel = 0; do { preason.clear(); assert confl != null; confl.calcReason(p, preason); if (confl.learnt()) claBumpActivity(confl); // Trace reason for p for (int j = 0; j < preason.size(); j++) { int q = preason.get(j); order.updateVar(q); if (!seen[q >> 1]) { // order.updateVar(q); // MINISAT seen[q >> 1] = true; if (voc.getLevel(q) == decisionLevel()) { analyzer.onCurrentDecisionLevelLiteral(q); } else if (voc.getLevel(q) > 0) { // ajoute les variables depuis le niveau de d?cision 0 outLearnt.push(q ^ 1); outBtlevel = Math.max(outBtlevel, voc.getLevel(q)); } } } // select next reason to look at do { p = trail.last(); confl = voc.getReason(p); undoOne(); } while (!seen[p >> 1]); // seen[p.var] indique que p se trouve dans outLearnt ou dans // le dernier niveau de d?cision } while (analyzer.clauseNonAssertive(confl)); outLearnt.set(0, p ^ 1); simplifier.simplify(outLearnt); Constr c = dsfactory.createUnregisteredClause(outLearnt); slistener.learn(c); results.reason = c; assert outBtlevel > -1; results.backtrackLevel = outBtlevel; } interface ISimplifier extends Serializable { void simplify(IVecInt outLearnt); } public static final ISimplifier NO_SIMPLIFICATION = new ISimplifier() { /** * */ private static final long serialVersionUID = 1L; public void simplify(IVecInt outLearnt) { } @Override public String toString() { return "No reason simplification"; //$NON-NLS-1$ } }; public final ISimplifier SIMPLE_SIMPLIFICATION = new ISimplifier() { /** * */ private static final long serialVersionUID = 1L; public void simplify(IVecInt conflictToReduce) { simpleSimplification(conflictToReduce); } @Override public String toString() { return "Simple reason simplification"; //$NON-NLS-1$ } }; public final ISimplifier EXPENSIVE_SIMPLIFICATION = new ISimplifier() { /** * */ private static final long serialVersionUID = 1L; public void simplify(IVecInt conflictToReduce) { expensiveSimplification(conflictToReduce); } @Override public String toString() { return "Expensive reason simplification"; //$NON-NLS-1$ } }; private ISimplifier simplifier = NO_SIMPLIFICATION; /** * Setup the reason simplification strategy. By default, there is no reason * simplification. NOTE THAT REASON SIMPLIFICATION DOES NOT WORK WITH * SPECIFIC DATA STRUCTURE FOR HANDLING BOTH BINARY AND TERNARY CLAUSES. * * @param simp * the name of the simplifier (one of NO_SIMPLIFICATION, * SIMPLE_SIMPLIFICATION, EXPENSIVE_SIMPLIFICATION). */ public void setSimplifier(String simp) { Field f; try { f = Solver.class.getDeclaredField(simp); simplifier = (ISimplifier) f.get(this); } catch (Exception e) { e.printStackTrace(); simplifier = NO_SIMPLIFICATION; } } /** * Setup the reason simplification strategy. By default, there is no reason * simplification. NOTE THAT REASON SIMPLIFICATION IS ONLY ALLOWED FOR WL * CLAUSAL data structures. USING REASON SIMPLIFICATION ON CB CLAUSES, * CARDINALITY CONSTRAINTS OR PB CONSTRAINTS MIGHT RESULT IN INCORRECT * RESULTS. * * @param simp */ public void setSimplifier(ISimplifier simp) { simplifier = simp; } // MiniSat -- Copyright (c) 2003-2005, Niklas Een, Niklas Sorensson // // 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. // Taken from MiniSAT 1.14: Simplify conflict clause (a little): private void simpleSimplification(IVecInt conflictToReduce) { int i, j; final boolean[] seen = mseen; for (i = j = 1; i < conflictToReduce.size(); i++) { IConstr r = voc.getReason(conflictToReduce.get(i)); if (r == null) { conflictToReduce.moveTo(j++, i); } else { for (int k = 0; k < r.size(); k++) if (voc.isFalsified(r.get(k)) && !seen[r.get(k) >> 1] && (voc.getLevel(r.get(k)) != 0)) { conflictToReduce.moveTo(j++, i); break; } } } conflictToReduce.shrink(i - j); stats.reducedliterals += (i - j); } private final IVecInt analyzetoclear = new VecInt(); private final IVecInt analyzestack = new VecInt(); // Taken from MiniSAT 1.14 private void expensiveSimplification(IVecInt conflictToReduce) { // Simplify conflict clause (a lot): // int i, j; // (maintain an abstraction of levels involved in conflict) analyzetoclear.clear(); conflictToReduce.copyTo(analyzetoclear); for (i = 1, j = 1; i < conflictToReduce.size(); i++) if (voc.getReason(conflictToReduce.get(i)) == null || !analyzeRemovable(conflictToReduce.get(i))) conflictToReduce.moveTo(j++, i); conflictToReduce.shrink(i - j); stats.reducedliterals += (i - j); } // Check if 'p' can be removed.' min_level' is used to abort early if // visiting literals at a level that cannot be removed. // private boolean analyzeRemovable(int p) { assert voc.getReason(p) != null; analyzestack.clear(); analyzestack.push(p); final boolean[] seen = mseen; int top = analyzetoclear.size(); while (analyzestack.size() > 0) { int q = analyzestack.last(); assert voc.getReason(q) != null; Constr c = voc.getReason(q); analyzestack.pop(); for (int i = 0; i < c.size(); i++) { int l = c.get(i); if (voc.isFalsified(l) && !seen[var(l)] && voc.getLevel(l) != 0) { if (voc.getReason(l) == null) { for (int j = top; j < analyzetoclear.size(); j++) seen[analyzetoclear.get(j) >> 1] = false; analyzetoclear.shrink(analyzetoclear.size() - top); return false; } seen[l >> 1] = true; analyzestack.push(l); analyzetoclear.push(l); } } } return true; } // END Minisat 1.14 cut and paste /** * */ protected void undoOne() { // recupere le dernier litteral affecte int p = trail.last(); assert p > 1; assert voc.getLevel(p) >= 0; int x = p >> 1; // desaffecte la variable voc.unassign(p); voc.setReason(p, null); voc.setLevel(p, -1); // met a jour l'heuristique order.undo(x); // depile le litteral des affectations trail.pop(); // met a jour les contraintes apres desaffectation du litteral : // normalement, il n'y a rien a faire ici pour les prouveurs de type // Chaff?? IVec<Undoable> undos = voc.undos(p); assert undos != null; while (undos.size() > 0) { undos.last().undo(p); undos.pop(); } } /** * Propagate activity to a constraint * * @param confl * a constraint */ public void claBumpActivity(Constr confl) { confl.incActivity(claInc); if (confl.getActivity() > CLAUSE_RESCALE_BOUND) claRescalActivity(); // for (int i = 0; i < confl.size(); i++) { // varBumpActivity(confl.get(i)); // } } public void varBumpActivity(int p) { order.updateVar(p); } private void claRescalActivity() { for (int i = 0; i < learnts.size(); i++) { learnts.get(i).rescaleBy(CLAUSE_RESCALE_FACTOR); } claInc *= CLAUSE_RESCALE_FACTOR; } /** * @return null if not conflict is found, else a conflicting constraint. */ public Constr propagate() { while (qhead < trail.size()) { stats.propagations++; int p = trail.get(qhead++); slistener.propagating(toDimacs(p)); order.assignLiteral(p); // p is the literal to propagate // Moved original MiniSAT code to dsfactory to avoid // watches manipulation in counter Based clauses for instance. assert p > 1; IVec<Propagatable> watched = dsfactory.getAttachesFor(p); final int size = watched.size(); for (int i = 0; i < size; i++) { stats.inspects++; if (!watched.get(i).propagate(this, p)) { // Constraint is conflicting: copy remaining watches to // watches[p] // and return constraint dsfactory.conflictDetectedInAttachesFor(p, i); qhead = trail.size(); // propQ.clear(); // FIXME enlever le transtypage return (Constr) watched.get(i); } } } return null; } void record(Constr constr) { constr.assertConstraint(this); slistener.adding(toDimacs(constr.get(0))); if (constr.size() == 1) { stats.learnedliterals++; learnedLiterals++; } else { learner.learns(constr); } } /** * @return false ssi conflit imm?diat. */ public boolean assume(int p) { // Precondition: assume propagation queue is empty assert trail.size() == qhead; trailLim.push(trail.size()); return enqueue(p); } /** * Revert to the state before the last push() */ private void cancel() { // assert trail.size() == qhead || !undertimeout; int decisionvar = trail.unsafeGet(trailLim.last()); slistener.backtracking(toDimacs(decisionvar)); for (int c = trail.size() - trailLim.last(); c > 0; c--) { undoOne(); } trailLim.pop(); } /** * Restore literals */ private void cancelLearntLiterals() { // assert trail.size() == qhead || !undertimeout; for (int c = learnedLiterals; c > 0; c--) { undoOne(); } qhead = trail.size(); } /** * Cancel several levels of assumptions * * @param level */ protected void cancelUntil(int level) { while (decisionLevel() > level) { cancel(); } qhead = trail.size(); } private final Pair analysisResult = new Pair(); private boolean[] fullmodel; Lbool search(long nofConflicts) { assert rootLevel == decisionLevel(); stats.starts++; int conflictC = 0; // varDecay = 1 / params.varDecay; order.setVarDecay(1 / params.getVarDecay()); claDecay = 1 / params.getClaDecay(); do { slistener.beginLoop(); // propage les clauses unitaires Constr confl = propagate(); assert trail.size() == qhead; if (confl == null) { // No conflict found // simpliFYDB() prevents a correct use of // constraints removal. if (decisionLevel() == 0 && isDBSimplificationAllowed) { // // Simplify the set of problem clause // // iff rootLevel==0 stats.rootSimplifications++; boolean ret = simplifyDB(); assert ret; } // was learnts.size() - nAssigns() > nofLearnts // if (nofLearnts.obj >= 0 && learnts.size() > nofLearnts.obj) { assert nAssigns() <= voc.realnVars(); if (nAssigns() == voc.realnVars()) { slistener.solutionFound(); modelFound(); return Lbool.TRUE; } if (conflictC >= nofConflicts) { // Reached bound on number of conflicts // Force a restart cancelUntil(rootLevel); return Lbool.UNDEFINED; } if (needToReduceDB) { reduceDB(); needToReduceDB = false; // Runtime.getRuntime().gc(); } // New variable decision stats.decisions++; int p = order.select(); assert p > 1; slistener.assuming(toDimacs(p)); boolean ret = assume(p); assert ret; } else { // un conflit apparait stats.conflicts++; conflictC++; slistener.conflictFound(); conflictCount.newConflict(); if (decisionLevel() == rootLevel) { // on est a la racine, la formule est inconsistante return Lbool.FALSE; } // analyze conflict analyze(confl, analysisResult); assert analysisResult.backtrackLevel < decisionLevel(); cancelUntil(Math.max(analysisResult.backtrackLevel, rootLevel)); assert (decisionLevel() >= rootLevel) && (decisionLevel() >= analysisResult.backtrackLevel); if (analysisResult.reason == null) { return Lbool.FALSE; } record(analysisResult.reason); analysisResult.reason = null; decayActivities(); } } while (undertimeout); return Lbool.UNDEFINED; // timeout occured } protected void analyzeAtRootLevel(Constr conflict) { } /** * */ void modelFound() { model = new int[trail.size()]; fullmodel = new boolean[nVars()]; int index = 0; for (int i = 1; i <= voc.nVars(); i++) { if (voc.belongsToPool(i)) { int p = voc.getFromPool(i); if (!voc.isUnassigned(p)) { model[index++] = voc.isSatisfied(p) ? i : -i; fullmodel[i - 1] = voc.isSatisfied(p); } } } assert index == model.length; cancelUntil(rootLevel); } public boolean model(int var) { if (var <= 0 || var > nVars()) { throw new IllegalArgumentException( "Use a valid Dimacs var id as argument!"); //$NON-NLS-1$ } if (fullmodel == null) { throw new UnsupportedOperationException( "Call the solve method first!!!"); //$NON-NLS-1$ } return fullmodel[var - 1]; } /** * */ protected void reduceDB() { reduceDB(claInc / learnts.size()); } public void clearLearntClauses() { for (Iterator<Constr> iterator = learnts.iterator(); iterator.hasNext();) iterator.next().remove(); learnts.clear(); } protected void reduceDB(double lim) { int i, j; sortOnActivity(); stats.reduceddb++; for (i = j = 0; i < learnts.size() / 2; i++) { Constr c = learnts.get(i); if (c.locked()) { learnts.set(j++, learnts.get(i)); } else { c.remove(); } } for (; i < learnts.size(); i++) { // Constr c = learnts.get(i); // if (!c.locked() && (c.getActivity() < lim)) { // c.remove(); // } else { learnts.set(j++, learnts.get(i)); // } } System.out.println("c cleaning " + (learnts.size() - j) //$NON-NLS-1$ + " clauses out of " + learnts.size() + " for limit " + lim); //$NON-NLS-1$ //$NON-NLS-2$ learnts.shrinkTo(j); } /** * @param learnts */ private void sortOnActivity() { learnts.sort(comparator); } /** * */ protected void decayActivities() { order.varDecayActivity(); claDecayActivity(); } /** * */ private void claDecayActivity() { claInc *= claDecay; } /** * @return true iff the set of constraints is satisfiable, else false. */ public boolean isSatisfiable() throws TimeoutException { return isSatisfiable(VecInt.EMPTY); } /** * @return true iff the set of constraints is satisfiable, else false. */ public boolean isSatisfiable(boolean global) throws TimeoutException { return isSatisfiable(VecInt.EMPTY, global); } private double timebegin = 0; private boolean needToReduceDB; private ConflictTimer conflictCount; private transient Timer timer; public boolean isSatisfiable(IVecInt assumps) throws TimeoutException { return isSatisfiable(assumps, false); } public boolean isSatisfiable(IVecInt assumps, boolean global) throws TimeoutException { Lbool status = Lbool.UNDEFINED; final int howmany = voc.nVars(); if (mseen.length < howmany) { mseen = new boolean[howmany + 1]; } trail.ensure(howmany); trailLim.ensure(howmany); learnedLiterals = 0; order.init(); learner.init(); restarter.init(params); timebegin = System.currentTimeMillis(); slistener.start(); model = null; // forget about previous model fullmodel = null; // propagate constraints Constr confl = propagate(); if (confl != null) { analyzeAtRootLevel(confl); slistener.end(Lbool.FALSE); cancelUntil(0); return false; } // push incremental assumptions for (IteratorInt iterator = assumps.iterator(); iterator.hasNext();) { if (!assume(voc.getFromPool(iterator.next())) || (propagate() != null)) { slistener.end(Lbool.FALSE); cancelUntil(0); return false; } } rootLevel = decisionLevel(); final long memorybound = Runtime.getRuntime().freeMemory() / 10; ConflictTimer freeMem = new ConflictTimerAdapter(500) { private static final long serialVersionUID = 1L; @Override void run() { long freemem = Runtime.getRuntime().freeMemory(); // System.out.println("c Free memory "+freemem); if (freemem < memorybound) { // Reduce the set of learnt clauses needToReduceDB = true; } } }; if (timeBasedTimeout) { if (!global || timer == null) { TimerTask stopMe = new TimerTask() { @Override public void run() { undertimeout = false; } }; timer = new Timer(true); timer.schedule(stopMe, timeout); conflictCount = freeMem; } } else { if (!global || conflictCount == null) { ConflictTimer conflictTimeout = new ConflictTimerAdapter( (int) timeout) { private static final long serialVersionUID = 1L; @Override public void run() { undertimeout = false; } }; conflictCount = new ConflictTimerContainer().add( conflictTimeout).add(freeMem); } } needToReduceDB = false; undertimeout = true; // Solve while ((status == Lbool.UNDEFINED) && undertimeout) { status = search(restarter.nextRestartNumberOfConflict()); // System.out.println("c speed // "+(stats.decisions/((System.currentTimeMillis()-timebegin)/1000))+" // dec/s, "+stats.starts+"/"+stats.conflicts); restarter.onRestart(); } cancelUntil(0); if (!global && timeBasedTimeout) { timer.cancel(); timer = null; } slistener.end(status); if (!undertimeout) { throw new TimeoutException(" Timeout (" + timeout + "s) exceeded"); //$NON-NLS-1$//$NON-NLS-2$ } return status == Lbool.TRUE; } public void printInfos(PrintWriter out, String prefix) { out.print(prefix); out.println("constraints type "); for (Map.Entry<String, Counter> entry : constrTypes.entrySet()) { out.println(prefix + entry.getKey() + " => " + entry.getValue()); } } public void printLearntClausesInfos(PrintWriter out, String prefix) { Map<String,Counter> learntTypes = new HashMap<String,Counter>(); for (Iterator<Constr> it = learnts.iterator(); it.hasNext();) { String type = it.next().getClass().getName(); Counter count = learntTypes.get(type); if (count == null) { learntTypes.put(type, new Counter()); } else { count.inc(); } } out.print(prefix); out.println("learnt constraints type "); for (Map.Entry<String, Counter> entry : learntTypes.entrySet()) { out.println(prefix + entry.getKey() + " => " + entry.getValue()); } } public SolverStats getStats() { return stats; } public IOrder<L> getOrder() { return order; } public void setOrder(IOrder<L> h) { order = h; order.setLits(voc); } public L getVocabulary() { return voc; } public void reset() { // FIXME verify that cleanup is OK voc.resetPool(); dsfactory.reset(); constrs.clear(); learnts.clear(); stats.reset(); constrTypes.clear(); } public int nVars() { return voc.nVars(); } /** * @param constr * a constraint implementing the Constr interface. * @return a reference to the constraint for external use. */ protected IConstr addConstr(Constr constr) { if (constr != null) { constrs.push(constr); String type = constr.getClass().getName(); Counter count = constrTypes.get(type); if (count == null) { constrTypes.put(type, new Counter()); } else { count.inc(); } } return constr; } public DataStructureFactory<L> getDSFactory() { return dsfactory; } public IVecInt getOutLearnt() { return outLearnt; } /** * returns the ith constraint in the solver. * * @param i * the constraint number (begins at 0) * @return the ith constraint */ public IConstr getIthConstr(int i) { return constrs.get(i); } /* * (non-Javadoc) * * @see org.sat4j.specs.ISolver#printStat(java.io.PrintStream, * java.lang.String) */ public void printStat(PrintStream out, String prefix) { printStat(new PrintWriter(out), prefix); } public void printStat(PrintWriter out, String prefix) { stats.printStat(out, prefix); double cputime = (System.currentTimeMillis() - timebegin) / 1000; out.println(prefix + "speed (assignments/second)\t: " + stats.propagations //$NON-NLS-1$ / cputime); order.printStat(out, prefix); printLearntClausesInfos(out, prefix); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ public String toString(String prefix) { StringBuffer stb = new StringBuffer(); Object[] objs = { analyzer, dsfactory, learner, params, order, simplifier, restarter }; stb.append(prefix); stb.append("--- Begin Solver configuration ---"); //$NON-NLS-1$ stb.append("\n"); //$NON-NLS-1$ for (Object o : objs) { stb.append(prefix); stb.append(o.toString()); stb.append("\n"); //$NON-NLS-1$ } stb.append(prefix); stb.append("timeout="); if (timeBasedTimeout) { stb.append(timeout / 1000); stb.append("s\n"); } else { stb.append(timeout); stb.append(" conflicts\n"); } stb.append(prefix); stb.append("DB Simplification allowed="); stb.append(isDBSimplificationAllowed); stb.append("\n"); stb.append(prefix); stb.append("--- End Solver configuration ---"); //$NON-NLS-1$ return stb.toString(); } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return toString(""); //$NON-NLS-1$ } public int getTimeout() { return (int) (timeBasedTimeout ? timeout / 1000 : timeout); } public long getTimeoutMs() { if (!timeBasedTimeout) { throw new UnsupportedOperationException( "The timeout is given in number of conflicts!"); } return timeout; } public void setExpectedNumberOfClauses(int nb) { constrs.ensure(nb); } public Map<String, Number> getStat() { return stats.toMap(); } public int[] findModel() throws TimeoutException { if (isSatisfiable()) { return model(); } // DLB findbugs ok // A zero length array would mean that the formula is a tautology. return null; } public int[] findModel(IVecInt assumps) throws TimeoutException { if (isSatisfiable(assumps)) { return model(); } // DLB findbugs ok // A zero length array would mean that the formula is a tautology. return null; } public boolean isDBSimplificationAllowed() { return isDBSimplificationAllowed; } public void setDBSimplificationAllowed(boolean status) { isDBSimplificationAllowed = status; } } class ActivityComparator implements Comparator<Constr>, Serializable { private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(Constr c1, Constr c2) { return (int) Math.round(c1.getActivity() - c2.getActivity()); } } interface ConflictTimer { void reset(); void newConflict(); } abstract class ConflictTimerAdapter implements Serializable, ConflictTimer { /** * */ private static final long serialVersionUID = 1L; private int counter; private final int bound; ConflictTimerAdapter(final int bound) { this.bound = bound; counter = 0; } public void reset() { counter = 0; } public void newConflict() { counter++; if (counter == bound) { run(); counter = 0; } } abstract void run(); } class ConflictTimerContainer implements Serializable, ConflictTimer { /** * */ private static final long serialVersionUID = 1L; private final IVec<ConflictTimer> timers = new Vec<ConflictTimer>(); ConflictTimerContainer add(ConflictTimer timer) { timers.push(timer); return this; } public void reset() { Iterator<ConflictTimer> it = timers.iterator(); while (it.hasNext()) { it.next().reset(); } } public void newConflict() { Iterator<ConflictTimer> it = timers.iterator(); while (it.hasNext()) { it.next().newConflict(); } } }