/* * Copyright (C) 2009-2012 University of Freiburg * * This file is part of SMTInterpol. * * SMTInterpol is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SMTInterpol is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>. */ package de.uni_freiburg.informatik.ultimate.smtinterpol.dpll; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import de.uni_freiburg.informatik.ultimate.logic.Assignments; import de.uni_freiburg.informatik.ultimate.logic.SMTLIBException; import de.uni_freiburg.informatik.ultimate.logic.Term; import de.uni_freiburg.informatik.ultimate.logic.Theory; import de.uni_freiburg.informatik.ultimate.smtinterpol.Config; import de.uni_freiburg.informatik.ultimate.smtinterpol.LogProxy; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Clause.WatchList; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.DPLLAtom.TrueAtom; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.LeafNode; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ProofNode; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ResolutionNode; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ResolutionNode.Antecedent; import de.uni_freiburg.informatik.ultimate.smtinterpol.smtlib2.TerminationRequest; import de.uni_freiburg.informatik.ultimate.smtinterpol.util.CuckooHashSet; import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedHashMap; /** * The DPLL engine. * @author hoenicke * */ public class DPLLEngine { private final LogProxy mLogger; /* Completeness */ public static final int COMPLETE = 0; public static final int INCOMPLETE_QUANTIFIER = 1; public static final int INCOMPLETE_THEORY = 2; public static final int INCOMPLETE_MEMOUT = 3; public static final int INCOMPLETE_UNKNOWN = 4; public static final int INCOMPLETE_TIMEOUT = 5; public static final int INCOMPLETE_CHECK = 6; public static final int INCOMPLETE_CANCELLED = 7; private static final String[] COMPLETENESS_STRINGS = { "Complete", "Quantifier in Assertion Stack", "Theories with incomplete decision procedure used", "Not enough memory", "Unknown internal error", "Sat check timed out", "Incomplete check used", "User requested cancellation" }; private int mCompleteness; /* Incrementality */ /** * Number of active pushs. */ private int mStacklevel = 0; /** * The assertion stack data. */ private StackData mPpStack; /** * List of all input clauses. This list should not contain any learned * clauses! */ private final SimpleList<Clause> mClauses = new SimpleList<Clause>(); /** * Empty clause. This is a cache that speeds up detecting unsatisfiability * in the case our proof does not depend on a newly pushed formula. */ private Clause mUnsatClause = null; /** * If we assume a literal and its negation, we mark the conflicting * assumption in this variable. */ private Literal mConflictingAssumption = null; /* Statistics */ private int mConflicts, mDecides, mTProps, mProps; private int mNumSolvedAtoms, mNumClauses, mNumAxiomClauses; SimpleList<Clause> mLearnedClauses = new SimpleList<Clause>(); private long mPropTime, mPropClauseTime, mExplainTime; private long mSetTime, mCheckTime, mBacktrackTime; private final Theory mSmtTheory; private int mNumRandomSplits; private boolean mHasModel; double mAtomScale = 1 - 1.0 / Config.ATOM_ACTIVITY_FACTOR; double mClsScale = 1 - 1.0 / Config.CLS_ACTIVITY_FACTOR; /** * The list of unit clauses that are not yet decided. */ WatchList mWatcherSetList = new WatchList(); WatchList mWatcherBackList = new WatchList(); ArrayList<Literal> mDecideStack = new ArrayList<Literal>(); /** * The list of all theories. */ private ITheory[] mTheories = new ITheory[0]; private final AtomQueue mAtoms = new AtomQueue(); private int mCurrentDecideLevel = 0; private int mBaseLevel = 0; private boolean mPGenabled = false; private boolean mProduceAssignments = false; private ScopedHashMap<String, Literal> mAssignments; // Random source for the solver. private final Random mRandom; private final TerminationRequest mCancel; public DPLLEngine( Theory smtTheory, LogProxy logger, TerminationRequest cancel) { mSmtTheory = smtTheory; mCompleteness = COMPLETE; assert(logger != null); mLogger = logger; mPpStack = new NonRootLvlStackData(null); // Benchmark sets the seed... mRandom = new Random(); mCancel = cancel; } public int getDecideLevel() { return mCurrentDecideLevel; } public void insertPropagatedLiteral(ITheory t, Literal lit, int decideLevel) { assert (lit.getAtom().mDecideStatus == null); assert (!mDecideStack.contains(lit)); assert (!mDecideStack.contains(lit.negate())); assert t != null : "Decision in propagation!!!"; assert checkDecideLevel(); int stackptr = mDecideStack.size(); int level = mCurrentDecideLevel; while (level > decideLevel) { final DPLLAtom atom = mDecideStack.get(--stackptr).getAtom(); if (atom.mExplanation == null) { level--; } atom.mStackPosition++; } mDecideStack.add(stackptr, lit); final DPLLAtom atom = lit.getAtom(); // We have to tell the ppStack about this literal. Otherwise it will // not be removed on a pop mPpStack.addAtom(atom); assert !mAtoms.contains(atom); atom.mDecideLevel = decideLevel; atom.mStackPosition = stackptr; atom.mDecideStatus = lit; atom.mLastStatus = atom.mDecideStatus; atom.mExplanation = t; if (decideLevel == mBaseLevel) { /* This atom is now decided once and for all. */ mNumSolvedAtoms++; generateLevel0Proof(lit); } assert checkDecideLevel(); } public void insertPropagatedLiteralBefore( ITheory t, Literal lit, Literal beforeLit) { final DPLLAtom beforeAtom = beforeLit.getAtom(); assert (mDecideStack.get(beforeAtom.getStackPosition()).getAtom() == beforeAtom); assert (beforeAtom.mDecideStatus != null); assert (beforeAtom.getStackPosition() >= 0); assert (lit.getAtom().mDecideStatus == null); assert (!mDecideStack.contains(lit)); assert (!mDecideStack.contains(lit.negate())); assert t != null : "Decision in propagation!!!"; assert checkDecideLevel(); final int stackptr = beforeAtom.getStackPosition(); int level = beforeAtom.getDecideLevel(); if (beforeAtom.mExplanation == null) { level--; } mDecideStack.add(stackptr, lit); for (int i = stackptr + 1; i < mDecideStack.size(); i++) { assert mDecideStack.get(i).getAtom().getStackPosition() == i - 1; mDecideStack.get(i).getAtom().mStackPosition = i; } final DPLLAtom atom = lit.getAtom(); // We have to tell the ppStack about this literal. Otherwise it will // not be removed on a pop mPpStack.addAtom(atom); assert !mAtoms.contains(atom); atom.mDecideLevel = level; atom.mStackPosition = stackptr; atom.mDecideStatus = lit; atom.mLastStatus = atom.mDecideStatus; atom.mExplanation = t; if (level == mBaseLevel) { /* This atom is now decided once and for all. */ mNumSolvedAtoms++; generateLevel0Proof(lit); } assert checkDecideLevel(); } /** * Propagate unit clauses first in boolean part, then in * the theory part. * @return a conflict clause if a conflict was detected. */ @SuppressWarnings("unused") private Clause propagateInternal() { while (true) { Clause conflict = propagateTheories(); if (conflict != null) { return conflict; } final int level = mDecideStack.size(); // NOPMD conflict = propagateClauses(); if (conflict != null) { return conflict; } if (mDecideStack.size() > level) { continue; } long time = 0; if (Config.PROFILE_TIME) { time = System.nanoTime(); } for (final ITheory t: mTheories) { conflict = t.checkpoint(); if (conflict != null) { return conflict; } } if (Config.PROFILE_TIME) { mCheckTime += System.nanoTime() - time; } conflict = propagateTheories(); assert !Config.EXPENSIVE_ASSERTS || checkDecideLevel(); if (conflict != null) { return conflict; } if (mDecideStack.size() == level) { return null; } } } private Clause propagateTheories() { while (true) { boolean changed = false; for (final ITheory t: mTheories) { Literal propLit = t.getPropagatedLiteral(); if (propLit != null) { do { if (propLit.mAtom.mDecideStatus == null) { mTProps++; if (propLit.mAtom.mExplanation == null) { propLit.mAtom.mExplanation = t; } final Clause conflict = setLiteral(propLit); if (conflict != null) { for (final Literal lit: conflict.mLiterals) { final DPLLAtom atom = lit.getAtom(); assert(atom.mDecideStatus == lit.negate()); } return conflict; } } else if (propLit.mAtom.mDecideStatus != propLit) { final Clause conflict = t.getUnitClause(propLit); return conflict; } propLit = t.getPropagatedLiteral(); } while (propLit != null); changed = true; } } if (!changed) { return null; } } } private Clause propagateClauses() { long time = 0; if (Config.PROFILE_TIME) { time = System.nanoTime() - mSetTime; } while (!mWatcherBackList.isEmpty()) { final int index = mWatcherBackList.mHeadIndex; Clause clause = mWatcherBackList.removeFirst(); /* check if clause was already removed */ if (clause.mNext == null) { // System.err.println("Found removed clause in progation: " + clause); continue; } final Literal[] lits = clause.mLiterals; final Literal myLit = lits[index]; final Literal status = myLit.getAtom().mDecideStatus; /* Special case unit clause: propagate or return as conflict clause */ if (lits.length == 1) { myLit.getAtom().mBacktrackWatchers.append(clause, index); if (status != myLit) { if (status == null) { /* Propagate the unit clause. */ myLit.getAtom().mExplanation = clause; mProps++; clause = setLiteral(myLit); } if (Config.PROFILE_TIME) { mPropClauseTime += System.nanoTime() - time - mSetTime; } return clause; } } else if (status == myLit.negate()) { mWatcherSetList.append(clause, index); } else { myLit.mWatchers.append(clause, index); } } //logger.info("new set: "+watcherSetList.size()); nextList: while (!mWatcherSetList.isEmpty()) { final int index = mWatcherSetList.getIndex(); Clause clause = mWatcherSetList.removeFirst(); /* check if clause was already removed */ if (clause.mNext == null) { continue nextList; } final Literal[] lits = clause.mLiterals; assert lits[index].getAtom().mDecideStatus == lits[index].negate(); assert(lits.length >= 2); final Literal otherLit = lits[1 - index]; final DPLLAtom otherAtom = otherLit.getAtom(); if (otherAtom.mDecideStatus == otherLit) { /* Other watcher is true, put ourself on * the backtrack watcher list. */ otherAtom.mBacktrackWatchers.append(clause, index); continue nextList; } for (int i = 2; i < lits.length; i++) { final Literal lit = lits[i]; final DPLLAtom atom = lit.getAtom(); final Literal status = atom.mDecideStatus; if (status != lit.negate()) { /* check if clause is too old to keep */ if (clause.mActivity < mClsScale * Config.CLAUSE_UNLEARN_ACTIVITY && status == null && clause.doCleanup(this)) { clause.removeFromList(); } else { /* watch this literal */ for (int j = i; j > 2; j--) { lits[j] = lits[j - 1]; } lits[2] = lits[index]; lits[index] = lit; lit.mWatchers.append(clause, index); } continue nextList; } } /* Now we haven't found another atom to watch. Hence we have * a unit clause or conflict clause. */ // put the entry back into the watch list of the literal. Until // the literal changes again, the watcher is not needed anymore. lits[index].mWatchers.append(clause, index); if (otherAtom.mDecideStatus == null) { /* Propagate the unit clause. */ otherAtom.mExplanation = clause; mProps++; clause = setLiteral(otherLit); if (clause == null) { clause = propagateTheories(); } } /* Conflict clause */ if (Config.PROFILE_TIME) { mPropClauseTime += System.nanoTime() - time - mSetTime; } return clause;// NOPMD } if (Config.PROFILE_TIME) { mPropClauseTime += System.nanoTime() - time - mSetTime; } return null; } private boolean checkConflict(Clause conflict) { for (final Literal lit: conflict.mLiterals) { final DPLLAtom a = lit.getAtom(); assert(a.mDecideStatus == lit.negate()); } return true; } /** * Sets a literal to true and tells all theories about it. The * literal must be undecided when this function is called. * @param literal the literal to set. * @return a conflict clause if a conflict was detected. */ @SuppressWarnings("unused") private Clause setLiteral(Literal literal) { if (mLogger.isDebugEnabled()) { mLogger.debug("S " + literal); } final DPLLAtom atom = literal.getAtom(); assert (atom.mDecideStatus == null); assert mAtoms.contains(atom); atom.mStackPosition = mDecideStack.size(); mDecideStack.add(literal); atom.mDecideLevel = mCurrentDecideLevel; atom.mDecideStatus = literal; atom.mLastStatus = atom.mDecideStatus; mAtoms.remove(atom); assert !Config.EXPENSIVE_ASSERTS || checkDecideLevel(); mWatcherSetList.moveAll(literal.negate().mWatchers); long time; if (Config.PROFILE_TIME) { time = System.nanoTime(); } Clause conflict = null; if (mCurrentDecideLevel == 0) { /* This atom is now decided once and for all. */ mNumSolvedAtoms++; generateLevel0Proof(literal); } for (final ITheory t: mTheories) { conflict = t.setLiteral(literal); if (conflict != null) { assert checkConflict(conflict); break; } } if (Config.PROFILE_TIME) { mSetTime += System.nanoTime() - time; } return conflict; } public void watchClause(Clause clause) { if (clause.getSize() <= 1) { if (clause.getSize() == 0) { if (mUnsatClause == null) { mUnsatClause = clause; } } else { // Make sure the unit literal is actually a unit if (clause.getLiteral(0).getAtom().isAssumption()) { // CHECK do we learn trivially satisfied clauses? if (clause.getLiteral(0) != clause.getLiteral(0).getAtom().getDecideStatus()) { mUnsatClause = clause; return; } } /* propagate unit clause: only register watcher zero. */ mWatcherBackList.append(clause, 0); } } else { // Move unset literals upfront int dest = 0; while (dest < 2) { if (clause.getLiteral(dest).getAtom().isAssumption()) { boolean found = false; for (int i = dest + 1; i < clause.getSize(); ++i) { if (clause.getLiteral(dest).getAtom().isAssumption()) { // swap literal @dest with literal @i final Literal tmp = clause.mLiterals[i]; clause.mLiterals[i] = clause.mLiterals[dest]; clause.mLiterals[dest] = tmp; found = true; break; } } if (found) { dest++; } else if (dest == 0) { // We only found assumptions in this clause. mUnsatClause = clause; return; } else { assert dest == 1; // We found a unit clause /* propagate unit clause: only register watcher zero. */ mWatcherBackList.append(clause, 0); return; } } else { dest++; } } /* A clause is "watched" if it appears on either the * watcherBack/SetList or the watchers list of some atom. */ mWatcherBackList.append(clause, 0); mWatcherBackList.append(clause, 1); } } public void addClause(Clause clause) { mAtomScale += 1.0 - (1.0 / Config.ATOM_ACTIVITY_FACTOR); clause.mActivity = Double.POSITIVE_INFINITY; mNumAxiomClauses++; assert (clause.mStacklevel == mStacklevel); mClauses.prepend(clause); watchClause(clause); } void removeClause(Clause c) { c.removeFromList(); } public void addFormulaClause(Literal[] literals, ProofNode proof) { addFormulaClause(literals, proof, null); } public void addFormulaClause( Literal[] literals, ProofNode proof, ClauseDeletionHook hook) { final Clause clause = new Clause(literals, mStacklevel); clause.setDeletionHook(hook); addClause(clause); if (isProofGenerationEnabled()) { clause.setProof(proof); } mLogger.trace("Added clause %s", clause); } public void learnClause(Clause clause) { mAtomScale += 1.0 - (1.0 / Config.ATOM_ACTIVITY_FACTOR); mNumClauses++; clause.mActivity = mClsScale;//Double.POSITIVE_INFINITY; if (clause.getSize() <= 2) { clause.mActivity = Double.POSITIVE_INFINITY; } mLearnedClauses.append(clause); watchClause(clause); } // public void addInstantiationClause(Literal[] lits) { // ++num_insts; // Clause clause = new Clause(lits); // addClause(clause); // if (isProofGenerationEnabled()) { // // FIXME: This should somehow be related to the instantiation // LeafNode ln = new LeafNode(LeafNode.QUANT_INST, null, false); // clause.setProof(ln); // } // } private boolean checkDecideLevel() { int decision = 0; int i = 0; for (final Literal lit : mDecideStack) { if (lit.getAtom().mExplanation == null) { decision++; } assert lit.getAtom().mStackPosition == i; assert lit.getAtom().mDecideLevel == decision; i++; } return decision == mCurrentDecideLevel; } private int countLitsOnDecideLevel(Set<Literal> conflict) { int numLits = 0; int stackPtr = mDecideStack.size(); while (true) { final Literal lit = mDecideStack.get(--stackPtr); assert(!conflict.contains(lit.negate())); if (conflict.contains(lit)) { numLits++; } if (lit.getAtom().mExplanation == null) { return numLits; } } } /** * Explain one conflict clause. DO NOT CALL THIS FUNCTION DIRECTLY!!! * USE {@link #explain(Clause)} INSTEAD SINCE THIS FUNCTION DOES A CORRECT * LOOP INCLUDING {@link #finalizeBacktrack()} AND HENCE DOES NOT LEAVE * BEHIND INCONSISTENT THEORY SOLVERS. * @param clause Conflict clause * @return Explanation */ private Clause explainConflict(Clause clause) { if (mLogger.isDebugEnabled()) { mLogger.debug("explain conflict " + clause); } List<Antecedent> antecedents = null; HashSet<Literal> level0Ants = null; if (isProofGenerationEnabled()) { antecedents = new ArrayList<Antecedent>(); level0Ants = new HashSet<Literal>(); } int expstacklevel = clause.mStacklevel; mConflicts++; assert checkDecideLevel(); mAtomScale *= Config.ATOM_ACTIVITY_FACTOR; mClsScale *= Config.CLS_ACTIVITY_FACTOR; final Set<Literal> conflict = new CuckooHashSet<Literal>(); int maxDecideLevel = 1; int numLitsOnMaxDecideLevel = 0; int numAssumptions = 0; for (final Literal lit: clause.mLiterals) { final DPLLAtom atom = lit.getAtom(); assert(atom.mDecideStatus == lit.negate()); if (atom.mDecideLevel > 0) { if (atom.isAssumption()) { ++numAssumptions; } else if (atom.mDecideLevel >= maxDecideLevel) { if (atom.mDecideLevel > maxDecideLevel) { maxDecideLevel = atom.mDecideLevel; numLitsOnMaxDecideLevel = 1; } else { numLitsOnMaxDecideLevel++; } } conflict.add(lit.negate()); } else { expstacklevel = level0resolve(lit, level0Ants, expstacklevel); } atom.mActivity += mAtomScale; } if (mLogger.isDebugEnabled()) { mLogger.debug("removing level0: " + conflict); } if (conflict.size() == numAssumptions) { /* Unsatisfiable */ final Literal[] newlits = new Literal[conflict.size()]; int i = 0; for (final Literal l : conflict) { newlits[i++] = l.negate(); } assert i == newlits.length; final Clause resolution = new Clause(newlits, expstacklevel); if (isProofGenerationEnabled()) { for (final Literal l0: level0Ants) { antecedents.add(new Antecedent(l0, getLevel0(l0))); } if (antecedents.isEmpty()) { // TODO: only one clause object needed here. resolution.setProof(clause.getProof()); } else { final Antecedent[] ants = antecedents.toArray( new Antecedent[antecedents.size()]); resolution.setProof(new ResolutionNode(clause, ants)); } } // Remember unsat clause (which might not be empty, by conflicting // against assumptions) mUnsatClause = resolution; return resolution; } assert numLitsOnMaxDecideLevel >= 1; while (mCurrentDecideLevel > maxDecideLevel) { final Literal lit = mDecideStack.remove(mDecideStack.size() - 1); assert(!conflict.contains(lit.negate())); assert !conflict.contains(lit); if (lit.getAtom().mExplanation == null) { decreaseDecideLevel(); } assert checkDecideLevel(); backtrackLiteral(lit); assert checkDecideLevel(); } while (numLitsOnMaxDecideLevel > 1) { assert checkDecideLevel(); assert mCurrentDecideLevel == maxDecideLevel; assert countLitsOnDecideLevel(conflict) == numLitsOnMaxDecideLevel; assert checkDecideLevel(); final Literal lit = mDecideStack.get(mDecideStack.size() - 1); assert(!conflict.contains(lit.negate())); if (!conflict.contains(lit)) { assert lit.getAtom().mExplanation != null; assert checkDecideLevel(); mDecideStack.remove(mDecideStack.size() - 1); backtrackLiteral(lit); assert checkDecideLevel(); continue; } /* Do a resolution step with explanation */ final Clause expl = getExplanation(lit); expl.mActivity += mClsScale; // expl.usedTimes++; expstacklevel = Math.max(expstacklevel,expl.mStacklevel); if (isProofGenerationEnabled()) { antecedents.add(new Antecedent(lit,expl)); } mDecideStack.remove(mDecideStack.size() - 1); backtrackLiteral(lit); assert checkDecideLevel(); conflict.remove(lit); numLitsOnMaxDecideLevel--; final DPLLAtom atom = lit.getAtom(); if (mLogger.isDebugEnabled()) { mLogger.debug("Resolving with " + expl + " pivot = " + atom); } for (final Literal l : expl.mLiterals) { if (l != lit) { assert(l.getAtom().mDecideStatus == l.negate()); final int level = l.getAtom().mDecideLevel; if (l.getAtom().isAssumption() && conflict.add(l.negate())) { ++numAssumptions; } else if (level >= mBaseLevel) { if (conflict.add(l.negate()) && level == maxDecideLevel) { numLitsOnMaxDecideLevel++; } } else if (level == 0) { // Here, we do level0 resolution as well expstacklevel = level0resolve(l, level0Ants, expstacklevel); } l.getAtom().mActivity += mAtomScale; } } assert countLitsOnDecideLevel(conflict) == numLitsOnMaxDecideLevel; if (mLogger.isDebugEnabled()) { mLogger.debug("new conflict: " + conflict); } } assert mCurrentDecideLevel == maxDecideLevel; assert countLitsOnDecideLevel(conflict) == numLitsOnMaxDecideLevel; assert numLitsOnMaxDecideLevel == 1; while (mCurrentDecideLevel >= maxDecideLevel) { final Literal lit = mDecideStack.remove(mDecideStack.size() - 1); assert(!conflict.contains(lit.negate())); if (lit.getAtom().mExplanation == null) { decreaseDecideLevel(); } assert checkDecideLevel(); backtrackLiteral(lit); assert checkDecideLevel(); } /* We removed at least one decision point. * Try to backtrack further. */ if (Config.DEEP_BACKTRACK) { findBacktrackingPoint(conflict); } if (mLogger.isDebugEnabled()) { mLogger.debug("Backtrack to " + mDecideStack.size()); } final HashMap<Literal, Integer> redundancy = computeRedundancy(conflict); final Integer REDUNDANT = 1; int stackPtr = mDecideStack.size(); while (stackPtr > mNumSolvedAtoms) { final Literal lit = mDecideStack.get(--stackPtr); if (redundancy.get(lit) == REDUNDANT && conflict.contains(lit)) { /* Do a resolution step with explanation */ final Clause expl = getExplanation(lit); expl.mActivity += mClsScale; // expl.usedTimes++; expstacklevel = Math.max(expstacklevel,expl.mStacklevel); if (isProofGenerationEnabled()) { antecedents.add(new Antecedent(lit,expl)); } conflict.remove(lit); for (final Literal l : expl.mLiterals) { if (l != lit) { assert(l.getAtom().mDecideStatus == l.negate()); final int level = l.getAtom().mDecideLevel; if (l.getAtom().isAssumption() && conflict.add(l.negate())) { ++numAssumptions; } else if (level > mBaseLevel) { conflict.add(l.negate()); } else if (level == 0) { // Here, we do level0 resolution as well expstacklevel = level0resolve(l, level0Ants, expstacklevel); } l.getAtom().mActivity += mAtomScale; } } } } if (mLogger.isDebugEnabled()) { mLogger.debug("removing redundancy yields " + conflict); } final Literal[] newlits = new Literal[conflict.size()]; int i = 0; for (final Literal l : conflict) { newlits[i++] = l.negate(); } assert newlits[newlits.length - 1] != null; final Clause resolution = new Clause(newlits, expstacklevel); if (isProofGenerationEnabled()) { for (final Literal l0: level0Ants) { antecedents.add(new Antecedent(l0, getLevel0(l0))); } if (antecedents.isEmpty()) { // TODO: only one clause object needed here. resolution.setProof(clause.getProof()); } else { final Antecedent[] ants = antecedents.toArray( new Antecedent[antecedents.size()]); resolution.setProof(new ResolutionNode(clause, ants)); } } if (mLogger.isDebugEnabled()) { mLogger.debug("Resolved to " + resolution); } // If resolution size is number of literals we are unsat if (newlits.length == numAssumptions) { mUnsatClause = resolution; } return resolution; } /** * Explain all conflicts currently present in the solver starting with a * given initial conflict. Returns <code>true</code> if and only if the * empty clause has been derived. * @param conflict The initial conflict. * @return Is the solver inconsistent? */ private boolean explain(Clause conflict) { while (conflict != null) { conflict = explainConflict(conflict); learnClause(conflict); if (mUnsatClause != null) { return true; } conflict = finalizeBacktrack(); } return false; } private final int level0resolve(Literal l, Set<Literal> level0Ants, int sl) { final Clause l0 = getLevel0(l.negate()); if (isProofGenerationEnabled()) { level0Ants.add(l.negate()); } return (l0.mStacklevel > sl) ? l0.mStacklevel : sl; } private Clause getExplanation(Literal lit) { final Object explanation = lit.getAtom().mExplanation; if (explanation instanceof ITheory) { final Clause expl = ((ITheory) explanation).getUnitClause(lit); lit.getAtom().mExplanation = expl; assert checkUnitClause(expl,lit); assert checkDecideLevel(); return expl; } else { assert explanation == null || checkUnitClause((Clause)explanation,lit); return (Clause) explanation; } } private HashMap<Literal, Integer> computeRedundancy(Set<Literal> conflict) { final Integer REDUNDANT = 1; final Integer FAILED = 2; final Integer KEEP = 3;// NOCHECKSTYLE final HashMap<Literal,Integer> status = new HashMap<Literal, Integer>(); for (final Literal l : conflict) { if (l.getAtom().getDecideStatus() != null) { assert l.getAtom().getDecideStatus() == l; status.put(l, REDUNDANT); } } final ArrayDeque<Literal> todo = new ArrayDeque<Literal>(); final Iterator<Literal> it = conflict.iterator(); litloop: while (it.hasNext()) { final Literal lit = it.next(); if (lit.getAtom().getDecideStatus() == null) { continue; } todo.addFirst(lit); todoloop: while (!todo.isEmpty()) { final Literal next = todo.getFirst(); assert next.getAtom().getDecideStatus() == next; final Clause expl = getExplanation(next); //logger.info("check "+next+" expl "+expl); if (expl == null) { while (todo.size() > 1) { status.put(todo.removeFirst(), FAILED); } status.put(todo.removeFirst(), KEEP); continue litloop; } for (final Literal l : expl.mLiterals) { assert l.getAtom().getDecideStatus() != null; if (l != next && l.getAtom().getDecideLevel() > mBaseLevel) { final Literal lneg = l.negate(); assert lneg.getAtom().getDecideStatus() == lneg; final Integer st = status.get(lneg); if (st == FAILED) { while (todo.size() > 1) { status.put(todo.removeFirst(), FAILED); } status.put(todo.removeFirst(), KEEP); continue litloop; } else if (st == null) { todo.addFirst(lneg); continue todoloop; } } } status.put(todo.removeFirst(), REDUNDANT); } } return status; } private boolean checkUnitClause(Clause unit, Literal lit) { boolean found = false; for (final Literal l : unit.mLiterals) { assert l != lit.negate() : "Negation of unit literal in explanation"; if (l == lit) { found = true; } else { assert l.mAtom.mDecideStatus == l.negate() && l.mAtom.getStackPosition() < lit.getAtom().getStackPosition() :// NOCHECKSTYLE "Not a unit clause: " + l + " in " + unit; } } assert found : "Unit literal not in explanation"; return found; } private Clause finalizeBacktrack() { mWatcherBackList.moveAll(mWatcherSetList); for (final ITheory t: mTheories) { final Clause conflict = t.backtrackComplete(); if (conflict != null) { return conflict; } } return null; } private void findBacktrackingPoint(Set<Literal> conflict) { int i = mDecideStack.size(); while (i > 0) { final Literal lit = mDecideStack.get(--i); if (conflict.contains(lit)) { break; } if (lit.getAtom().mExplanation == null) { while (mDecideStack.size() > i) { backtrackLiteral( mDecideStack.remove(mDecideStack.size() - 1)); } decreaseDecideLevel(); } } } private void backtrackLiteral(Literal literal) { long time; if (mLogger.isDebugEnabled()) { mLogger.debug("B " + literal); } final DPLLAtom atom = literal.getAtom(); mWatcherBackList.moveAll(atom.mBacktrackWatchers); atom.mExplanation = null; atom.mDecideStatus = null; atom.mDecideLevel = -1; atom.mStackPosition = -1; mAtoms.add(atom); if (Config.PROFILE_TIME) { time = System.nanoTime(); } for (final ITheory t2: mTheories) { t2.backtrackLiteral(literal); } if (Config.PROFILE_TIME) { mBacktrackTime += System.nanoTime() - time; } } private Clause checkConsistency() { long time = 0; if (Config.PROFILE_TIME) { time = System.nanoTime(); } for (final ITheory t: mTheories) { final Clause conflict = t.computeConflictClause(); if (conflict != null) { return conflict; } } if (Config.PROFILE_TIME) { mCheckTime += System.nanoTime() - time; } return null; } private Literal chooseLiteral() { final Literal lit = suggestions(); if (lit != null) { return lit; } DPLLAtom atom; // int ran = mRandom.nextInt(Config.RANDOM_SPLIT_BASE); // if (!mAtoms.isEmpty() && ran <= Config.RANDOM_SPLIT_FREQ) { // atom = mAtoms.mAtoms[mRandom.nextInt(mAtoms.size())]; // ++mNumRandomSplits; // } else atom = mAtoms.peek(); if (atom == null) { return null; } assert atom.mDecideStatus == null; //logger.debug("Choose literal: "+atom+" Weight " // + (atom.activity/factor) +" - last: " + atom.lastStatus); // return atom.lastStatus == null ? atom.negate() : atom.lastStatus; return atom.getPreferredStatus(); } private static final int luby_super(int i) { int power; assert i > 0; /* let 2^k be the least power of 2 >= (i+1) */ power = 2; while (power < (i + 1)) { power *= 2; } if (power == (i + 1)) { return power / 2; } return luby_super(i - (power / 2) + 1); } private void printStatistics() { if (mLogger.isInfoEnabled()) { mLogger.info("Confl: " + mConflicts + " Props: " + mProps + " Tprops: " + mTProps + " Decides: " + mDecides + " RSplits: " + mNumRandomSplits); if (Config.PROFILE_TIME) { mLogger.info("Times: Expl: " + (mExplainTime / 1000 / 1000.0)// NOCHECKSTYLE + " Prop: " + (mPropTime / 1000 / 1000.0)// NOCHECKSTYLE + " PropClause: " + (mPropClauseTime / 1000 / 1000.0)// NOCHECKSTYLE + " Set: " + (mSetTime / 1000 / 1000.0)// NOCHECKSTYLE + " Check: " + (mCheckTime / 1000 / 1000.0)// NOCHECKSTYLE + " Back: " + (mBacktrackTime / 1000 / 1000.0));// NOCHECKSTYLE } mLogger.info("Atoms: " + mNumSolvedAtoms + "/" + (mAtoms.size() + mDecideStack.size()) + " Clauses: " + mNumClauses + " Axioms: " + mNumAxiomClauses); for (final ITheory t: mTheories) { t.printStatistics(mLogger); } } } /** * Solves the current problem. * @return true if sat, false if unsat. */ public boolean solve() { mHasModel = false; if (mUnsatClause != null) { mLogger.debug("Using cached unsatisfiability"); return false; } try { if (Config.INITIAL_PHASE_BIAS_JW) { // Compute for all remaining atoms an initial polarity according // to Jeruslaw Wang final Map<Literal, Double> scores = new HashMap<Literal, Double>(); clause_loop: for (final Clause c : mClauses) { double inc = 1.0; for (final Literal lit : c.mLiterals) { final Literal ds = lit.getAtom().getDecideStatus(); if (ds == lit) { // clause is satisfied continue clause_loop; } if (ds != lit.negate()) { inc /= 2.0; } } // Here, clause is not satisfied for (final Literal lit : c.mLiterals) { final Literal ds = lit.getAtom().getDecideStatus(); if (ds != lit.negate()) { final Double score = scores.get(lit); if (score == null) { scores.put(lit, inc); } else { scores.put(lit, score + inc); } } } } for (final DPLLAtom atom : mAtoms) { final Double pscore = scores.get(atom); final Double nscore = scores.get(atom.negate()); final double Pscore = pscore == null ? 0 : pscore; final double Nscore = nscore == null ? 0 : nscore; atom.setPreferredStatus( Pscore > Nscore ? atom : atom.negate()); } } long lastTime; if (Config.PROFILE_TIME) { lastTime = System.nanoTime() - mSetTime - mBacktrackTime; } for (final ITheory t : mTheories) { final Clause conflict = t.startCheck(); if (explain(conflict)) { return false; } } int iteration = 1; int nextRestart = Config.RESTART_FACTOR; long time; while (!isTerminationRequested()) { Clause conflict; do { conflict = propagateInternal(); if (conflict != null || isTerminationRequested()) { break; } if (Config.PROFILE_TIME) { time = System.nanoTime(); mPropTime += time - lastTime - mSetTime - mBacktrackTime; lastTime = time - mSetTime - mBacktrackTime; } final Literal literal = chooseLiteral(); if (literal == null) { conflict = checkConsistency(); if (conflict == null) { Literal lit; boolean suggested = false; while (conflict != null && (lit = suggestions()) != null) { // NOPMD if (lit.getAtom().mExplanation == null) { increaseDecideLevel(); mDecides++; } conflict = setLiteral(lit); suggested = true; } //@assert conflict != null ==> suggested == true if (!suggested && mWatcherBackList.isEmpty() && mAtoms.isEmpty()) { /* We found a model */ if (mLogger.isInfoEnabled()) { printStatistics(); mLogger.info("Hooray, we found a model:"); for (final ITheory t: mTheories) { t.dumpModel(mLogger); } if (mLogger.isTraceEnabled()) { for (final Literal dlit : mDecideStack) { mLogger.trace("%d: %s", dlit.hashCode(), dlit); } } } mHasModel = true; return true; } } } else { increaseDecideLevel(); mDecides++; conflict = setLiteral(literal); } } while (conflict == null && !isTerminationRequested()); if (Config.PROFILE_TIME) { time = System.nanoTime(); mPropTime += time - lastTime - mSetTime - mBacktrackTime; lastTime = time - mSetTime - mBacktrackTime; } if (explain(conflict)) { if (Config.PROFILE_TIME) { time = System.nanoTime(); mExplainTime += time - lastTime - mSetTime - mBacktrackTime; lastTime = time - mSetTime - mBacktrackTime; } printStatistics(); mLogger.info("Formula is unsat"); /* logger.info("Learned Clauses"); for (Clause c : learnedClauses) { logger.info("Cl: len "+c.literals.length+ " used "+c.usedTimes + ": "+c); } */ return false; } if (Config.PROFILE_TIME) { time = System.nanoTime(); mExplainTime += time - lastTime - mSetTime - mBacktrackTime; lastTime = time - mSetTime - mBacktrackTime; } if (mAtomScale > Config.LIMIT) { for (final DPLLAtom a : mAtoms) { a.mActivity *= Double.MIN_NORMAL; } for (final Literal l : mDecideStack) { l.getAtom().mActivity *= Double.MIN_NORMAL; } mAtomScale *= Double.MIN_NORMAL; } if (mClsScale > Config.LIMIT) { final Iterator<Clause> it = mLearnedClauses.iterator(); while (it.hasNext()) { final Clause c = it.next(); c.mActivity *= Double.MIN_NORMAL; } mClsScale *= Double.MIN_NORMAL; } if (--nextRestart == 0) { final DPLLAtom next = mAtoms.peek(); int restartpos = -1; for (int i = mNumSolvedAtoms + mBaseLevel; i < mDecideStack.size(); ++i) { final DPLLAtom var = mDecideStack.get(i).getAtom(); if (var.mExplanation == null && var.mActivity < next.mActivity) { // This has been a decision restartpos = i; break; } } int decleveldec = 0; if (restartpos != -1) { while (mDecideStack.size() > restartpos) { final Literal lit = mDecideStack.remove( mDecideStack.size() - 1); assert(lit.getAtom().mDecideLevel != mBaseLevel); final Object litexpl = lit.getAtom().mExplanation; if (litexpl == null) { ++decleveldec; } if (litexpl instanceof Clause) { ((Clause) litexpl).mActivity += mClsScale; // ((Clause) litexpl).usedTimes++; } backtrackLiteral(lit); } } unlearnClauses(mStacklevel); conflict = finalizeBacktrack(); assert (conflict == null); mCurrentDecideLevel -= decleveldec; iteration++; for (final ITheory t : mTheories) { t.restart(iteration); } nextRestart = Config.RESTART_FACTOR * luby_super(iteration); if (Config.PRINT_STATISTICS) { mLogger.info("Restart"); printStatistics(); } } if (Config.PROFILE_TIME) { lastTime = System.nanoTime() - mSetTime - mBacktrackTime; } } return true; } catch (final RuntimeException eUnknown) { if (System.getProperty("smtinterpol.ddfriendly") != null) { System.exit(3); } throw eUnknown; } finally { for (final ITheory t : mTheories) { t.endCheck(); } } } private final void unlearnClauses(int targetstacklevel) { final Iterator<Clause> it = mLearnedClauses.iterator(); while (it.hasNext()) { final Clause c = it.next(); if (c.mActivity < mClsScale * Config.CLAUSE_UNLEARN_ACTIVITY || c.mStacklevel > targetstacklevel && c.doCleanup(this)) { mNumClauses--; it.remove(); } } } private Literal suggestions() { for (final ITheory t : mTheories) { final Literal lit = t.getPropagatedLiteral(); if (lit != null) { lit.mAtom.mExplanation = t; assert(lit.getAtom().mDecideStatus == null); return lit; } } for (final ITheory t : mTheories) { final Literal lit = t.getSuggestion(); if (lit != null) { assert(lit.getAtom().mDecideStatus == null); return lit; } } return null; } public void addAtom(DPLLAtom atom) { mAtoms.add(atom); mPpStack.addAtom(atom); } public void removeAtom(DPLLAtom atom) { assert(atom.mDecideStatus == null); mAtoms.remove(atom); for (final ITheory t : mTheories) { t.removeAtom(atom); } } public void addTheory(ITheory t) { final ITheory[] newTheories = new ITheory[mTheories.length + 1]; System.arraycopy(mTheories, 0, newTheories, 0, mTheories.length); newTheories[mTheories.length] = t; mTheories = newTheories; } public void removeTheory() { final ITheory[] newTheories = new ITheory[mTheories.length - 1]; System.arraycopy(mTheories, 0, newTheories, 0, mTheories.length); mTheories = newTheories; } public Theory getSMTTheory() { return mSmtTheory; } public String dumpClauses() { final StringBuilder sb = new StringBuilder(); for (final Clause c : mClauses) { sb.append("(assert "); final Literal[] lits = c.mLiterals; if (lits.length == 1) { sb.append(lits[0].getSMTFormula(mSmtTheory)).append(")\n"); } else { sb.append("(or"); for (final Literal l : lits) { sb.append(' ').append(l.getSMTFormula(mSmtTheory)); } sb.append("))\n"); } } return sb.toString(); } public void setCompleteness(int ncompleteness) { mCompleteness = ncompleteness; } public int getCompleteness() { return mCompleteness; } public void provideCompleteness(int ncompleteness) { if (mCompleteness == COMPLETE) { mCompleteness = ncompleteness; } } public String getCompletenessReason() { return COMPLETENESS_STRINGS[mCompleteness]; } public void push() { mPpStack = mPpStack.save(this); if (mAssignments != null) { mAssignments.beginScope(); } ++mStacklevel; } public void pop(int numpops) { if (numpops < 1 || numpops > mStacklevel) { throw new IllegalArgumentException( "Must pop a positive number less than the current stack level"); } final int targetstacklevel = mStacklevel - numpops; if (mUnsatClause != null && mUnsatClause.mStacklevel > targetstacklevel) { mUnsatClause = null; } if (Config.EXPENSIVE_ASSERTS && !checkProofStackLevel(mUnsatClause, targetstacklevel)) { throw new AssertionError(); } if (!mDecideStack.isEmpty()) { final java.util.ListIterator<Literal> literals = mDecideStack.listIterator(mDecideStack.size()); while (literals.hasPrevious()) { final Literal lit = literals.previous(); final Object litexpl = lit.getAtom().mExplanation; if (litexpl instanceof Clause) { ((Clause) litexpl).mActivity += mClsScale; // ((Clause) litexpl).usedTimes++; } backtrackLiteral(lit); } mDecideStack.clear(); final Clause conflict = finalizeBacktrack(); assert (conflict == null); } unlearnClauses(targetstacklevel); mCurrentDecideLevel = 0; mNumSolvedAtoms = 0; final Iterator<Clause> inputit = mClauses.iterator(); while (inputit.hasNext()) { final Clause input = inputit.next(); if (input.mStacklevel > targetstacklevel) { if (input.doCleanup(this)) { inputit.remove(); } else { throw new InternalError( "Input clause still blocked, but invalid"); // logger.debug("Removed clause {0}", input); } } else { // Terminate iteration here since only clauses with lower // stacklevel remain. // logger.debug("Keeping input {0}", input); break; } } for (int i = 0; i < numpops; ++i) { mPpStack = mPpStack.restore(this, mStacklevel - i - 1); } mStacklevel = targetstacklevel; if (mAssignments != null) { for (int i = 0; i < numpops; ++i) { if (mAssignments.getActiveScopeNum() == 1) { break; } mAssignments.endScope(); } } } public LogProxy getLogger() { return mLogger; } public void showClauses(PrintWriter pw) { SimpleListable<Clause> c = mClauses.mNext; while (c != mClauses) { pw.println(c); c = c.mNext; } } public boolean hasModel() { return mHasModel && mCompleteness == COMPLETE; } public void setProofGeneration(boolean enablePG) { mPGenabled = enablePG; } public boolean isProofGenerationEnabled() { return mPGenabled; } public Literal[] getUnsatAssumptions() { if (mConflictingAssumption != null) { return new Literal[] {mConflictingAssumption, mConflictingAssumption.negate()}; } return mUnsatClause.mLiterals; } public Clause getProof() { assert checkValidUnsatClause(); Clause empty = mUnsatClause; if (mUnsatClause != null && mUnsatClause.getSize() > 0) { // We have to remove the assumptions via resolution final Antecedent[] antecedents = new Antecedent[mUnsatClause.getSize()]; for (int i = 0; i < mUnsatClause.getSize(); ++i) { final Literal lit = mUnsatClause.getLiteral(i).negate(); antecedents[i] = new Antecedent(lit, new Clause(new Literal[] {lit}, new LeafNode(LeafNode.ASSUMPTION, null))); } final ResolutionNode proof = new ResolutionNode(mUnsatClause, antecedents); empty = new Clause(new Literal[0], proof); } return empty; } private void generateLevel0Proof(Literal lit) { assert (lit.getAtom().mDecideLevel == mBaseLevel) : "Level0 proof for non-level0 literal?"; final Clause c = getExplanation(lit); if (c.getSize() > 1) { int stacklvl = c.mStacklevel; final Literal[] lits = c.mLiterals; Clause res; if (isProofGenerationEnabled()) { final Antecedent[] ants = new Antecedent[c.getSize() - 1]; int i = 0; for (final Literal l : lits) { if (l != lit) { final Clause lc = getLevel0(l.negate()); ants[i++] = new Antecedent(l.negate(), lc); stacklvl = Math.max(stacklvl, lc.mStacklevel); } } res = new Clause(new Literal[] {lit}, new ResolutionNode(c, ants), stacklvl); } else { for (final Literal l : lits) { if (l != lit) { final Clause lc = getLevel0(l.negate()); stacklvl = Math.max(stacklvl, lc.mStacklevel); } } res = new Clause(new Literal[] {lit}, stacklvl); } lit.getAtom().mExplanation = res; } } private Clause getLevel0(Literal lit) { assert(lit.getAtom().mDecideLevel == mBaseLevel); final Object expl = lit.getAtom().mExplanation; assert expl instanceof Clause && ((Clause)expl).getSize() == 1; assert ((Clause)expl).contains(lit); return (Clause)expl; } private final void increaseDecideLevel() { if (mLogger.isDebugEnabled()) { mLogger.debug("Decide@" + mDecideStack.size()); } mCurrentDecideLevel++; assert(mCurrentDecideLevel >= 0) : "Decidelevel negative"; for (final ITheory t : mTheories) { t.increasedDecideLevel(mCurrentDecideLevel); } } private final void decreaseDecideLevel() { mCurrentDecideLevel--; assert(mCurrentDecideLevel >= 0) : "Decidelevel negative"; for (final ITheory t : mTheories) { t.decreasedDecideLevel(mCurrentDecideLevel); } } public ITheory[] getAttachedTheories() { return mTheories; } public int getAssertionStackLevel() { return mStacklevel; } public boolean inconsistent() { return mUnsatClause != null; } private boolean checkProofStackLevel(Clause c, int targetlvl) { if (c == null || c.mProof == null) { return true; } if (c.mStacklevel > targetlvl) { System.err.println("Clause " + c + " above target level!"); return false; } for (final Literal lit : c.mLiterals) { if (lit.getAtom().mAssertionstacklevel > targetlvl) { System.err.println("Literal " + lit + " in clause " + c + " above target level"); return false; } } if (c.mProof instanceof ResolutionNode) { final ResolutionNode rn = (ResolutionNode) c.mProof; if (!checkProofStackLevel(rn.getPrimary(), targetlvl)) { return false; } for (final Antecedent ante : rn.getAntecedents()) { if (!checkProofStackLevel(ante.mAntecedent, targetlvl)) { return false; } } } return true; } public Object getStatistics() { // Don't crash the solver one stupid scripts... final Object[] res = mTheories == null ? new Object[1] : new Object[mTheories.length + 1]; final Object[] mystats = new Object[][] { {"Conflicts", mConflicts}, {"Propagations", mProps}, {"Theory_propagations", mTProps}, {"Decides", mDecides}, {"Random_splits", mNumRandomSplits}, {"Num_Atoms", mAtoms.size() + mDecideStack.size()}, {"Solved_Atoms", mNumSolvedAtoms}, {"Clauses", mNumClauses}, {"Axioms", mNumAxiomClauses}, {"Times", new Object[][]{ {"Explain", mExplainTime}, {"Propagation", mPropTime}, {"Set", mSetTime}, {"Check", mCheckTime}, {"Backtrack", mBacktrackTime}} } }; res[0] = new Object[]{":Core", mystats}; for (int i = 1; i < res.length; ++i) { res[i] = mTheories[i - 1].getStatistics(); } return res; } public void setProduceAssignments(boolean value) { final boolean old = mProduceAssignments; mProduceAssignments = value; if (old != mProduceAssignments) { if (old) { mAssignments = null; } else { mAssignments = new ScopedHashMap<String, Literal>(); } } } public boolean isProduceAssignments() { return mProduceAssignments; } public void trackAssignment(String label, Literal literal) { mAssignments.put(label, literal); } public Assignments getAssignments() { if (!mProduceAssignments) { return null; } final HashMap<String, Boolean> assignment = new HashMap<String, Boolean>(mAssignments.size(), 1.0f); for (final Map.Entry<String, Literal> me : mAssignments.entrySet()) { assignment.put(me.getKey(), me.getValue().getAtom().mDecideStatus == me.getValue()); } return new Assignments(assignment); } /** * Run a quick and incomplete check on the current context. This only uses * propagations and a conflict explanation to the empty clause. * @return <code>false</code> if and only if the empty clause could be * derived. */ public boolean quickCheck() { if (mUnsatClause != null) { return false; } final Clause conflict = propagateInternal(); final boolean res = !explain(conflict); return res; } /** * Propagate as much as possible. In contrast to {@link #quickCheck()}, * this function tells the theory solvers to start a check. This might get * more propagations than {@link #quickCheck()}. * @return <code>false</code> if and only if the empty clause could be * derived. */ public boolean propagate() { if (mUnsatClause != null) { return false; } Clause conflict = null; for (final ITheory t : mTheories) { conflict = t.startCheck(); if (conflict != null) { break; } } if (conflict == null) { conflict = propagateInternal(); } final boolean res = !explain(conflict); for (final ITheory t : mTheories) { t.endCheck(); } return res; } public Random getRandom() { return mRandom; } public void setRandomSeed(long seed) { mRandom.setSeed(seed); } public void flipDecisions() { while (mDecideStack.size() > mBaseLevel + mNumSolvedAtoms) { final Literal lit = mDecideStack.remove(mDecideStack.size() - 1); backtrackLiteral(lit); // Flip the decision lit.getAtom().mLastStatus = lit.negate(); } final Clause conflict = finalizeBacktrack(); assert (conflict == null); mCurrentDecideLevel = mBaseLevel; } public void flipNamedLiteral(String name) throws SMTLIBException { while (mDecideStack.size() > mBaseLevel + mNumSolvedAtoms) { final Literal lit = mDecideStack.remove(mDecideStack.size() - 1); backtrackLiteral(lit); } final Clause conflict = finalizeBacktrack(); assert (conflict == null); mCurrentDecideLevel = mBaseLevel; final Literal lit = mAssignments.get(name); if (lit == null) { throw new SMTLIBException("Name " + name + " not known"); } final DPLLAtom atom = lit.getAtom(); atom.mLastStatus = atom.mLastStatus == null ? atom : atom.mLastStatus.negate(); } /** * Returns the list of all input clauses. This list does not contain * any learned clauses! */ public SimpleList<Clause> getClauses() { return mClauses; } public Term[] getSatisfiedLiterals() { int size = 0; for (final Literal lit : mDecideStack) { if (!(lit.getAtom() instanceof NamedAtom)) { ++size; } } final Term[] res = new Term[size]; int i = -1; for (final Literal lit : mDecideStack) { if (!(lit.getAtom() instanceof NamedAtom)) { res[++i] = lit.getSMTFormula(mSmtTheory, true); } } return res; } public class AllSatIterator implements Iterator<Term[]> { private final Literal[] mPreds; private final Term[] mTerms; private Literal[] mBlocker; private int mBlockerSize; public AllSatIterator(Literal[] preds, Term[] terms) { mPreds = preds; mTerms = terms; assert (mPreds.length == mTerms.length); for (final Literal l : preds) { if (!(l.getAtom() instanceof TrueAtom)) { ++mBlockerSize; } } } @Override public boolean hasNext() { if (mBlocker != null) { final Clause conflict = new Clause(mBlocker, mStacklevel); if (explain(conflict)) { return false; } } if (solve() && hasModel()) { return true; } return false; } @Override public Term[] next() { final Term[] res = new Term[mPreds.length]; mBlocker = new Literal[mBlockerSize]; for (int i = 0; i < mPreds.length; ++i) { final Literal l = mPreds[i]; if (!(l.getAtom() instanceof TrueAtom)) { mBlocker[i] = l.getAtom().mDecideStatus.negate(); } res[i] = l.getAtom().mDecideStatus == l ? mTerms[i] : getSMTTheory().term("not", mTerms[i]); } return res; } @Override public void remove() { throw new UnsupportedOperationException("Cannot remove model!"); } } public boolean isTerminationRequested() { if (mCompleteness == INCOMPLETE_CANCELLED || mCancel.isTerminationRequested()) { mCompleteness = INCOMPLETE_CANCELLED; return true; } return false; } /** * Remove all assumptions. We backtrack to level 0. */ public void clearAssumptions() { mConflictingAssumption = null; mLogger.debug("Clearing Assumptions (Baselevel is %d)", mBaseLevel); if (mBaseLevel != 0) { Literal top = mDecideStack.get(mDecideStack.size() - 1); while (top.getAtom().getDecideLevel() != 0) { backtrackLiteral(top); mDecideStack.remove(mDecideStack.size() - 1); if (top.getAtom().isAssumption()) { top.getAtom().unassume(); } top = mDecideStack.get(mDecideStack.size() - 1); } final Clause res = finalizeBacktrack(); assert res == null : "Conflict when clearing assumptions!"; mBaseLevel = 0; mCurrentDecideLevel = 0; } if (mUnsatClause != null && mUnsatClause.getSize() != 0) { // delete unsat clause since it depends on assumptions mUnsatClause = null; } } /** * Add some literals and prepare for a check-sat. Trivial * inconsistencies between assumptions are detected. * @param lits The literals to assume. * @return <code>false</code> if the assumptions are trivially inconsistent. */ public boolean assume(Literal[] lits) { for (final Literal lit : lits) { mLogger.debug("Assuming Literal %s", lit); // First check if the literal is already set if (lit.getAtom().getDecideStatus() != null) { if (lit.getAtom().getDecideStatus() == lit) { mLogger.debug("Already set!"); continue; } else { // We have the assumption lit, but we know ~lit holds. // This can have two cases: // 1) We assumed ~lit in which case we have to build a // new clause to represent the proof using only // assumptions // 2) We derived the unit ~lit from assertions in which // case we can use the proof for ~lit if (lit.getAtom().isAssumption()) { mUnsatClause = new Clause(new Literal[] {lit.negate()}, new LeafNode(LeafNode.ASSUMPTION, null)); mLogger.debug("Conflicting assumptions"); mConflictingAssumption = lit; } else { assert lit.getAtom().getDecideLevel() == 0; assert lit.getAtom().mExplanation instanceof Clause; mUnsatClause = (Clause) lit.getAtom().mExplanation; mLogger.debug("Conflict against unit clause"); } assert checkValidUnsatClause(); mBaseLevel = mCurrentDecideLevel; return false; } } // The literal is not already set => assume it lit.getAtom().assume(); increaseDecideLevel(); final Clause conflict = setLiteral(lit); if (conflict != null) { mLogger.debug("Conflict when setting literal"); mBaseLevel = mCurrentDecideLevel; mUnsatClause = explainConflict(conflict); checkValidUnsatClause(); final Clause tmp = finalizeBacktrack(); assert tmp == null; return false; } } mLogger.debug("Setting base level to %d", mCurrentDecideLevel); mBaseLevel = mCurrentDecideLevel; return true; } private boolean checkValidUnsatClause() { if (mUnsatClause != null) { for (final Literal lit : mUnsatClause.mLiterals) { assert lit.getAtom().isAssumption() : "Not an assumption in unsat clause"; assert lit.getAtom().getDecideStatus() == lit.negate() : "unsat clause satisfied!"; } } return true; } }