/******************************************************************************* * SAT4J: a SATisfiability library for Java Copyright (C) 2004, 2012 Artois University and CNRS * * 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++. * * Contributors: * CRIL - initial API and implementation *******************************************************************************/ package org.sat4j.minisat.core; import static org.sat4j.core.LiteralsUtils.neg; import static org.sat4j.core.LiteralsUtils.toDimacs; import static org.sat4j.core.LiteralsUtils.toInternal; import static org.sat4j.core.LiteralsUtils.var; import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import org.sat4j.core.ConstrGroup; import org.sat4j.core.LiteralsUtils; import org.sat4j.core.Vec; import org.sat4j.core.VecInt; import org.sat4j.specs.ContradictionException; import org.sat4j.specs.IConstr; import org.sat4j.specs.ILogAble; import org.sat4j.specs.ISolver; import org.sat4j.specs.ISolverService; import org.sat4j.specs.IVec; import org.sat4j.specs.IVecInt; import org.sat4j.specs.IteratorInt; import org.sat4j.specs.Lbool; import org.sat4j.specs.SearchListener; import org.sat4j.specs.TimeoutException; import org.sat4j.specs.UnitClauseProvider; /** * The backbone of the library providing the modular implementation of a MiniSAT * (Chaff) like solver. * * @author leberre */ public class Solver<D extends DataStructureFactory> implements ISolverService, ICDCL<D> { 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; protected ILogAble out; /** * Set of original constraints. */ protected final IVec<Constr> constrs = new Vec<Constr>(); /** * Set of learned constraints. */ protected final IVec<Constr> learnts = new Vec<Constr>(); /** * Increment for clause activity. */ private double claInc = 1.0; /** * decay factor pour l'activit? des clauses. */ private double claDecay = 1.0; /** * propagation queue */ // head of the queue in trail ... (taken from MiniSAT 1.14) private int qhead = 0; /** * variable assignments (literals) in chronological order. */ protected final IVecInt trail = new VecInt(); /** * position of the decision levels on the trail. */ protected final IVecInt trailLim = new VecInt(); /** * position of assumptions before starting the search. */ protected int rootLevel; private int[] model = null; protected ILits voc; private IOrder order; private final ActivityComparator comparator = new ActivityComparator(); private SolverStats stats = new SolverStats(); private LearningStrategy<D> learner; protected 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(); protected SearchListener slistener = new VoidTracing(); private RestartStrategy restarter; private final Map<String, Counter> constrTypes = new HashMap<String, Counter>(); private boolean isDBSimplificationAllowed = false; private final IVecInt learnedLiterals = new VecInt(); private boolean verbose = false; private boolean keepHot = false; private String prefix = "c "; private int declaredMaxVarId = 0; private UnitClauseProvider unitClauseProvider = UnitClauseProvider.VOID; protected IVecInt dimacs2internal(IVecInt in) { this.__dimacs_out.clear(); this.__dimacs_out.ensure(in.size()); int p; for (int i = 0; i < in.size(); i++) { p = in.get(i); if (p == 0) { throw new IllegalArgumentException( "0 is not a valid variable identifier"); } this.__dimacs_out.unsafePush(this.voc.getFromPool(p)); } return this.__dimacs_out; } /* * @since 2.3.1 */ public void registerLiteral(int p) { this.voc.getFromPool(p); } /** * 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. */ public Solver(LearningStrategy<D> learner, D dsf, IOrder order, RestartStrategy restarter) { this(learner, dsf, new SearchParams(), order, restarter); } public Solver(LearningStrategy<D> learner, D dsf, SearchParams params, IOrder order, RestartStrategy restarter) { this(learner, dsf, params, order, restarter, ILogAble.CONSOLE); } public Solver(LearningStrategy<D> learner, D dsf, SearchParams params, IOrder order, RestartStrategy restarter, ILogAble logger) { this.order = order; this.params = params; this.restarter = restarter; this.out = logger; setDataStructureFactory(dsf); // should be called after dsf has been set up setLearningStrategy(learner); } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#setDataStructureFactory(D) */ public final void setDataStructureFactory(D dsf) { this.dsfactory = dsf; this.dsfactory.setUnitPropagationListener(this); this.dsfactory.setLearner(this); this.voc = dsf.getVocabulary(); this.order.setLits(this.voc); } /** * @since 2.2 */ public boolean isVerbose() { return this.verbose; } /** * @param value * @since 2.2 */ public void setVerbose(boolean value) { this.verbose = value; } /* * (non-Javadoc) * * @see * org.sat4j.minisat.core.ICDCL#setSearchListener(org.sat4j.specs.SearchListener * ) */ public <S extends ISolverService> void setSearchListener( SearchListener<S> sl) { this.slistener = sl; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#getSearchListener() */ public <S extends ISolverService> SearchListener<S> getSearchListener() { return this.slistener; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#setLearner(org.sat4j.minisat.core. * LearningStrategy) */ public void setLearner(LearningStrategy<D> strategy) { setLearningStrategy(strategy); } /* * (non-Javadoc) * * @see * org.sat4j.minisat.core.ICDCL#setLearningStrategy(org.sat4j.minisat.core. * LearningStrategy) */ public void setLearningStrategy(LearningStrategy<D> strategy) { if (this.learner != null) { this.learner.setSolver(null); } this.learner = strategy; strategy.setSolver(this); } public void setTimeout(int t) { this.timeout = t * 1000L; this.timeBasedTimeout = true; } public void setTimeoutMs(long t) { this.timeout = t; this.timeBasedTimeout = true; } public void setTimeoutOnConflicts(int count) { this.timeout = count; this.timeBasedTimeout = false; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#setSearchParams(org.sat4j.minisat.core. * SearchParams) */ public void setSearchParams(SearchParams sp) { this.params = sp; } public SearchParams getSearchParams() { return this.params; } /* * (non-Javadoc) * * @see * org.sat4j.minisat.core.ICDCL#setRestartStrategy(org.sat4j.minisat.core * .RestartStrategy) */ public void setRestartStrategy(RestartStrategy restarter) { this.restarter = restarter; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#getRestartStrategy() */ public RestartStrategy getRestartStrategy() { return this.restarter; } public void expireTimeout() { this.undertimeout = false; if (this.timeBasedTimeout) { if (this.timer != null) { this.timer.cancel(); this.timer = null; } } else { if (this.conflictCount != null) { this.conflictCount = null; } } } protected int nAssigns() { return this.trail.size(); } public int nConstraints() { return this.constrs.size(); } public void learn(Constr c) { this.slistener.learn(c); this.learnts.push(c); c.setLearnt(); c.register(); this.stats.learnedclauses++; switch (c.size()) { case 2: this.stats.learnedbinaryclauses++; break; case 3: this.stats.learnedternaryclauses++; break; default: // do nothing } } public final int decisionLevel() { return this.trailLim.size(); } @Deprecated public int newVar() { int index = this.voc.nVars() + 1; this.voc.ensurePool(index); return index; } public int newVar(int howmany) { this.voc.ensurePool(howmany); this.declaredMaxVarId = howmany; return howmany; } public IConstr addClause(IVecInt literals) throws ContradictionException { IVecInt vlits = dimacs2internal(literals); return addConstr(this.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(this); this.constrs.remove(c); clearLearntClauses(); String type = c.getClass().getName(); this.constrTypes.get(type).dec(); return true; } /** * @since 2.1 */ public boolean removeSubsumedConstr(IConstr co) { if (co == null) { throw new IllegalArgumentException( "Reference to the constraint to remove needed!"); //$NON-NLS-1$ } if (this.constrs.last() != co) { throw new IllegalArgumentException( "Can only remove latest added constraint!!!"); //$NON-NLS-1$ } Constr c = (Constr) co; c.remove(this); this.constrs.pop(); String type = c.getClass().getName(); this.constrTypes.get(type).dec(); 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(this.dsfactory.createCardinalityConstraint(vlits, degree)); } public IConstr addExactly(IVecInt literals, int n) throws ContradictionException { ConstrGroup group = new ConstrGroup(false); group.add(addAtMost(literals, n)); group.add(addAtLeast(literals, n)); return group; } @SuppressWarnings("unchecked") public boolean simplifyDB() { // Simplifie la base de clauses apres la premiere propagation des // clauses unitaires IVec<Constr>[] cs = new IVec[] { this.constrs, this.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(this); } 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 (this.model == null) { throw new UnsupportedOperationException( "Call the solve method first!!!"); //$NON-NLS-1$ } int[] nmodel = new int[this.model.length]; System.arraycopy(this.model, 0, nmodel, 0, this.model.length); return nmodel; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#enqueue(int) */ public boolean enqueue(int p) { return enqueue(p, null); } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#enqueue(int, * org.sat4j.minisat.core.Constr) */ public boolean enqueue(int p, Constr from) { assert p > 1; if (this.voc.isSatisfied(p)) { // literal is already satisfied. Skipping. return true; } if (this.voc.isFalsified(p)) { // conflicting enqueued assignment return false; } // new fact, store it this.voc.satisfies(p); this.voc.setLevel(p, decisionLevel()); this.voc.setReason(p, from); this.trail.push(p); if (from != null && from.learnt()) { this.learnedConstraintsDeletionStrategy.onPropagation(from); } return true; } private boolean[] mseen = new boolean[0]; private final IVecInt mpreason = new VecInt(); private final IVecInt moutLearnt = new VecInt(); /** * @throws TimeoutException * if the timeout is reached during conflict analysis. */ public void analyze(Constr confl, Pair results) throws TimeoutException { assert confl != null; final boolean[] seen = this.mseen; final IVecInt outLearnt = this.moutLearnt; final IVecInt preason = this.mpreason; outLearnt.clear(); assert outLearnt.size() == 0; for (int i = 0; i < seen.length; i++) { seen[i] = false; } int counter = 0; int p = ILits.UNDEFINED; outLearnt.push(ILits.UNDEFINED); // reserve de la place pour le litteral falsifie int outBtlevel = 0; IConstr prevConfl = null; do { preason.clear(); assert confl != null; if (prevConfl != confl) { confl.calcReason(p, preason); this.learnedConstraintsDeletionStrategy .onConflictAnalysis(confl); // Trace reason for p for (int j = 0; j < preason.size(); j++) { int q = preason.get(j); this.order.updateVar(q); if (!seen[q >> 1]) { seen[q >> 1] = true; if (this.voc.getLevel(q) == decisionLevel()) { counter++; this.order.updateVarAtDecisionLevel(q); } else if (this.voc.getLevel(q) > 0) { // only literals assigned after decision level 0 // part of // the explanation outLearnt.push(q ^ 1); outBtlevel = Math.max(outBtlevel, this.voc.getLevel(q)); } } } } prevConfl = confl; // select next reason to look at do { p = this.trail.last(); confl = this.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 (--counter > 0); outLearnt.set(0, p ^ 1); this.simplifier.simplify(outLearnt); Constr c = this.dsfactory.createUnregisteredClause(outLearnt); // slistener.learn(c); this.learnedConstraintsDeletionStrategy.onClauseLearning(c); results.reason = c; assert outBtlevel > -1; results.backtrackLevel = outBtlevel; } /** * Derive a subset of the assumptions causing the inconistency. * * @param confl * the last conflict of the search, occuring at root level. * @param assumps * the set of assumption literals * @param conflictingLiteral * the literal detected conflicting while propagating * assumptions. * @return a subset of assumps causing the inconsistency. * @since 2.2 */ public IVecInt analyzeFinalConflictInTermsOfAssumptions(Constr confl, IVecInt assumps, int conflictingLiteral) { if (assumps.size() == 0) { return null; } while (!this.trailLim.isEmpty() && this.trailLim.last() == this.trail.size()) { // conflict detected when assuming a value this.trailLim.pop(); } final boolean[] seen = this.mseen; final IVecInt outLearnt = this.moutLearnt; final IVecInt preason = this.mpreason; outLearnt.clear(); if (this.trailLim.size() == 0) { // conflict detected on unit clauses return outLearnt; } assert outLearnt.size() == 0; for (int i = 0; i < seen.length; i++) { seen[i] = false; } if (confl == null) { seen[conflictingLiteral >> 1] = true; } int p = ILits.UNDEFINED; while (confl == null && this.trail.size() > 0 && this.trailLim.size() > 0) { p = this.trail.last(); confl = this.voc.getReason(p); undoOne(); if (confl == null && p == (conflictingLiteral ^ 1)) { outLearnt.push(toDimacs(p)); } if (this.trail.size() <= this.trailLim.last()) { this.trailLim.pop(); } } if (confl == null) { return outLearnt; } do { preason.clear(); confl.calcReason(p, preason); // Trace reason for p for (int j = 0; j < preason.size(); j++) { int q = preason.get(j); if (!seen[q >> 1]) { seen[q >> 1] = true; if (this.voc.getReason(q) == null && this.voc.getLevel(q) > 0) { assert assumps.contains(toDimacs(q)); outLearnt.push(toDimacs(q)); } } } // select next reason to look at do { p = this.trail.last(); confl = this.voc.getReason(p); undoOne(); if (decisionLevel() > 0 && this.trail.size() <= this.trailLim.last()) { this.trailLim.pop(); } } while (this.trail.size() > 0 && decisionLevel() > 0 && (!seen[p >> 1] || confl == null)); } while (decisionLevel() > 0); return 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$ } }; public final ISimplifier EXPENSIVE_SIMPLIFICATION_WLONLY = new ISimplifier() { /** * */ private static final long serialVersionUID = 1L; public void simplify(IVecInt conflictToReduce) { expensiveSimplificationWLOnly(conflictToReduce); } @Override public String toString() { return "Expensive reason simplification specific for WL data structure"; //$NON-NLS-1$ } }; private ISimplifier simplifier = NO_SIMPLIFICATION; /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#setSimplifier(java.lang.String) */ public void setSimplifier(SimplificationType simp) { Field f; try { f = Solver.class.getDeclaredField(simp.toString()); this.simplifier = (ISimplifier) f.get(this); } catch (Exception e) { e.printStackTrace(); this.simplifier = NO_SIMPLIFICATION; } } /* * (non-Javadoc) * * @see * org.sat4j.minisat.core.ICDCL#setSimplifier(org.sat4j.minisat.core.Solver * .ISimplifier) */ public void setSimplifier(ISimplifier simp) { this.simplifier = simp; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#getSimplifier() */ public ISimplifier getSimplifier() { return this.simplifier; } // 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, p; final boolean[] seen = this.mseen; IConstr r; for (i = j = 1; i < conflictToReduce.size(); i++) { r = this.voc.getReason(conflictToReduce.get(i)); if (r == null || r.canBePropagatedMultipleTimes()) { conflictToReduce.moveTo(j++, i); } else { for (int k = 0; k < r.size(); k++) { p = r.get(k); if (!seen[p >> 1] && this.voc.isFalsified(p) && this.voc.getLevel(p) != 0) { conflictToReduce.moveTo(j++, i); break; } } } } conflictToReduce.shrink(i - j); this.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) this.analyzetoclear.clear(); conflictToReduce.copyTo(this.analyzetoclear); for (i = 1, j = 1; i < conflictToReduce.size(); i++) { if (this.voc.getReason(conflictToReduce.get(i)) == null || !analyzeRemovable(conflictToReduce.get(i))) { conflictToReduce.moveTo(j++, i); } } conflictToReduce.shrink(i - j); this.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 this.voc.getReason(p) != null; ILits lvoc = this.voc; IVecInt lanalyzestack = this.analyzestack; IVecInt lanalyzetoclear = this.analyzetoclear; lanalyzestack.clear(); lanalyzestack.push(p); final boolean[] seen = this.mseen; int top = lanalyzetoclear.size(); while (lanalyzestack.size() > 0) { int q = lanalyzestack.last(); assert lvoc.getReason(q) != null; Constr c = lvoc.getReason(q); lanalyzestack.pop(); if (c.canBePropagatedMultipleTimes()) { for (int j = top; j < lanalyzetoclear.size(); j++) { seen[lanalyzetoclear.get(j) >> 1] = false; } lanalyzetoclear.shrink(lanalyzetoclear.size() - top); return false; } for (int i = 0; i < c.size(); i++) { int l = c.get(i); if (!seen[var(l)] && lvoc.isFalsified(l) && lvoc.getLevel(l) != 0) { if (lvoc.getReason(l) == null) { for (int j = top; j < lanalyzetoclear.size(); j++) { seen[lanalyzetoclear.get(j) >> 1] = false; } lanalyzetoclear.shrink(lanalyzetoclear.size() - top); return false; } seen[l >> 1] = true; lanalyzestack.push(l); lanalyzetoclear.push(l); } } } return true; } // Taken from MiniSAT 1.14 private void expensiveSimplificationWLOnly(IVecInt conflictToReduce) { // Simplify conflict clause (a lot): // int i, j; // (maintain an abstraction of levels involved in conflict) this.analyzetoclear.clear(); conflictToReduce.copyTo(this.analyzetoclear); for (i = 1, j = 1; i < conflictToReduce.size(); i++) { if (this.voc.getReason(conflictToReduce.get(i)) == null || !analyzeRemovableWLOnly(conflictToReduce.get(i))) { conflictToReduce.moveTo(j++, i); } } conflictToReduce.shrink(i - j); this.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 analyzeRemovableWLOnly(int p) { assert this.voc.getReason(p) != null; this.analyzestack.clear(); this.analyzestack.push(p); final boolean[] seen = this.mseen; int top = this.analyzetoclear.size(); while (this.analyzestack.size() > 0) { int q = this.analyzestack.last(); assert this.voc.getReason(q) != null; Constr c = this.voc.getReason(q); this.analyzestack.pop(); for (int i = 1; i < c.size(); i++) { int l = c.get(i); if (!seen[var(l)] && this.voc.getLevel(l) != 0) { if (this.voc.getReason(l) == null) { for (int j = top; j < this.analyzetoclear.size(); j++) { seen[this.analyzetoclear.get(j) >> 1] = false; } this.analyzetoclear.shrink(this.analyzetoclear.size() - top); return false; } seen[l >> 1] = true; this.analyzestack.push(l); this.analyzetoclear.push(l); } } } return true; } // END Minisat 1.14 cut and paste /** * */ protected void undoOne() { // gather last assigned literal int p = this.trail.last(); assert p > 1; assert this.voc.getLevel(p) >= 0; int x = p >> 1; // unassign variable this.voc.unassign(p); this.voc.setReason(p, null); this.voc.setLevel(p, -1); // update heuristics value this.order.undo(x); // remove literal from the trail this.trail.pop(); // update constraints on backtrack. // not used if the solver uses watched literals. IVec<Undoable> undos = this.voc.undos(p); assert undos != null; for (int size = undos.size(); size > 0; size--) { undos.last().undo(p); undos.pop(); } } /** * Propagate activity to a constraint * * @param confl * a constraint */ public void claBumpActivity(Constr confl) { confl.incActivity(this.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) { this.order.updateVar(p); } private void claRescalActivity() { for (int i = 0; i < this.learnts.size(); i++) { this.learnts.get(i).rescaleBy(CLAUSE_RESCALE_FACTOR); } this.claInc *= CLAUSE_RESCALE_FACTOR; } private final IVec<Propagatable> watched = new Vec<Propagatable>(); /** * @return null if not conflict is found, else a conflicting constraint. */ public final Constr propagate() { IVecInt ltrail = this.trail; SolverStats lstats = this.stats; IOrder lorder = this.order; SearchListener lslistener = this.slistener; // ltrail.size() changes due to propagation // cannot cache that value. while (this.qhead < ltrail.size()) { lstats.propagations++; int p = ltrail.get(this.qhead++); lslistener.propagating(toDimacs(p), null); lorder.assignLiteral(p); Constr confl = reduceClausesForFalsifiedLiteral(p); if (confl != null) { return confl; } } return null; } private Constr reduceClausesForFalsifiedLiteral(int 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> lwatched = this.watched; lwatched.clear(); this.voc.watches(p).moveTo(lwatched); final int size = lwatched.size(); for (int i = 0; i < size; i++) { this.stats.inspects++; // try shortcut // shortcut = shortcuts.get(i); // if (shortcut != ILits.UNDEFINED && voc.isSatisfied(shortcut)) // { // voc.watch(p, watched.get(i), shortcut); // stats.shortcuts++; // continue; // } if (!lwatched.get(i).propagate(this, p)) { // Constraint is conflicting: copy remaining watches to // watches[p] // and return constraint final int sizew = lwatched.size(); for (int j = i + 1; j < sizew; j++) { this.voc.watch(p, lwatched.get(j)); } this.qhead = this.trail.size(); // propQ.clear(); return lwatched.get(i).toConstraint(); } } return null; } void record(Constr constr) { constr.assertConstraint(this); int p = toDimacs(constr.get(0)); this.slistener.adding(p); if (constr.size() == 1) { this.stats.learnedliterals++; this.slistener.learnUnit(p); } else { this.learner.learns(constr); } } /** * @return false ssi conflit imm?diat. */ public boolean assume(int p) { // Precondition: assume propagation queue is empty assert this.trail.size() == this.qhead; assert !this.trailLim.contains(this.trail.size()); this.trailLim.push(this.trail.size()); return enqueue(p); } /** * Revert to the state before the last assume() */ private void cancel() { // assert trail.size() == qhead || !undertimeout; int decisionvar = this.trail.unsafeGet(this.trailLim.last()); this.slistener.backtracking(toDimacs(decisionvar)); for (int c = this.trail.size() - this.trailLim.last(); c > 0; c--) { undoOne(); } this.trailLim.pop(); this.qhead = this.trail.size(); } /** * Restore literals */ private void cancelLearntLiterals(int learnedLiteralsLimit) { this.learnedLiterals.clear(); // assert trail.size() == qhead || !undertimeout; while (this.trail.size() > learnedLiteralsLimit) { this.learnedLiterals.push(this.trail.last()); undoOne(); } // qhead = 0; // learnedLiterals = 0; } /** * Cancel several levels of assumptions * * @param level */ protected void cancelUntil(int level) { while (decisionLevel() > level) { cancel(); } } private final Pair analysisResult = new Pair(); private boolean[] userbooleanmodel; private IVecInt unsatExplanationInTermsOfAssumptions; Lbool search(IVecInt assumps) { assert this.rootLevel == decisionLevel(); this.stats.starts++; int backjumpLevel; // varDecay = 1 / params.varDecay; this.order.setVarDecay(1 / this.params.getVarDecay()); this.claDecay = 1 / this.params.getClaDecay(); do { this.slistener.beginLoop(); // propagate unit clauses and other constraints Constr confl = propagate(); assert this.trail.size() == this.qhead; if (confl == null) { // No conflict found if (decisionLevel() == 0 && this.isDBSimplificationAllowed) { this.stats.rootSimplifications++; boolean ret = simplifyDB(); assert ret; } assert nAssigns() <= this.voc.realnVars(); if (nAssigns() == this.voc.realnVars()) { modelFound(); this.slistener.solutionFound( (this.fullmodel != null) ? this.fullmodel : this.model, this); if (this.sharedConflict == null) { cancelUntil(this.rootLevel); return Lbool.TRUE; } else { confl = this.sharedConflict; } } else { if (this.restarter.shouldRestart()) { cancelUntil(this.rootLevel); return Lbool.UNDEFINED; } if (this.needToReduceDB) { reduceDB(); this.needToReduceDB = false; } if (this.sharedConflict == null) { // New variable decision this.stats.decisions++; int p = this.order.select(); if (p == ILits.UNDEFINED) { confl = preventTheSameDecisionsToBeMade(); this.lastConflictMeansUnsat = false; } else { assert p > 1; this.slistener.assuming(toDimacs(p)); boolean ret = assume(p); assert ret; } } else { confl = this.sharedConflict; } } } if (confl != null) { // conflict found this.stats.conflicts++; this.slistener.conflictFound(confl, decisionLevel(), this.trail.size()); this.conflictCount.newConflict(); if (decisionLevel() == this.rootLevel) { if (this.lastConflictMeansUnsat) { // conflict at root level, the formula is inconsistent this.unsatExplanationInTermsOfAssumptions = analyzeFinalConflictInTermsOfAssumptions( confl, assumps, ILits.UNDEFINED); return Lbool.FALSE; } return Lbool.UNDEFINED; } int conflictTrailLevel = this.trail.size(); // analyze conflict try { analyze(confl, this.analysisResult); } catch (TimeoutException e) { return Lbool.UNDEFINED; } assert this.analysisResult.backtrackLevel < decisionLevel(); backjumpLevel = Math.max(this.analysisResult.backtrackLevel, this.rootLevel); this.slistener.backjump(backjumpLevel); cancelUntil(backjumpLevel); if (backjumpLevel == this.rootLevel) { this.restarter.onBackjumpToRootLevel(); } if (confl == this.sharedConflict) { this.sharedConflict.assertConstraintIfNeeded(this); this.sharedConflict = null; } assert decisionLevel() >= this.rootLevel && decisionLevel() >= this.analysisResult.backtrackLevel; if (this.analysisResult.reason == null) { return Lbool.FALSE; } record(this.analysisResult.reason); this.restarter.newLearnedClause(this.analysisResult.reason, conflictTrailLevel); this.analysisResult.reason = null; decayActivities(); } } while (this.undertimeout); return Lbool.UNDEFINED; // timeout occured } private Constr preventTheSameDecisionsToBeMade() { IVecInt clause = new VecInt(nVars()); int p; for (int i = this.trail.size() - 1; i >= this.rootLevel; i--) { p = this.trail.get(i); if (this.voc.getReason(p) == null) { clause.push(p ^ 1); } } return this.dsfactory.createUnregisteredClause(clause); } protected void analyzeAtRootLevel(Constr conflict) { } private final IVecInt implied = new VecInt(); private final IVecInt decisions = new VecInt(); private int[] fullmodel; /** * */ void modelFound() { IVecInt tempmodel = new VecInt(nVars()); this.userbooleanmodel = new boolean[realNumberOfVariables()]; this.fullmodel = null; for (int i = 1; i <= nVars(); i++) { if (this.voc.belongsToPool(i)) { int p = this.voc.getFromPool(i); if (!this.voc.isUnassigned(p)) { tempmodel.push(this.voc.isSatisfied(p) ? i : -i); this.userbooleanmodel[i - 1] = this.voc.isSatisfied(p); if (this.voc.getReason(p) == null && voc.getLevel(p) > 0) { this.decisions.push(tempmodel.last()); } else { this.implied.push(tempmodel.last()); } } } } this.model = new int[tempmodel.size()]; tempmodel.copyTo(this.model); if (realNumberOfVariables() > nVars()) { for (int i = nVars() + 1; i <= realNumberOfVariables(); i++) { if (this.voc.belongsToPool(i)) { int p = this.voc.getFromPool(i); if (!this.voc.isUnassigned(p)) { tempmodel.push(this.voc.isSatisfied(p) ? i : -i); this.userbooleanmodel[i - 1] = this.voc.isSatisfied(p); if (this.voc.getReason(p) == null) { this.decisions.push(tempmodel.last()); } else { this.implied.push(tempmodel.last()); } } } } this.fullmodel = new int[tempmodel.size()]; tempmodel.moveTo(this.fullmodel); } else { this.fullmodel = this.model; } } /** * Forget a variable in the formula by falsifying both its positive and * negative literals. * * @param var * a variable * @return a conflicting constraint resulting from the disparition of those * literals. */ private Constr forget(int var) { boolean satisfied = this.voc.isSatisfied(toInternal(var)); this.voc.forgets(var); Constr confl; if (satisfied) { confl = reduceClausesForFalsifiedLiteral(LiteralsUtils .toInternal(-var)); } else { confl = reduceClausesForFalsifiedLiteral(LiteralsUtils .toInternal(var)); } return confl; } /** * Assume literal p and perform unit propagation * * @param p * a literal * @return true if no conflict is reached, false if a conflict is found. */ private boolean setAndPropagate(int p) { if (voc.isUnassigned(p)) { assert !trail.contains(p); assert !trail.contains(neg(p)); return assume(p) && propagate() == null; } return voc.isSatisfied(p); } private int[] prime; public int[] primeImplicant() { assert this.qhead == this.trail.size() + this.learnedLiterals.size(); if (this.learnedLiterals.size() > 0) { this.qhead = trail.size(); } if (isVerbose()) { System.out.printf("%s implied: %d, decision: %d %n", getLogPrefix(), implied.size(), decisions.size()); } this.prime = new int[realNumberOfVariables() + 1]; int p, d; for (int i = 0; i < this.prime.length; i++) { this.prime[i] = 0; } boolean noproblem; for (IteratorInt it = this.implied.iterator(); it.hasNext();) { d = it.next(); p = toInternal(d); this.prime[Math.abs(d)] = d; noproblem = setAndPropagate(p); assert noproblem; } boolean canBeRemoved; int rightlevel; int removed = 0; int propagated = 0; int tested = 0; int l2propagation = 0; for (int i = 0; i < this.decisions.size(); i++) { d = this.decisions.get(i); assert !this.voc.isFalsified(toInternal(d)); if (this.voc.isSatisfied(toInternal(d))) { // d has been propagated this.prime[Math.abs(d)] = d; propagated++; } else if (setAndPropagate(toInternal(-d))) { canBeRemoved = true; tested++; rightlevel = currentDecisionLevel(); for (int j = i + 1; j < this.decisions.size(); j++) { l2propagation++; if (!setAndPropagate(toInternal(this.decisions.get(j)))) { canBeRemoved = false; break; } } cancelUntil(rightlevel); if (canBeRemoved) { // it is not a necessary literal forget(Math.abs(d)); IConstr confl = propagate(); assert confl == null; removed++; } else { this.prime[Math.abs(d)] = d; cancel(); assert voc.isUnassigned(toInternal(d)); noproblem = setAndPropagate(toInternal(d)); assert noproblem; } } else { // conflict, literal is necessary this.prime[Math.abs(d)] = d; cancel(); noproblem = setAndPropagate(toInternal(d)); assert noproblem; } } cancelUntil(0); int[] implicant = new int[this.prime.length - removed - 1]; int index = 0; for (int i : this.prime) { if (i != 0) { implicant[index++] = i; } } if (isVerbose()) { System.out.printf("%s prime implicant computation statistics%n", getLogPrefix()); System.out .printf("%s implied: %d, decision: %d (removed %d, tested %d, propagated %d), l2 propagation:%d%n", getLogPrefix(), implied.size(), decisions.size(), removed, tested, propagated, l2propagation); } return implicant; } public boolean primeImplicant(int p) { if (p == 0 || Math.abs(p) > realNumberOfVariables()) { throw new IllegalArgumentException( "Use a valid Dimacs var id as argument!"); //$NON-NLS-1$ } if (this.prime == null) { throw new UnsupportedOperationException( "Call the primeImplicant method first!!!"); //$NON-NLS-1$ } return this.prime[Math.abs(p)] == p; } public boolean model(int var) { if (var <= 0 || var > realNumberOfVariables()) { throw new IllegalArgumentException( "Use a valid Dimacs var id as argument!"); //$NON-NLS-1$ } if (this.userbooleanmodel == null) { throw new UnsupportedOperationException( "Call the solve method first!!!"); //$NON-NLS-1$ } return this.userbooleanmodel[var - 1]; } public void clearLearntClauses() { for (Iterator<Constr> iterator = this.learnts.iterator(); iterator .hasNext();) { iterator.next().remove(this); } this.learnts.clear(); this.learnedLiterals.clear(); } protected final void reduceDB() { this.stats.reduceddb++; this.slistener.cleaning(); this.learnedConstraintsDeletionStrategy.reduce(this.learnts); System.gc(); } /** * @param learnts */ protected void sortOnActivity() { this.learnts.sort(this.comparator); } /** * */ protected void decayActivities() { this.order.varDecayActivity(); claDecayActivity(); } /** * */ private void claDecayActivity() { this.claInc *= this.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 ConflictTimerContainer conflictCount; private transient Timer timer; public boolean isSatisfiable(IVecInt assumps) throws TimeoutException { return isSatisfiable(assumps, false); } public final LearnedConstraintsDeletionStrategy fixedSize(final int maxsize) { return new LearnedConstraintsDeletionStrategy() { private static final long serialVersionUID = 1L; private final ConflictTimer aTimer = new ConflictTimerAdapter( maxsize) { private static final long serialVersionUID = 1L; @Override public void run() { Solver.this.needToReduceDB = true; } }; public void reduce(IVec<Constr> learnedConstrs) { int i, j, k; for (i = j = k = 0; i < Solver.this.learnts.size() && Solver.this.learnts.size() - k > maxsize; i++) { Constr c = Solver.this.learnts.get(i); if (c.locked() || c.size() == 2) { Solver.this.learnts .set(j++, Solver.this.learnts.get(i)); } else { c.remove(Solver.this); k++; } } for (; i < Solver.this.learnts.size(); i++) { Solver.this.learnts.set(j++, Solver.this.learnts.get(i)); } if (Solver.this.verbose) { Solver.this.out.log(getLogPrefix() + "cleaning " + (Solver.this.learnts.size() - j) //$NON-NLS-1$ + " clauses out of " + Solver.this.learnts.size()); //$NON-NLS-1$ // out.flush(); } Solver.this.learnts.shrinkTo(j); } public void onConflictAnalysis(Constr reason) { // TODO Auto-generated method stub } public void onClauseLearning(Constr outLearnt) { // TODO Auto-generated method stub } @Override public String toString() { return "Fixed size (" + maxsize + ") learned constraints deletion strategy"; } public void init() { } public ConflictTimer getTimer() { return this.aTimer; } public void onPropagation(Constr from) { // TODO Auto-generated method stub } }; } private LearnedConstraintsDeletionStrategy activityBased( final ConflictTimer timer) { return new LearnedConstraintsDeletionStrategy() { private static final long serialVersionUID = 1L; private final ConflictTimer freeMem = timer; public void reduce(IVec<Constr> learnedConstrs) { sortOnActivity(); int i, j; for (i = j = 0; i < Solver.this.learnts.size() / 2; i++) { Constr c = Solver.this.learnts.get(i); if (c.locked() || c.size() == 2) { Solver.this.learnts .set(j++, Solver.this.learnts.get(i)); } else { c.remove(Solver.this); } } for (; i < Solver.this.learnts.size(); i++) { Solver.this.learnts.set(j++, Solver.this.learnts.get(i)); } if (Solver.this.verbose) { Solver.this.out.log(getLogPrefix() + "cleaning " + (Solver.this.learnts.size() - j) //$NON-NLS-1$ + " clauses out of " + Solver.this.learnts.size()); //$NON-NLS-1$ // out.flush(); } Solver.this.learnts.shrinkTo(j); } public ConflictTimer getTimer() { return this.freeMem; } @Override public String toString() { return "Memory based learned constraints deletion strategy"; } public void init() { // do nothing } public void onClauseLearning(Constr constr) { // do nothing } public void onConflictAnalysis(Constr reason) { if (reason.learnt()) { claBumpActivity(reason); } } public void onPropagation(Constr from) { // do nothing } }; } private final ConflictTimer memoryTimer = new ConflictTimerAdapter(500) { private static final long serialVersionUID = 1L; final long memorybound = Runtime.getRuntime().freeMemory() / 10; @Override public void run() { long freemem = Runtime.getRuntime().freeMemory(); // System.out.println("c Free memory "+freemem); if (freemem < this.memorybound) { // Reduce the set of learnt clauses Solver.this.needToReduceDB = true; } } }; /** * @since 2.1 */ public final LearnedConstraintsDeletionStrategy memory_based = activityBased(this.memoryTimer); private class GlucoseLCDS implements LearnedConstraintsDeletionStrategy { private static final long serialVersionUID = 1L; private int[] flags = new int[0]; private int flag = 0; // private int wall = 0; private final ConflictTimer clauseManagement; GlucoseLCDS(ConflictTimer timer) { this.clauseManagement = timer; } public void reduce(IVec<Constr> learnedConstrs) { sortOnActivity(); int i, j; for (i = j = learnedConstrs.size() / 2; i < learnedConstrs.size(); i++) { Constr c = learnedConstrs.get(i); if (c.locked() || c.getActivity() <= 2.0) { learnedConstrs.set(j++, Solver.this.learnts.get(i)); } else { c.remove(Solver.this); } } if (Solver.this.verbose) { Solver.this.out .log(getLogPrefix() + "cleaning " + (learnedConstrs.size() - j) //$NON-NLS-1$ + " clauses out of " + learnedConstrs.size() + " with flag " + this.flag + "/" + Solver.this.stats.conflicts); //$NON-NLS-1$ //$NON-NLS-2$ // out.flush(); } Solver.this.learnts.shrinkTo(j); } public ConflictTimer getTimer() { return this.clauseManagement; } @Override public String toString() { return "Glucose learned constraints deletion strategy"; } public void init() { final int howmany = Solver.this.voc.nVars(); // wall = constrs.size() > 10000 ? constrs.size() : 10000; if (this.flags.length <= howmany) { this.flags = new int[howmany + 1]; } this.flag = 0; this.clauseManagement.reset(); } public void onClauseLearning(Constr constr) { int nblevel = computeLBD(constr); constr.incActivity(nblevel); } protected int computeLBD(Constr constr) { int nblevel = 1; this.flag++; int currentLevel; for (int i = 1; i < constr.size(); i++) { currentLevel = Solver.this.voc.getLevel(constr.get(i)); if (this.flags[currentLevel] != this.flag) { this.flags[currentLevel] = this.flag; nblevel++; } } return nblevel; } public void onConflictAnalysis(Constr reason) { } public void onPropagation(Constr from) { } } private class Glucose2LCDS extends GlucoseLCDS { /** * */ private static final long serialVersionUID = 1L; Glucose2LCDS(ConflictTimer timer) { super(timer); } @Override public String toString() { return "Glucose 2 learned constraints deletion strategy"; } @Override public void onPropagation(Constr from) { if (from.getActivity() > 2.0) { int nblevel = computeLBD(from); if (nblevel < from.getActivity()) { Solver.this.stats.updateLBD++; from.setActivity(nblevel); } } } } private final ConflictTimer lbdTimer = new ConflictTimerAdapter(1000) { private static final long serialVersionUID = 1L; private int nbconflict = 0; private static final int MAX_CLAUSE = 5000; private static final int INC_CLAUSE = 1000; private int nextbound = MAX_CLAUSE; @Override public void run() { this.nbconflict += bound(); if (this.nbconflict >= this.nextbound) { this.nextbound += INC_CLAUSE; // if (nextbound > wall) { // nextbound = wall; // } this.nbconflict = 0; Solver.this.needToReduceDB = true; } } @Override public void reset() { super.reset(); this.nextbound = MAX_CLAUSE; if (this.nbconflict >= this.nextbound) { this.nbconflict = 0; Solver.this.needToReduceDB = true; } } }; /** * @since 2.1 */ public final LearnedConstraintsDeletionStrategy glucose = new Glucose2LCDS( this.lbdTimer); protected LearnedConstraintsDeletionStrategy learnedConstraintsDeletionStrategy = this.glucose; /* * (non-Javadoc) * * @see * org.sat4j.minisat.core.ICDCL#setLearnedConstraintsDeletionStrategy(org * .sat4j.minisat.core.Solver.LearnedConstraintsDeletionStrategy) */ public void setLearnedConstraintsDeletionStrategy( LearnedConstraintsDeletionStrategy lcds) { if (this.conflictCount != null) { this.conflictCount.add(lcds.getTimer()); assert this.learnedConstraintsDeletionStrategy != null; this.conflictCount.remove(this.learnedConstraintsDeletionStrategy .getTimer()); } this.learnedConstraintsDeletionStrategy = lcds; } private boolean lastConflictMeansUnsat; public boolean isSatisfiable(IVecInt assumps, boolean global) throws TimeoutException { Lbool status = Lbool.UNDEFINED; boolean alreadylaunched = this.conflictCount != null; final int howmany = this.voc.nVars(); if (this.mseen.length <= howmany) { this.mseen = new boolean[howmany + 1]; } this.trail.ensure(howmany); this.trailLim.ensure(howmany); this.learnedLiterals.ensure(howmany); this.decisions.clear(); this.implied.clear(); this.slistener.init(this); this.slistener.start(); this.model = null; // forget about previous model this.userbooleanmodel = null; this.prime = null; this.unsatExplanationInTermsOfAssumptions = null; if (!alreadylaunched || !this.keepHot) { this.order.init(); } this.learnedConstraintsDeletionStrategy.init(); int learnedLiteralsLimit = this.trail.size(); // Fix for Bug SAT37 this.qhead = 0; // Apply undos on unit literals because they are getting propagated // again now that qhead is 0. for (int i = learnedLiteralsLimit - 1; i >= 0; i--) { int p = this.trail.get(i); IVec<Undoable> undos = this.voc.undos(p); assert undos != null; for (int size = undos.size(); size > 0; size--) { undos.last().undo(p); undos.pop(); } } // push previously learned literals for (IteratorInt iterator = this.learnedLiterals.iterator(); iterator .hasNext();) { enqueue(iterator.next()); } // propagate constraints Constr confl = propagate(); if (confl != null) { analyzeAtRootLevel(confl); this.slistener.conflictFound(confl, 0, 0); this.slistener.end(Lbool.FALSE); cancelUntil(0); cancelLearntLiterals(learnedLiteralsLimit); return false; } // push incremental assumptions for (IteratorInt iterator = assumps.iterator(); iterator.hasNext();) { int assump = iterator.next(); int p = this.voc.getFromPool(assump); if (!this.voc.isSatisfied(p) && !assume(p) || (confl = propagate()) != null) { if (confl == null) { this.slistener.conflictFound(p); this.unsatExplanationInTermsOfAssumptions = analyzeFinalConflictInTermsOfAssumptions( null, assumps, p); this.unsatExplanationInTermsOfAssumptions.push(assump); } else { this.slistener.conflictFound(confl, decisionLevel(), this.trail.size()); this.unsatExplanationInTermsOfAssumptions = analyzeFinalConflictInTermsOfAssumptions( confl, assumps, ILits.UNDEFINED); } this.slistener.end(Lbool.FALSE); cancelUntil(0); cancelLearntLiterals(learnedLiteralsLimit); return false; } } this.rootLevel = decisionLevel(); // moved initialization here if new literals are added in the // assumptions. if (!alreadylaunched || !this.keepHot) { this.order.init(); // duplicated on purpose } this.learner.init(); if (!alreadylaunched) { this.conflictCount = new ConflictTimerContainer(); this.conflictCount.add(this.restarter); this.conflictCount.add(this.learnedConstraintsDeletionStrategy .getTimer()); } boolean firstTimeGlobal = false; if (this.timeBasedTimeout) { if (!global || this.timer == null) { firstTimeGlobal = true; this.undertimeout = true; TimerTask stopMe = new TimerTask() { @Override public void run() { Solver.this.undertimeout = false; } }; this.timer = new Timer(true); this.timer.schedule(stopMe, this.timeout); } } else { if (!global || !alreadylaunched) { firstTimeGlobal = true; this.undertimeout = true; ConflictTimer conflictTimeout = new ConflictTimerAdapter( (int) this.timeout) { private static final long serialVersionUID = 1L; @Override public void run() { Solver.this.undertimeout = false; } }; this.conflictCount.add(conflictTimeout); } } if (!global || firstTimeGlobal) { this.restarter.init(this.params, this.stats); this.timebegin = System.currentTimeMillis(); } this.needToReduceDB = false; // this is used to allow the solver to be incomplete, // when using a heuristics limited to a subset of variables this.lastConflictMeansUnsat = true; // Solve while (status == Lbool.UNDEFINED && this.undertimeout && this.lastConflictMeansUnsat) { int before = this.trail.size(); unitClauseProvider.provideUnitClauses(this); this.stats.importedUnits += this.trail.size() - before; status = search(assumps); if (status == Lbool.UNDEFINED) { this.restarter.onRestart(); this.slistener.restarting(); } } cancelUntil(0); cancelLearntLiterals(learnedLiteralsLimit); if (!global && this.timeBasedTimeout && this.timer != null) { this.timer.cancel(); this.timer = null; } this.slistener.end(status); if (!this.undertimeout) { String message = " Timeout (" + this.timeout + (this.timeBasedTimeout ? "s" : " conflicts") + ") exceeded"; throw new TimeoutException(message); } if (status == Lbool.UNDEFINED && !this.lastConflictMeansUnsat) { throw new TimeoutException("Cannot decide the satisfiability"); } // When using a search enumerator (to compute all models) // the final answer is FALSE, however we are aware of at least one model // (the last one) return model != null; } public void printInfos(PrintWriter out) { printInfos(out, prefix); } public void printInfos(PrintWriter out, String prefix) { out.print(prefix); out.println("constraints type "); long total = 0; for (Map.Entry<String, Counter> entry : this.constrTypes.entrySet()) { out.println(prefix + entry.getKey() + " => " + entry.getValue()); total += entry.getValue().getValue(); } out.print(prefix); out.print(total); out.println(" constraints processed."); } /** * @since 2.1 */ public void printLearntClausesInfos(PrintWriter out, String prefix) { Map<String, Counter> learntTypes = new HashMap<String, Counter>(); for (Iterator<Constr> it = this.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 this.stats; } /** * * @param myStats * @since 2.2 */ protected void initStats(SolverStats myStats) { this.stats = myStats; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#getOrder() */ public IOrder getOrder() { return this.order; } /* * (non-Javadoc) * * @see org.sat4j.minisat.core.ICDCL#setOrder(org.sat4j.minisat.core.IOrder) */ public void setOrder(IOrder h) { this.order = h; this.order.setLits(this.voc); } public ILits getVocabulary() { return this.voc; } public void reset() { if (this.timer != null) { this.timer.cancel(); this.timer = null; } this.trail.clear(); this.trailLim.clear(); this.qhead = 0; for (Iterator<Constr> iterator = this.constrs.iterator(); iterator .hasNext();) { iterator.next().remove(this); } this.constrs.clear(); clearLearntClauses(); this.voc.resetPool(); this.dsfactory.reset(); this.stats.reset(); this.constrTypes.clear(); } public int nVars() { if (this.declaredMaxVarId == 0) { return this.voc.nVars(); } return this.declaredMaxVarId; } /** * @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) { Counter count = this.constrTypes .get("ignored satisfied constraints"); if (count == null) { this.constrTypes.put("ignored satisfied constraints", new Counter()); } else { count.inc(); } } else { this.constrs.push(constr); String type = constr.getClass().getName(); Counter count = this.constrTypes.get(type); if (count == null) { this.constrTypes.put(type, new Counter()); } else { count.inc(); } } return constr; } public DataStructureFactory getDSFactory() { return this.dsfactory; } public IVecInt getOutLearnt() { return this.moutLearnt; } /** * 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 this.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, true), prefix); } public void printStat(PrintWriter out) { printStat(out, prefix); } public void printStat(PrintWriter out, String prefix) { this.stats.printStat(out, prefix); double cputime = (System.currentTimeMillis() - this.timebegin) / 1000; out.println(prefix + "speed (assignments/second)\t: " + this.stats.propagations //$NON-NLS-1$ / cputime); this.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 = { this.dsfactory, this.learner, this.params, this.order, this.simplifier, this.restarter, this.learnedConstraintsDeletionStrategy }; 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 (this.timeBasedTimeout) { stb.append(this.timeout / 1000); stb.append("s\n"); } else { stb.append(this.timeout); stb.append(" conflicts\n"); } stb.append(prefix); stb.append("DB Simplification allowed="); stb.append(this.isDBSimplificationAllowed); stb.append("\n"); stb.append(prefix); if (isSolverKeptHot()) { stb.append("Heuristics kept accross calls (keep the solver \"hot\")\n"); stb.append(prefix); } stb.append("Listener: "); stb.append(slistener); 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) (this.timeBasedTimeout ? this.timeout / 1000 : this.timeout); } /** * @since 2.1 */ public long getTimeoutMs() { if (!this.timeBasedTimeout) { throw new UnsupportedOperationException( "The timeout is given in number of conflicts!"); } return this.timeout; } public void setExpectedNumberOfClauses(int nb) { this.constrs.ensure(nb); } public Map<String, Number> getStat() { return this.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 this.isDBSimplificationAllowed; } public void setDBSimplificationAllowed(boolean status) { this.isDBSimplificationAllowed = status; } /** * @since 2.1 */ public int nextFreeVarId(boolean reserve) { return this.voc.nextFreeVarId(reserve); } /** * @since 2.1 */ public IConstr addBlockingClause(IVecInt literals) throws ContradictionException { return addClause(literals); } /** * @since 2.1 */ public void unset(int p) { // the literal might already have been // removed from the trail. if (this.voc.isUnassigned(p) || this.trail.isEmpty()) { return; } int current = this.trail.last(); while (current != p) { undoOne(); if (this.trail.isEmpty()) { return; } current = this.trail.last(); } undoOne(); this.qhead = this.trail.size(); } /** * @since 2.2 */ public void setLogPrefix(String prefix) { this.prefix = prefix; } /** * @since 2.2 */ public String getLogPrefix() { return this.prefix; } /** * @since 2.2 */ public IVecInt unsatExplanation() { IVecInt copy = new VecInt( this.unsatExplanationInTermsOfAssumptions.size()); this.unsatExplanationInTermsOfAssumptions.copyTo(copy); return copy; } /** * @since 2.3.1 */ public int[] modelWithInternalVariables() { if (this.model == null) { throw new UnsupportedOperationException( "Call the solve method first!!!"); //$NON-NLS-1$ } int[] nmodel; if (nVars() == realNumberOfVariables()) { nmodel = new int[this.model.length]; System.arraycopy(this.model, 0, nmodel, 0, nmodel.length); } else { nmodel = new int[this.fullmodel.length]; System.arraycopy(this.fullmodel, 0, nmodel, 0, nmodel.length); } return nmodel; } /** * @since 2.3.1 */ public int realNumberOfVariables() { return this.voc.nVars(); } /** * @since 2.3.2 */ public void stop() { expireTimeout(); } protected Constr sharedConflict; /** * @since 2.3.2 */ public void backtrack(int[] reason) { IVecInt clause = new VecInt(reason.length); for (int d : reason) { clause.push(LiteralsUtils.toInternal(d)); } this.sharedConflict = this.dsfactory.createUnregisteredClause(clause); learn(this.sharedConflict); } /** * @since 2.3.2 */ public Lbool truthValue(int literal) { int p = LiteralsUtils.toInternal(literal); if (this.voc.isFalsified(p)) { return Lbool.FALSE; } if (this.voc.isSatisfied(p)) { return Lbool.TRUE; } return Lbool.UNDEFINED; } /** * @since 2.3.2 */ public int currentDecisionLevel() { return decisionLevel(); } /** * @since 2.3.2 */ public int[] getLiteralsPropagatedAt(int decisionLevel) { throw new UnsupportedOperationException("Not implemented yet!"); } /** * @since 2.3.2 */ public void suggestNextLiteralToBranchOn(int l) { throw new UnsupportedOperationException("Not implemented yet!"); } protected boolean isNeedToReduceDB() { return this.needToReduceDB; } public void setNeedToReduceDB(boolean needToReduceDB) { this.needToReduceDB = needToReduceDB; } public void setLogger(ILogAble out) { this.out = out; } public ILogAble getLogger() { return this.out; } public double[] getVariableHeuristics() { return this.order.getVariableHeuristics(); } public IVec<Constr> getLearnedConstraints() { return this.learnts; } /** * @since 2.3.2 */ public void setLearnedConstraintsDeletionStrategy(ConflictTimer timer, LearnedConstraintsEvaluationType evaluation) { if (this.conflictCount != null) { this.conflictCount.add(timer); this.conflictCount.remove(this.learnedConstraintsDeletionStrategy .getTimer()); } switch (evaluation) { case ACTIVITY: this.learnedConstraintsDeletionStrategy = activityBased(timer); break; case LBD: this.learnedConstraintsDeletionStrategy = new GlucoseLCDS(timer); break; case LBD2: this.learnedConstraintsDeletionStrategy = new Glucose2LCDS(timer); break; } if (this.conflictCount != null) { this.learnedConstraintsDeletionStrategy.init(); } } /** * @since 2.3.2 */ public void setLearnedConstraintsDeletionStrategy( LearnedConstraintsEvaluationType evaluation) { ConflictTimer aTimer = this.learnedConstraintsDeletionStrategy .getTimer(); switch (evaluation) { case ACTIVITY: this.learnedConstraintsDeletionStrategy = activityBased(aTimer); break; case LBD: this.learnedConstraintsDeletionStrategy = new GlucoseLCDS(aTimer); break; case LBD2: this.learnedConstraintsDeletionStrategy = new Glucose2LCDS(aTimer); break; } if (this.conflictCount != null) { this.learnedConstraintsDeletionStrategy.init(); } } public boolean isSolverKeptHot() { return this.keepHot; } public void setKeepSolverHot(boolean keepHot) { this.keepHot = keepHot; } private final Comparator<Integer> dimacsLevel = new Comparator<Integer>() { public int compare(Integer i1, Integer i2) { return voc.getLevel(Math.abs(i2)) - voc.getLevel(Math.abs(i1)); } }; public IConstr addClauseOnTheFly(int[] literals) { List<Integer> lliterals = new ArrayList<Integer>(); for (Integer d : literals) { lliterals.add(d); } Collections.sort(lliterals, dimacsLevel); IVecInt clause = new VecInt(literals.length); for (int d : lliterals) { clause.push(LiteralsUtils.toInternal(d)); } this.sharedConflict = this.dsfactory.createUnregisteredClause(clause); this.sharedConflict.register(); addConstr(this.sharedConflict); IVecInt reason = new VecInt(); this.sharedConflict.calcReasonOnTheFly(ILits.UNDEFINED, trail, reason); Set<Integer> subset = fromLastDecisionLevel(reason); while (!trail.isEmpty() && !subset.contains(trail.last())) { undoOne(); if (!trailLim.isEmpty() && trailLim.last() == trail.size()) { trailLim.pop(); } } return this.sharedConflict; } public ISolver getSolvingEngine() { return this; } /** * * @param literals */ public IConstr addAtMostOnTheFly(int[] literals, int degree) { IVecInt clause = new VecInt(literals.length); for (int d : literals) { clause.push(LiteralsUtils.toInternal(-d)); } IVecInt copy = new VecInt(clause.size()); clause.copyTo(copy); this.sharedConflict = this.dsfactory .createUnregisteredCardinalityConstraint(copy, literals.length - degree); this.sharedConflict.register(); addConstr(this.sharedConflict); // backtrack to the first decision level with a reason // for falsifying that constraint IVecInt reason = new VecInt(); this.sharedConflict.calcReasonOnTheFly(ILits.UNDEFINED, trail, reason); Set<Integer> subset = fromLastDecisionLevel(reason); while (!trail.isEmpty() && !subset.contains(trail.last())) { undoOne(); if (!trailLim.isEmpty() && trailLim.last() == trail.size()) { trailLim.pop(); } } return this.sharedConflict; } protected Set<Integer> fromLastDecisionLevel(IVecInt lits) { Set<Integer> subset = new HashSet<Integer>(); int max = -1, q, level; for (int i = 0; i < lits.size(); i++) { q = lits.get(i); level = voc.getLevel(q); if (level > max) { subset.clear(); subset.add(q); max = level; } else if (level == max) { subset.add(q); } } return subset; } public void setUnitClauseProvider(UnitClauseProvider ucp) { this.unitClauseProvider = ucp; } }