/* * 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.convert; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import de.uni_freiburg.informatik.ultimate.logic.AnnotatedTerm; import de.uni_freiburg.informatik.ultimate.logic.Annotation; import de.uni_freiburg.informatik.ultimate.logic.ApplicationTerm; import de.uni_freiburg.informatik.ultimate.logic.FormulaUnLet; import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol; import de.uni_freiburg.informatik.ultimate.logic.Logics; import de.uni_freiburg.informatik.ultimate.logic.QuantifiedFormula; import de.uni_freiburg.informatik.ultimate.logic.Rational; 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.BooleanVarAtom; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Clause; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.ClauseDeletionHook; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.DPLLAtom; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.DPLLAtom.TrueAtom; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.DPLLEngine; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.IAnnotation; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Literal; import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.NamedAtom; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.IProofTracker; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.LeafNode; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.NoopProofTracker; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ProofConstants; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ProofNode; import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ProofTracker; 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.proof.SourceAnnotation; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.ArrayTheory; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CCAppTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CCTerm; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.cclosure.CClosure; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.LinArSolve; import de.uni_freiburg.informatik.ultimate.smtinterpol.theory.linar.MutableAffinTerm; import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedArrayList; import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedHashMap; /** * Utility to convert an arbitrary term into CNF and insert it into SMTInterpol. * @author Juergen Christ */ public class Clausifier { /** * Helper class to add the index-axiom for (store a i v), i.e., * (select (store a i v) i) = v. Additionally, this class creates an array * read (select a i). * @author Juergen Christ */ private final class AddStoreAxioms implements Operation { private final ApplicationTerm mStore; public AddStoreAxioms(ApplicationTerm store) { mStore = store; } @Override public void perform() { final IProofTracker sub = mTracker.getDescendent(); final Term i = mStore.getParameters()[1]; final Term v = mStore.getParameters()[2]; final Term selstore = mTheory.term("select", mStore, i); final EqualityProxy ep = createEqualityProxy( getSharedTerm(selstore), getSharedTerm(v)); final Literal lit = ep.getLiteral(); final Term prf = sub.auxAxiom( ProofConstants.AUX_ARRAY_STORE, null, mStore, null, null); addClause(new Literal[] {lit}, null, getProofNewSource( ProofConstants.AUX_ARRAY_STORE, sub.clause(prf))); if (Config.ARRAY_ALWAYS_ADD_READ // HACK: We meen "finite sorts" || v.getSort() == mTheory.getBooleanSort()) { final Term a = mStore.getParameters()[0]; final Term sel = mTheory.term("select", a, i); // Simply create the CCTerm getSharedTerm(sel); } } } /** * Helper class to insert instantiations of the array diff extensionality * axiom into the clause set. * @author Juergen Christ */ private final class AddDiffAxiom implements Operation { private final ApplicationTerm mDiff; public AddDiffAxiom(ApplicationTerm diff) { mDiff = diff; } @Override public void perform() { // Create a = b \/ select(a, diff(a,b)) != select(b, diff(a,b)) final Term a = mDiff.getParameters()[0]; final Term b = mDiff.getParameters()[1]; final SharedTerm sharedA = getSharedTerm(a); final SharedTerm sharedB = getSharedTerm(b); final EqualityProxy eparray = createEqualityProxy(sharedA, sharedB); if (eparray == EqualityProxy.getTrueProxy()) { // Someone wrote (@diff a a)... return; } final IProofTracker sub = mTracker.getDescendent(); final Theory t = mDiff.getTheory(); final Term selecta = t.term("select", a, mDiff); final Term selectb = t.term("select", b, mDiff); final SharedTerm sharedSelectA = getSharedTerm(selecta); final SharedTerm sharedSelectB = getSharedTerm(selectb); final EqualityProxy epselect = createEqualityProxy( sharedSelectA, sharedSelectB); final Literal[] lits = { eparray.getLiteral(), epselect.getLiteral().negate() }; final Term prf = sub.auxAxiom( ProofConstants.AUX_ARRAY_DIFF, null, mDiff, null, null); addClause(lits, null, getProofNewSource( ProofConstants.AUX_ARRAY_DIFF, sub.clause(prf))); } } public class CCTermBuilder { private class BuildCCTerm implements Operation { private Term mTerm; public BuildCCTerm(Term term) { mTerm = term; } @Override public void perform() { final SharedTerm shared = getSharedTerm(mTerm, true); if (shared.mCCterm == null) { final FunctionSymbol fs = getSymbol(); if (fs == null) { // We have an intern function symbol final CCTerm res = mCClosure.createAnonTerm(shared); shared.setCCTerm(res); mConverted.push(res); if (mTerm.getSort().isArraySort()) { mArrayTheory.notifyArray(res, false); } } else { mOps.push(new SaveCCTerm(shared)); final ApplicationTerm at = (ApplicationTerm) mTerm; final Term[] args = at.getParameters(); for (int i = args.length - 1; i >= 0; --i) { mOps.push(new BuildCCAppTerm(i != args.length - 1)); mOps.push(new BuildCCTerm(args[i])); } mConverted.push(mCClosure.getFuncTerm(fs)); } } else { mConverted.push(shared.mCCterm); } } private FunctionSymbol getSymbol() { if (mTerm instanceof SMTAffineTerm) { mTerm = ((SMTAffineTerm) mTerm).internalize(mCompiler); } if (mTerm instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) mTerm; final FunctionSymbol fs = at.getFunction(); // Don't descend into interpreted function symbols unless // it is a select or store if (Clausifier.needCCTerm(fs)) { return fs; } } return null; } } private class SaveCCTerm implements Operation { private final SharedTerm mShared; public SaveCCTerm(SharedTerm shared) { mShared = shared; } @Override public void perform() { mShared.setCCTerm(mConverted.peek()); mCClosure.addTerm(mShared.mCCterm, mShared); final Term t = mShared.getTerm(); if (t.getSort().isArraySort()) { final ApplicationTerm at = (ApplicationTerm) t; mArrayTheory.notifyArray(mShared.mCCterm, at.getFunction().getName().equals("store")); } if (t instanceof ApplicationTerm && ((ApplicationTerm) t).getFunction().getName().equals( "@diff")) { mArrayTheory.notifyDiff((CCAppTerm) mShared.mCCterm); } } } /** * Helper class to build the intermediate CCAppTerms. Note that all * these terms will be func terms. * @author Juergen Christ */ private class BuildCCAppTerm implements Operation { private final boolean mIsFunc; public BuildCCAppTerm(boolean isFunc) { mIsFunc = isFunc; } @Override public void perform() { final CCTerm arg = mConverted.pop(); final CCTerm func = mConverted.pop(); mConverted.push(mCClosure.createAppTerm(mIsFunc, func, arg)); } } private final ArrayDeque<Operation> mOps = new ArrayDeque<Operation>(); private final ArrayDeque<CCTerm> mConverted = new ArrayDeque<CCTerm>(); public CCTermBuilder() { if (mCClosure == null) { // Delayed setup for @div0, @mod0, and @/0 setupCClosure(); } } public CCTerm convert(Term t) { mOps.push(new BuildCCTerm(t)); while (!mOps.isEmpty()) { mOps.pop().perform(); } final CCTerm res = mConverted.pop(); assert mConverted.isEmpty(); return res; } } /** * Basic interface used to undo certain events related to the assertion * stack. * * Due to our instantiation mechanism, trail objects should only be used to * undo changes related to push/pop and instantiations at the same time. * @author Juergen Christ */ static abstract class TrailObject { private TrailObject mPrev; protected TrailObject() { mPrev = this; } protected TrailObject(TrailObject prev) { mPrev = prev; } /** * Undo an action performed since the corresponding push. */ public abstract void undo(); public TrailObject getPrevious() { return mPrev; } void setPrevious(TrailObject prev) { mPrev = prev; } /** * Is this the end of the scope. * @return <code>true</code> if this object represents the end of a * scope. */ public boolean isScopeMarker() { // NOPMD return false; } } /** * Helper class to remove a theory added at some higher scope level. This * should be used with care. It's mainly intended to remove the cclosure * that was added because of a @/0, @div0, or @mod0 function symbol in pure * linear arithmetic. * * It is safe to do this as a trail object since we need a cclosure for all * quantified theories. * @author Juergen Christ */ private class RemoveTheory extends TrailObject { public RemoveTheory(TrailObject prev) { super(prev); } @Override public void undo() { mEngine.removeTheory(); } } /** * Mark the begin/end of a scope on the assertion stack. * @author Juergen Christ */ private class ScopeMarker extends TrailObject { public ScopeMarker(TrailObject prev) { super(prev); } @Override public void undo() { // Nothing to do here } @Override public boolean isScopeMarker() { return true; } } private class RemoveClausifierInfo extends TrailObject { private final Term mTerm; public RemoveClausifierInfo(TrailObject prev, Term term) { super(prev); mTerm = term; } @Override public void undo() { mClauseData.remove(mTerm); } } private class RemoveFlag extends TrailObject { private final ClausifierInfo mCi; private final int mFlag; public RemoveFlag(TrailObject prev, ClausifierInfo ci, int flag) { super(prev); mCi = ci; mFlag = flag; } @Override public void undo() { mCi.clearFlag(mFlag); } } private class RemoveLiteral extends TrailObject { private final ClausifierInfo mCi; public RemoveLiteral(TrailObject prev, ClausifierInfo ci) { super(prev); mCi = ci; } @Override public void undo() { mCi.clearLiteral(); } } private class RemoveAxiomProof extends TrailObject { private final ClausifierInfo mCi; public RemoveAxiomProof(TrailObject prev, ClausifierInfo ci) { super(prev); mCi = ci; } @Override public void undo() { mCi.clearAxiomProof(); } } private class RemoveAtom extends TrailObject { private final Term mTerm; public RemoveAtom(TrailObject prev, Term term) { super(prev); mTerm = term; } @Override public void undo() { mLiteralData.remove(mTerm); } } /** * A helper class to remember whether a formula has been added as axioms or * the corresponding aux axioms have been added. This info also contains a * field to mark the aux axioms as blocked. We use this to prevent deletion * of the aux axioms if the corresponding literal has been used to simplify * clausification, i.e., we did not convert a top-level formula as axiom, * but added the unit clause containing only the proxy literal. For * quantifier-free logics, this feature is unused since we do not run into * problems with the assertion stack management. If however a proxy was * created as a result of a quantifier instantiation, the instantiation * survives a push and an assertion on the next stacklevel triggers a * simplification where we only use the proxy, we have to block all * clauses defining the auxiliary literal. This will prevent the deletion * of the quantifier instantiation which however is a top-level assertion * now. * @author Juergen Christ */ private static class ClausifierInfo { private static class ProofData { private final Term mTerm; private final Term mAxiomProof; private final IAnnotation mAnnot; private final boolean mNegated; public ProofData(Term term, Term axiomProof, IAnnotation annot, boolean neg) { mTerm = term; mAxiomProof = axiomProof; mAnnot = annot; mNegated = neg; } } static final int POS_AXIOMS_ADDED = 1; static final int NEG_AXIOMS_ADDED = 2; static final int POS_AUX_AXIOMS_ADDED = 4; static final int NEG_AUX_AXIOMS_ADDED = 8; private Literal mLit; private int mFlags; private Object mProof; public ClausifierInfo() { mLit = null; mFlags = 0; } public void setFlag(int flag) { mFlags |= flag; } public void clearFlag(int flag) { mFlags &= ~flag; } public boolean testFlags(int flag) { return (mFlags & flag) != 0; } public Literal getLiteral() { return mLit; } public void setLiteral(Literal lit) { mLit = lit; } public void clearLiteral() { mLit = null; } public void setAxiomProof( Term term, Term proof, IAnnotation annot, boolean negated) { if (proof == null) { mProof = annot; } else { mProof = new ProofData(term, proof, annot, negated); } } /// Debugging only boolean isAxiomProofAvailable() { return mProof != null; } public ProofNode getAxiomProof( IProofTracker tracker, Term idx, Literal lit) { if (mProof instanceof IAnnotation) { return new LeafNode(LeafNode.NO_THEORY, (IAnnotation) mProof); } final ProofData pd = (ProofData) mProof; final Theory t = pd.mTerm.getTheory(); final IProofTracker sub = tracker.getDescendent(); final Term unquoted = pd.mTerm; if (pd.mNegated && testFlags(ClausifierInfo.POS_AXIOMS_ADDED)) { sub.negation(t.term(t.mNot, unquoted), unquoted, ProofConstants.RW_NOT_SIMP); } sub.intern(idx, lit); return new LeafNode(LeafNode.NO_THEORY, new SourceAnnotation((SourceAnnotation) pd.mAnnot, sub.clause(pd.mAxiomProof))); } public void clearAxiomProof() { mProof = null; } } private interface Operation { public void perform(); } private class AddAsAxiom implements Operation { /** * The proof so far. */ private final Term mProofTerm; /** * The term to add as axiom. */ private final Term mTerm; /** * The polarity (true means negated). */ private final boolean mNegated; public AddAsAxiom(Term term, Term proofTerm) { this(term, false, proofTerm); } public AddAsAxiom(Term term, boolean negated, Term proofTerm) { mTerm = term; mNegated = negated; mProofTerm = proofTerm; } @Override public void perform() { final Term idx = toPositive(mTerm); final ClausifierInfo ci = getInfo(idx); // idx != m_Term ==> additional negation // idx == m_Term ==> no additional negation final boolean positive = (idx == mTerm) ^ mNegated; int flag, auxflag, negflag; if (positive) { flag = ClausifierInfo.POS_AXIOMS_ADDED; auxflag = ClausifierInfo.POS_AUX_AXIOMS_ADDED; negflag = ClausifierInfo.NEG_AXIOMS_ADDED; } else { flag = ClausifierInfo.NEG_AXIOMS_ADDED; auxflag = ClausifierInfo.NEG_AUX_AXIOMS_ADDED; negflag = ClausifierInfo.POS_AXIOMS_ADDED; } if (ci.testFlags(flag)) { // We've already added this formula as axioms return; } if (ci.testFlags(negflag)) { // We've added the negation as axiom => Create empty clause if (mEngine.isProofGenerationEnabled()) { // (@res sourceAnnot ci.getAxiomProof) Clause primary; Antecedent ante; final NamedAtom idxAtom = new NamedAtom(idx, mStackLevel); mTracker.quoted(idx, idxAtom); if (positive) { primary = new Clause(new Literal[] {idxAtom}, getClauseProof(mProofTerm)); ante = new Antecedent(idxAtom.negate(), new Clause( new Literal[] {idxAtom.negate()}, ci.getAxiomProof( mTracker, idx, idxAtom))); } else { primary = new Clause(new Literal[] {idxAtom}, ci.getAxiomProof(mTracker, idx, idxAtom)); ante = new Antecedent(idxAtom.negate(), new Clause( new Literal[] {idxAtom.negate()}, getClauseProof(mProofTerm))); } final ResolutionNode rn = new ResolutionNode(primary, new Antecedent[] {ante}); addClause(new Literal[0], null, rn); } else { addClause(new Literal[0], null, mProof); } return; } assert (!ci.isAxiomProofAvailable()); ci.setAxiomProof(mTerm, mProofTerm, getAnnotation(), !positive); mUndoTrail = new RemoveAxiomProof(mUndoTrail, ci); final Literal auxlit = ci.getLiteral(); if (auxlit != null) { if (!ci.testFlags(auxflag)) { (new AddAuxAxioms(idx, auxlit, positive)).perform(); } mTracker.quoted(idx, auxlit.getAtom()); addClause(new Literal[] { positive ? auxlit : auxlit.negate()}, null, getClauseProof(mProofTerm)); ci.setFlag(flag); mUndoTrail = new RemoveFlag(mUndoTrail, ci, flag); return; } ci.setFlag(flag); mUndoTrail = new RemoveFlag(mUndoTrail, ci, flag); final Theory t = mTerm.getTheory(); if (idx instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) idx; if (at.getFunction() == t.mOr) { if (positive) { final BuildClause bc = new BuildClause(mProofTerm, at); bc.setOrigArgs(at.getParameters()); pushOperation(bc); for (final Term p : at.getParameters()) { pushOperation(new CollectLiterals(p, bc)); } } else { for (final Term p : at.getParameters()) { pushOperation(new AddAsAxiom(p, true, mTracker.split(p, mProofTerm, ProofConstants.SPLIT_NEG_OR))); } } } else if ((!at.getFunction().isIntern() || at.getFunction().getName().equals("select")) && at.getFunction().getReturnSort() == t.getBooleanSort()) { final Literal lit = createBooleanLit(at); final IProofTracker sub = mTracker.getDescendent(); sub.intern(at, lit); addClause(new Literal[] {positive ? lit : lit.negate()}, null, getProofNewSource(sub.clause(mProofTerm))); } else if (at.getFunction().getName().equals("=")) { final Term lhs = at.getParameters()[0]; final Term rhs = at.getParameters()[1]; if (lhs.getSort() == t.getBooleanSort()) { final BuildClause bc1 = new BuildClause(LeafNode.NO_THEORY); pushOperation(bc1); Term t1, t2; if (positive) { bc1.setProofTerm(mTracker.split(at, mProofTerm, ProofConstants.SPLIT_POS_EQ_1)); pushOperation(new CollectLiterals(lhs, bc1)); pushOperation(new CollectLiterals( t2 = new Utils(bc1.getTracker()). createNot(rhs), bc1)); bc1.setOrigArgs(lhs, t2); } else { bc1.setProofTerm(mTracker.split(at, mProofTerm, ProofConstants.SPLIT_NEG_EQ_1)); pushOperation(new CollectLiterals(lhs, bc1)); pushOperation(new CollectLiterals(rhs, bc1)); bc1.setOrigArgs(lhs, rhs); } bc1.getTracker().markPosition(); final BuildClause bc2 = new BuildClause(LeafNode.NO_THEORY); pushOperation(bc2); final Utils tmp = new Utils(bc2.getTracker()); if (positive) { bc2.setProofTerm(mTracker.split(at, mProofTerm, ProofConstants.SPLIT_POS_EQ_2)); pushOperation(new CollectLiterals( t1 = tmp.createNot(lhs), bc2)); pushOperation(new CollectLiterals(rhs, bc2)); bc2.setOrigArgs(t1, rhs); } else { bc2.setProofTerm(mTracker.split(at, mProofTerm, ProofConstants.SPLIT_NEG_EQ_2)); pushOperation(new CollectLiterals( t1 = tmp.createNot(lhs), bc2)); pushOperation(new CollectLiterals( t2 = tmp.createNot(rhs), bc2)); bc2.setOrigArgs(t1, t2); } bc2.getTracker().markPosition(); } else { final SharedTerm slhs = getSharedTerm(lhs); final SharedTerm srhs = getSharedTerm(rhs); final EqualityProxy eq = createEqualityProxy(slhs, srhs); // eq == true and positive ==> return // eq == true and !positive ==> addClause({}) // eq == false and !positive ==> return // eq == false and positive ==> addClause({}) if (eq == EqualityProxy.getTrueProxy()) { if (!positive) { mTracker.eq(lhs, rhs, mTheory.mTrue); mTracker.negation(mTheory.mTrue, mTheory.mFalse, ProofConstants.RW_NOT_SIMP); addClause(new Literal[0], null, getClauseProof(mProofTerm)); } return; } if (eq == EqualityProxy.getFalseProxy()) { if (positive) { mTracker.eq(lhs, rhs, mTheory.mFalse); addClause(new Literal[0], null, getClauseProof(mProofTerm)); } return; } final Literal lit = eq.getLiteral(); final IProofTracker sub = mTracker.getDescendent(); sub.intern(at, lit); addClause(new Literal[] { positive ? lit : lit.negate()}, null, getProofNewSource(sub.clause(mProofTerm))); } } else if (at.getFunction().getName().equals("ite")) { assert at.getFunction().getReturnSort() == t.getBooleanSort(); final Term cond = at.getParameters()[0]; Term thenForm = at.getParameters()[1]; Term elseForm = at.getParameters()[2]; int kind1 = ProofConstants.SPLIT_POS_ITE_1; int kind2 = ProofConstants.SPLIT_POS_ITE_2; BuildClause bc1, bc2; Term t1; if (!positive) { kind1 = ProofConstants.SPLIT_NEG_ITE_1; kind2 = ProofConstants.SPLIT_NEG_ITE_2; } bc1 = new BuildClause(LeafNode.NO_THEORY); bc1.setProofTerm(mTracker.split(at, mProofTerm, kind1)); Utils tmp1 = new Utils(bc1.getTracker()); pushOperation(bc1); pushOperation(new CollectLiterals( t1 = tmp1.createNot(cond), bc1)); if (!positive) { thenForm = tmp1.createNot(thenForm); } bc1.setOrigArgs(t1, thenForm); tmp1 = null; pushOperation(new CollectLiterals(thenForm, bc1)); bc1.getTracker().markPosition(); bc2 = new BuildClause(LeafNode.NO_THEORY); bc2.setProofTerm(mTracker.split(at, mProofTerm, kind2)); Utils tmp2 = new Utils(bc2.getTracker()); pushOperation(bc2); pushOperation(new CollectLiterals(cond, bc2)); if (!positive) { elseForm = tmp2.createNot(elseForm); } bc2.setOrigArgs(cond, elseForm); tmp2 = null; pushOperation(new CollectLiterals(elseForm, bc2)); bc2.getTracker().markPosition(); } else if (at.getFunction().getName().equals("<=")) { // (<= SMTAffineTerm 0) final Literal lit = createLeq0(at); final IProofTracker sub = mTracker.getDescendent(); sub.intern(at, lit); if (lit.getSign() == -1 && !positive) { sub.negateLit(lit, mTheory); } addClause(new Literal[] {positive ? lit : lit.negate()}, null, getProofNewSource(sub.clause(mProofTerm))); } else if (at == t.mTrue) { // Nothing to do... } else if (at == t.mFalse) { addClause(new Literal[0], null, getClauseProof(mProofTerm)); } else { throw new InternalError("Not implementd: " + SMTAffineTerm.cleanup(at)); } } else if (idx instanceof QuantifiedFormula) { // TODO Fix Quantifiers once supported throw new SMTLIBException( "Cannot create quantifier in quantifier-free logic"); } else { throw new InternalError( "Don't know how to convert into axiom: " + SMTAffineTerm.cleanup(mTerm)); } } } private class AddAuxAxioms implements Operation { private final Term mTerm; private final Literal mAuxLit; private final boolean mPositive; public AddAuxAxioms(Term term, Literal lit, boolean pos) { assert(term == toPositive(term)); mTerm = term; mAuxLit = lit; mPositive = pos; } @Override public void perform() { final ClausifierInfo ci = getInfo(mTerm); int auxflag, flag, negflag; if (mPositive) { auxflag = ClausifierInfo.POS_AUX_AXIOMS_ADDED; flag = ClausifierInfo.POS_AXIOMS_ADDED; negflag = ClausifierInfo.NEG_AXIOMS_ADDED; } else { auxflag = ClausifierInfo.NEG_AUX_AXIOMS_ADDED; flag = ClausifierInfo.NEG_AXIOMS_ADDED; negflag = ClausifierInfo.POS_AXIOMS_ADDED; } if (ci.testFlags(auxflag)) { // We've already added the aux axioms // Nothing to do return; } if (ci.testFlags(flag)) { /* * If we know that the axiom already holds, the aux axioms * trivially simplify to true. * Hence, we don't need them at all. */ return; } ci.setFlag(auxflag); mUndoTrail = new RemoveFlag(mUndoTrail, ci, auxflag); if (ci.testFlags(negflag)) { // simplify by asserting the proxy as unit. final Literal[] unit = new Literal[] { mPositive ? mAuxLit.negate() : mAuxLit }; if (mEngine.isProofGenerationEnabled()) { addClause(unit, null, ci.getAxiomProof(mTracker, mTerm, mAuxLit)); } else { addClause(unit, null, mProof); } return; } final Theory t = mTerm.getTheory(); if (mTerm instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) mTerm; if (at.getFunction() == t.mOr) { if (mPositive) { final BuildClause bc = new BuildClause( ProofConstants.AUX_OR_POS); bc.auxAxiom(mAuxLit, at, null, null); bc.addLiteral(mAuxLit.negate()); bc.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit.negate(), at.getParameters())); pushOperation(bc); for (final Term param : at.getParameters()) { pushOperation(new CollectLiterals(param, bc)); } } else { final CreateNegClauseAuxAxioms helper = new CreateNegClauseAuxAxioms(mAuxLit); pushOperation(helper); helper.init(mTerm); } } else if (at.getFunction().getName().equals("ite")) { final Term cond = at.getParameters()[0]; final Term thenTerm = at.getParameters()[1]; final Term elseTerm = at.getParameters()[2]; if (mPositive) { Term t1; final BuildClause bc1 = new BuildClause( ProofConstants.AUX_ITE_POS_1); bc1.auxAxiom(mAuxLit, at, null, null); bc1.addLiteral(mAuxLit.negate()); pushOperation(bc1); pushOperation(new CollectLiterals(thenTerm, bc1)); pushOperation(new CollectLiterals( t1 = new Utils(bc1.getTracker()). createNot(cond), bc1)); bc1.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit.negate(), t1, thenTerm)); bc1.getTracker().markPosition(); final BuildClause bc2 = new BuildClause( ProofConstants.AUX_ITE_POS_2); bc2.auxAxiom(mAuxLit, at, null, null); bc2.addLiteral(mAuxLit.negate()); pushOperation(bc2); pushOperation(new CollectLiterals(elseTerm, bc2)); pushOperation(new CollectLiterals(cond, bc2)); bc2.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit.negate(), cond, elseTerm)); bc2.getTracker().markPosition(); if (Config.REDUNDANT_ITE_CLAUSES) { final BuildClause bc3 = new BuildClause( ProofConstants.AUX_ITE_POS_RED); bc3.auxAxiom(mAuxLit, at, null, null); bc3.addLiteral(mAuxLit.negate()); pushOperation(bc3); pushOperation(new CollectLiterals(elseTerm, bc3)); pushOperation(new CollectLiterals(thenTerm, bc3)); bc3.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit.negate(), thenTerm, elseTerm)); bc3.getTracker().markPosition(); } } else { Term t1, t2; final BuildClause bc1 = new BuildClause( ProofConstants.AUX_ITE_NEG_1); Utils tmp1 = new Utils(bc1.getTracker()); bc1.auxAxiom(mAuxLit, at, null, null); bc1.addLiteral(mAuxLit); pushOperation(bc1); pushOperation(new CollectLiterals( t2 = tmp1.createNot(thenTerm), bc1)); pushOperation(new CollectLiterals( t1 = tmp1.createNot(cond), bc1)); bc1.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit, t1, t2)); bc1.getTracker().markPosition(); tmp1 = null; final BuildClause bc2 = new BuildClause( ProofConstants.AUX_ITE_NEG_2); bc2.auxAxiom(mAuxLit, at, null, null); bc2.addLiteral(mAuxLit); pushOperation(bc2); pushOperation(new CollectLiterals( t1 = new Utils( bc2.getTracker()). createNot(elseTerm), bc2)); pushOperation(new CollectLiterals(cond, bc2)); bc2.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit, cond, t1)); bc2.getTracker().markPosition(); if (Config.REDUNDANT_ITE_CLAUSES) { final BuildClause bc3 = new BuildClause( ProofConstants.AUX_ITE_NEG_RED); final Utils tmp3 = new Utils(bc3.getTracker()); bc3.auxAxiom(mAuxLit, at, null, null); bc3.addLiteral(mAuxLit); pushOperation(bc3); pushOperation(new CollectLiterals( t2 = tmp3.createNot(elseTerm), bc3)); pushOperation(new CollectLiterals( t1 = tmp3.createNot(thenTerm), bc3)); bc3.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit, t1, t2)); bc3.getTracker().markPosition(); } } } else if (at.getFunction().getName().equals("=")) { assert at.getParameters().length == 2; final Term lhs = at.getParameters()[0]; final Term rhs = at.getParameters()[1]; assert (lhs.getSort() == t.getBooleanSort()); assert (rhs.getSort() == t.getBooleanSort()); Term t1, t2; if (mPositive) { final BuildClause bc1 = new BuildClause( ProofConstants.AUX_EQ_POS_1); Utils tmp1 = new Utils(bc1.getTracker()); bc1.auxAxiom(mAuxLit, at, null, null); bc1.addLiteral(mAuxLit.negate()); pushOperation(bc1); pushOperation(new CollectLiterals( t2 = tmp1.createNot(rhs), bc1)); pushOperation(new CollectLiterals(lhs, bc1)); bc1.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit.negate(), lhs, t2)); bc1.getTracker().markPosition(); tmp1 = null; final BuildClause bc2 = new BuildClause( ProofConstants.AUX_EQ_POS_2); final Utils tmp2 = new Utils(bc2.getTracker()); bc2.auxAxiom(mAuxLit, at, null, null); bc2.addLiteral(mAuxLit.negate()); pushOperation(bc2); pushOperation(new CollectLiterals(rhs, bc2)); pushOperation(new CollectLiterals( t1 = tmp2.createNot(lhs), bc2)); bc2.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit.negate(), t1, rhs)); bc2.getTracker().markPosition(); } else { final BuildClause bc1 = new BuildClause( ProofConstants.AUX_EQ_NEG_1); bc1.auxAxiom(mAuxLit, at, null, null); bc1.addLiteral(mAuxLit); pushOperation(bc1); pushOperation(new CollectLiterals(rhs, bc1)); pushOperation(new CollectLiterals(lhs, bc1)); bc1.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit, lhs, rhs)); bc1.getTracker().markPosition(); final BuildClause bc2 = new BuildClause( ProofConstants.AUX_EQ_NEG_2); final Utils tmp = new Utils(bc2.getTracker()); bc2.auxAxiom(mAuxLit, at, null, null); bc2.addLiteral(mAuxLit); pushOperation(bc2); pushOperation(new CollectLiterals( t2 = tmp.createNot(rhs), bc2)); pushOperation(new CollectLiterals( t1 = tmp.createNot(lhs), bc2)); bc2.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit, t1, t2)); bc2.getTracker().markPosition(); } } else { throw new InternalError("AuxAxiom not implemented: " + SMTAffineTerm.cleanup(mTerm)); } } else if (mTerm instanceof QuantifiedFormula) { // TODO: Correctly implement this once we support quantifiers. throw new SMTLIBException( "Cannot create quantifier in quantifier-free logic"); } else { throw new InternalError( "Don't know how to create aux axiom: " + SMTAffineTerm.cleanup(mTerm)); } } } private class CreateNegClauseAuxAxioms implements Operation { Set<Term> mDisjuncts = new LinkedHashSet<Term>(); private final Literal mAuxLit; public CreateNegClauseAuxAxioms(Literal auxLit) { mAuxLit = auxLit; } public void init(Term term) { // Cannot be done in ctor since CollectDisjuncts has to be before this. pushOperation(new CollectDisjuncts(term)); } @Override public void perform() { Term t; for (final Term disj : mDisjuncts) { final BuildClause bc = new BuildClause(ProofConstants.AUX_OR_NEG); bc.auxAxiom(mAuxLit, disj, null, null); bc.addLiteral(mAuxLit); pushOperation(bc); pushOperation(new CollectLiterals( t = new Utils(bc.getTracker()). createNot(disj), bc)); bc.getTracker().markPosition(); bc.setOrigArgs(mTracker.produceAuxAxiom( mAuxLit, t)); } } private class CollectDisjuncts implements Operation { private final Term mTerm; public CollectDisjuncts(Term term) { mTerm = term; } @Override public void perform() { if (mTerm instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) mTerm; if (at.getFunction() == at.getTheory().mOr) { for (final Term disj : at.getParameters()) { pushOperation(new CollectDisjuncts(disj)); } return; } } mDisjuncts.add(mTerm); } } } /** * Collect literals to build a clause. * @author Juergen Christ */ private class CollectLiterals implements Operation { private final Term mTerm; private final BuildClause mCollector; public CollectLiterals(Term term, BuildClause collector) { assert term.getSort() == mTheory.getBooleanSort(); mTerm = term; mCollector = collector; } @Override public void perform() { final Theory t = mTerm.getTheory(); if (mTerm == t.mFalse) { return; } if (mTerm == t.mTrue) { mCollector.setTrue(); return; } final Term idx = toPositive(mTerm); final boolean positive = mTerm == idx; // TODO What about this optimization? It increases the number of // conflicts on some examples, but should be better. // Literal knownlit = getLiteralForPolarity(idx, positive); // if (knownlit != null) { // m_Collector.addLiteral(knownlit); // return; // } if (idx instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) idx; if (positive && at.getFunction() == t.mOr) { if (mTerm.mTmpCtr > Config.OCC_INLINE_THRESHOLD) { // m_Logger.trace("Don't inline the clause" + SMTAffineTerm.cleanup(idx)); mCollector.getTracker().save(); final Literal lit = getLiteral(idx); mCollector.getTracker().quoted(idx, lit.getAtom()); mCollector.addLiteral(lit, mTerm); mCollector.getTracker().cleanSave(); } else { mCollector.setFlatten(at.getParameters()); for (final Term p : at.getParameters()) { pushOperation(new CollectLiterals(p, mCollector)); } } } else if ((!at.getFunction().isIntern() || at.getFunction().getName().equals("select")) && at.getFunction().getReturnSort() == t.getBooleanSort()) { mCollector.getTracker().save(); final Literal lit = createBooleanLit(at); mCollector.getTracker().intern(idx, lit); mCollector.addLiteral(positive ? lit : lit.negate(), mTerm); mCollector.getTracker().cleanSave(); // } else if (at.getFunction().getName().equals("ite")) { // Term cond = at.getParameters()[0]; // Term tc = at.getParameters()[1]; // Term ec = at.getParameters()[2]; // assert tc.getSort() == t.getBooleanSort(); // // (ite cond tc ec) === // // (or (and cond tc) (and (not cond) ec)) // /* // * (= A B) === (or (and A B) (and (not A) (not B))) // */ } else if (at.getFunction().getName().equals("=") && at.getParameters()[0].getSort() != mTheory.getBooleanSort()) { final Term lhs = at.getParameters()[0]; final Term rhs = at.getParameters()[1]; final SharedTerm slhs = getSharedTerm(lhs); final SharedTerm srhs = getSharedTerm(rhs); final EqualityProxy eq = createEqualityProxy(slhs, srhs); // eq == true and positive ==> set to true // eq == true and !positive ==> noop // eq == false and !positive ==> set to true // eq == false and positive ==> noop if (eq == EqualityProxy.getTrueProxy()) { if (positive) { mCollector.setTrue(); } else { mCollector.getTracker().eq(lhs, rhs, mTheory.mTrue); mCollector.getTracker().negation(mTheory.mTrue, mTheory.mFalse, ProofConstants.RW_NOT_SIMP); mCollector.getTracker().notifyFalseLiteral(at); mCollector.setSimpOr(); } return; } if (eq == EqualityProxy.getFalseProxy()) { if (positive) { mCollector.getTracker().eq(lhs, rhs, mTheory.mFalse); mCollector.getTracker().notifyFalseLiteral(at); mCollector.setSimpOr(); } else { mCollector.setTrue(); } return; } mCollector.getTracker().save(); final DPLLAtom eqAtom = eq.getLiteral(); mCollector.getTracker().eq(lhs, rhs, eqAtom); mCollector.addLiteral( positive ? eqAtom : eqAtom.negate(), mTerm); mCollector.getTracker().cleanSave(); } else if (at.getFunction().getName().equals("<=")) { // (<= SMTAffineTerm 0) mCollector.getTracker().save(); final Literal lit = createLeq0(at); mCollector.getTracker().intern(at, lit); if (!positive && lit.getSign() == -1) { mCollector.getTracker().negateLit(lit, mTheory); } mCollector.addLiteral(positive ? lit : lit.negate(), mTerm); mCollector.getTracker().cleanSave(); } else { mCollector.getTracker().save(); final Literal lit = getLiteral(mTerm); mCollector.getTracker().quoted(mTerm, lit); mCollector.addLiteral(lit, mTerm); mCollector.getTracker().cleanSave(); } } else { if (positive) { assert (idx instanceof QuantifiedFormula); final Literal lit = getLiteral(idx); // TODO: Proof mCollector.addLiteral(lit, mTerm); } else { // TODO Skolemize and recurse } } } } private class BuildClause implements Operation { private boolean mIsTrue = false; private final int mLeafKind; private final LinkedHashSet<Literal> mLits = new LinkedHashSet<Literal>(); private Term mProofTerm; private Term[] mOrigArgs; private final IProofTracker mSubTracker = mTracker.getDescendent(); private boolean mFlatten; private boolean mSimpOr; //@ invariant ProofProductionEnabled ==> // (m_LeafKind != LeafNode.NO_THEORY) == (m_ProofTerm == null); public BuildClause(int leafKind) { mLeafKind = leafKind; mProofTerm = null; } public BuildClause(Term proofTerm, Term original) { this(LeafNode.NO_THEORY); mProofTerm = proofTerm; } public void auxAxiom(Literal lit, Term res, Term base, Object auxData) { mProofTerm = mSubTracker.auxAxiom( mLeafKind, lit, res, base, auxData); } public void setProofTerm(Term proof) { mProofTerm = proof; } public void setOrigArgs(Term... args) { mOrigArgs = args; } /** * Add a literal to the clause. Use this version if merges on this * literal are possible. This version notifies the proof tracker and * then delegates to the non-merge function {@link #addLiteral(Literal)} * to do the real work. * @param lit The literal to add to the clause. * @param t The term for which this literal has been created. */ public void addLiteral(Literal lit, Term t) { if (mSubTracker.notifyLiteral(lit, t)) { addLiteral(lit); } else { mSimpOr = true; mSubTracker.restore(); } } /** * Add a literal to the clause. This version should only be used if * merges on the literal are impossible or already taken care of. This * function records trivial satisfiability of the clause and takes care * or the proof tracker to restore duplicated intern-steps on merges of * the literal. Furthermore, it remembers to perform delayed clause * simplification. * @param lit The literal to add to the clause. */ public void addLiteral(Literal lit) { if (mLits.add(lit)) { mIsTrue |= mLits.contains(lit.negate()); } else { mSimpOr = true; } } public void setTrue() { mIsTrue = true; } @Override public void perform() { if (!mIsTrue) { final Literal[] lits = mLits.toArray(new Literal[mLits.size()]); if (mFlatten) { mSubTracker.flatten(mOrigArgs, mSimpOr); } else if (mSimpOr) { mSubTracker.orSimpClause(mOrigArgs); } addClause(lits, null, getProofNewSource(mLeafKind, mSubTracker.clause(mProofTerm))); } } public IProofTracker getTracker() { return mSubTracker; } public void setFlatten(Term[] origArgs) { for (final Term t : origArgs) { if (t instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) t; if (shouldFlatten(at)) { mOrigArgs = origArgs; mFlatten = true; return; } } } } public void setSimpOr() { mSimpOr = true; } } private class AddDivideAxioms implements Operation { private final Term mDivTerm; private final Term mDivider; private final Rational mDivident; public AddDivideAxioms(Term divTerm, Term divider, Rational divident) { mDivTerm = divTerm; mDivider = divider; mDivident = divident; } @Override public void perform() { IProofTracker sub = mTracker.getDescendent(); Utils tmp = new Utils(sub); final SMTAffineTerm arg = SMTAffineTerm.create(mDivider); final SMTAffineTerm div = SMTAffineTerm.create(mDivTerm); // (<= (- (* d (div x d)) x) 0) final SMTAffineTerm difflow = div.mul(mDivident).add(arg.negate()); final Literal lit1 = createLeq0( (ApplicationTerm) tmp.createLeq0(difflow)); Term prf = sub.auxAxiom( ProofConstants.AUX_DIV_LOW, null, difflow, null, null); sub.leq0(difflow, lit1); addClause(new Literal[] {lit1}, null, getProofNewSource( ProofConstants.AUX_DIV_LOW, sub.clause(prf))); // (not (<= (+ |d| (- x) (* d (div x d))) 0)) sub = mTracker.getDescendent(); tmp = new Utils(sub); final SMTAffineTerm diffhigh = arg.negate().add(div.mul(mDivident)).add( mDivident.abs()); prf = sub.auxAxiom( ProofConstants.AUX_DIV_HIGH, null, diffhigh, null, null); final Literal lit2 = createLeq0( (ApplicationTerm) tmp.createLeq0(diffhigh)); sub.leq0(diffhigh, lit2); if (lit2.getSign() == -1) { sub.negateLit(lit2, mTheory); } addClause(new Literal[] {lit2.negate()}, null, getProofNewSource( ProofConstants.AUX_DIV_HIGH, sub.clause(prf))); } } /** * Helper to add the auxiliary axioms for to_int axioms. Since the axioms * for (to_int x) equal the axioms added for (div x 1), we reuse * AddDivideAxioms. * @author Juergen Christ */ private class AddToIntAxioms implements Operation { private final ApplicationTerm mToIntTerm; public AddToIntAxioms(ApplicationTerm toIntTerm) { mToIntTerm = toIntTerm; } @Override public void perform() { IProofTracker sub = mTracker.getDescendent(); Utils tmp = new Utils(sub); final SMTAffineTerm realTerm = SMTAffineTerm.create( mToIntTerm.getParameters()[0]); final SMTAffineTerm toInt = SMTAffineTerm.create(mToIntTerm).typecast( realTerm.getSort()); // (<= (- (to_real (to_int x)) x) 0) final SMTAffineTerm difflow = toInt.add(realTerm.negate()); final Literal lit1 = createLeq0( (ApplicationTerm) tmp.createLeq0(difflow)); Term prf = sub.auxAxiom( ProofConstants.AUX_TO_INT_LOW, null, difflow, null, null); sub.leq0(difflow, lit1); addClause(new Literal[] {lit1}, null, getProofNewSource( ProofConstants.AUX_TO_INT_LOW, sub.clause(prf))); // (not (<= (+ d (- x) (* d (div x d))) 0)) sub = mTracker.getDescendent(); tmp = new Utils(sub); final SMTAffineTerm diffhigh = toInt.add(Rational.ONE).add(realTerm.negate()); prf = sub.auxAxiom( ProofConstants.AUX_TO_INT_HIGH, null, diffhigh, null, null); final Literal lit2 = createLeq0( (ApplicationTerm) tmp.createLeq0(diffhigh)); sub.leq0(diffhigh, lit2); if (lit2.getSign() == -1) { sub.negateLit(lit2, mTheory); } addClause(new Literal[] {lit2.negate()}, null, getProofNewSource( ProofConstants.AUX_TO_INT_HIGH, sub.clause(prf))); } } /** * Add the axioms for the law of excluded middle. This must happen if a * Boolean function is used as a parameter to a non-Boolean function. * @author Juergen Christ */ private class AddExcludedMiddleAxiom implements Operation { private final SharedTerm mSharedTerm; public AddExcludedMiddleAxiom(SharedTerm shared) { mSharedTerm = shared; } @Override public void perform() { final EqualityProxy thenProxy = createEqualityProxy( mSharedTerm, mSharedTrue); final EqualityProxy elseProxy = createEqualityProxy( mSharedTerm, mSharedFalse); // These asserts should hold since we do not add excluded middle // axioms for true or false, and the equality proxies are // non-numeric assert thenProxy != EqualityProxy.getTrueProxy(); assert thenProxy != EqualityProxy.getFalseProxy(); assert elseProxy != EqualityProxy.getTrueProxy(); assert elseProxy != EqualityProxy.getFalseProxy(); // m_Term => thenForm is (not m_Term) \/ thenForm final BuildClause bc1 = new BuildClause( ProofConstants.AUX_EXCLUDED_MIDDLE_1); final Literal lit1 = thenProxy.getLiteral(); bc1.auxAxiom(lit1, mSharedTerm.getTerm(), null, null); bc1.addLiteral(lit1); pushOperation(bc1); pushOperation(new CollectLiterals( new Utils(bc1.getTracker()).createNot( mSharedTerm.getTerm()), bc1)); // (not m_Term) => elseForm is m_Term \/ elseForm final BuildClause bc2 = new BuildClause( ProofConstants.AUX_EXCLUDED_MIDDLE_2); final Literal lit2 = elseProxy.getLiteral(); bc2.auxAxiom(lit2, mSharedTerm.getTerm(), null, null); bc2.addLiteral(lit2); pushOperation(bc2); pushOperation(new CollectLiterals(mSharedTerm.getTerm(), bc2)); } } public static class ConditionChain { final ConditionChain mPrev; final Term mCond; final boolean mNegated; public ConditionChain(ConditionChain prev, Term cond) { this(prev, cond, false); } public ConditionChain(ConditionChain prev, Term cond, boolean negated) { mPrev = prev; mCond = cond; mNegated = negated; } public Term getTerm() { return mNegated ? mCond.getTheory().term( mCond.getTheory().mNot, mCond) : mCond; } public ConditionChain getPrevious() { return mPrev; } } private class AddTermITEAxiom implements Operation { private class CollectConditions implements Operation { private final ConditionChain mConds; private final Term mTerm; private final SharedTerm mIte; public CollectConditions( ConditionChain conds, Term term, SharedTerm ite) { mConds = conds; mTerm = term; mIte = ite; } @Override public void perform() { if (mTerm instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) mTerm; if (at.getFunction().getName().equals("ite") && at.mTmpCtr <= Config.OCC_INLINE_TERMITE_THRESHOLD) { final Term c = at.getParameters()[0]; final Term t = at.getParameters()[1]; final Term e = at.getParameters()[2]; mCollects.push(new CollectConditions( new ConditionChain(mConds, c), t, mIte)); mCollects.push(new CollectConditions( new ConditionChain(mConds, c, true), e, mIte)); return; } } // Not a nested ite term or a nested shared ite term final BuildClause bc = new BuildClause(ProofConstants.AUX_TERM_ITE); bc.auxAxiom(null, mTerm, mIte.getTerm(), mConds); pushOperation(bc); final SharedTerm st = getSharedTerm(mTerm); final EqualityProxy eqproxy = createEqualityProxy(mIte, st); // These asserts should be safe assert eqproxy != EqualityProxy.getFalseProxy(); assert eqproxy != EqualityProxy.getTrueProxy(); final DPLLAtom eq = eqproxy.getLiteral(); /* We don't track merges here since there cannot be any merges * on this equality. Otherwise we have an infinite term (since * the termITE is a sub-term of itself). */ bc.addLiteral(eq); bc.getTracker().eq(mIte.getTerm(), mTerm, eq); ConditionChain walk = mConds; final Utils tmp = new Utils(bc.getTracker()); while (walk != null) { pushOperation(new CollectLiterals( walk.mNegated ? walk.mCond : tmp.createNot(walk.mCond), bc)); walk = walk.mPrev; } } } private final SharedTerm mTermITE; private ArrayDeque<Operation> mCollects; public AddTermITEAxiom(SharedTerm termITE) { mTermITE = termITE; } @Override public void perform() { mCollects = new ArrayDeque<Clausifier.Operation>(); final ApplicationTerm ite = (ApplicationTerm) mTermITE.getTerm(); final Term cond = ite.getParameters()[0]; mCollects.push( new CollectConditions(new ConditionChain(null, cond), ite.getParameters()[1], mTermITE)); mCollects.push( new CollectConditions(new ConditionChain(null, cond, true), ite.getParameters()[2], mTermITE)); while (!mCollects.isEmpty()) { mCollects.pop().perform(); } } } // Term creation public MutableAffinTerm createMutableAffinTerm(SharedTerm term) { final SMTAffineTerm at = SMTAffineTerm.create(term.getTerm()); return createMutableAffinTerm(at); } MutableAffinTerm createMutableAffinTerm(SMTAffineTerm at) { final MutableAffinTerm res = new MutableAffinTerm(); res.add(at.getConstant()); for (final Map.Entry<Term,Rational> summand : at.getSummands().entrySet()) { final SharedTerm shared = getSharedTerm(summand.getKey()); final Rational coeff = summand.getValue(); shared.shareWithLinAr(); res.add(shared.mFactor.mul(coeff), shared); res.add(shared.mOffset.mul(coeff)); } return res; } public SharedTerm getSharedTerm(Term t) { return getSharedTerm(t, false); } /** * Get or create a shared term for a term. This version does not force * creation of a CCTerm for non-internal functions with arguments if * <code>inCCTermBuilder</code> is <code>true</code>. * * As a side effect, this function adds divide, to_int, or ite axioms for * the corresponding terms. Furthermore, For Boolean terms other than true * or false the law of excluded middle is instantiated. * @param t The term to create a shared term for. * @param inCCTermBuilder Are we in {@link CCTermBuilder}? * @return Shared term. */ public SharedTerm getSharedTerm(Term t, boolean inCCTermBuilder) { // NOPMD if (t instanceof SMTAffineTerm) { t = ((SMTAffineTerm) t).internalize(mCompiler); } SharedTerm res = mSharedTerms.get(t); if (res == null) { // if we reach here, t is neither true nor false res = new SharedTerm(this, t); mSharedTerms.put(t, res); if (t instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) t; // Special cases if (t.getSort() == t.getTheory().getBooleanSort()) { pushOperation(new AddExcludedMiddleAxiom(res)); } else { final FunctionSymbol fs = at.getFunction(); if (fs.isInterpreted()) { if (fs.getName().equals("div")) { pushOperation( new AddDivideAxioms(t, at.getParameters()[0], SMTAffineTerm.create( at.getParameters()[1]). getConstant())); } else if (fs.getName().equals("to_int")) { pushOperation(new AddToIntAxioms(at)); } else if (fs.getName().equals("ite") && (fs.getReturnSort() != mTheory.getBooleanSort())) { pushOperation(new AddTermITEAxiom(res)); } else if (fs.getName().equals("store")) { pushOperation(new AddStoreAxioms(at)); } else if (fs.getName().equals("@diff")) { pushOperation(new AddDiffAxiom(at)); } } if (needCCTerm(fs) && !inCCTermBuilder && at.getParameters().length > 0) { final CCTermBuilder cc = new CCTermBuilder(); res.mCCterm = cc.convert(t); } } } if (t instanceof SMTAffineTerm) { res.shareWithLinAr(); } } return res; } private static boolean needCCTerm(FunctionSymbol fs) { return !fs.isInterpreted() || fs.getName() == "select" || fs.getName() == "store" || fs.getName() == "@diff"; } /// Internalization stuff private final FormulaUnLet mUnlet = new FormulaUnLet(); private final TermCompiler mCompiler = new TermCompiler(); private final OccurrenceCounter mOccCounter = new OccurrenceCounter(); private final Deque<Operation> mTodoStack = new ArrayDeque<Clausifier.Operation>(); private ProofNode mProof; private final Theory mTheory; private final DPLLEngine mEngine; private CClosure mCClosure; private LinArSolve mLASolver; private ArrayTheory mArrayTheory; private boolean mInstantiationMode; /** * Mapping from Boolean terms to information about clauses produced for * these terms. */ private final Map<Term, ClausifierInfo> mClauseData = new HashMap<Term, ClausifierInfo>(); /** * Mapping from Boolean base terms to literals. A term is considered a base * term when it corresponds to an atom or its negation. */ private final Map<Term, Literal> mLiteralData = new HashMap<Term, Literal>(); /** * We cache the SharedTerms for true and false here to be able to quickly * create excluded middle axioms. */ SharedTerm mSharedTrue, mSharedFalse; /// Statistics stuff @SuppressWarnings("unused") private int mNumAtoms = 0; /// Assertion stack stuff /** * The undo trail used as a stack. */ private TrailObject mUndoTrail = new TrailObject() { @Override public void undo() { // Nothing to do for this sentinel entry } }; /** * Keep all shared terms that need to be unshared from congruence closure * when the top level is popped off the assertion stack. */ final ScopedArrayList<SharedTerm> mUnshareCC = new ScopedArrayList<SharedTerm>(); /** * Keep all shared terms that need to be unshared from linear arithmetic * when the top level is popped off the assertion stack. */ final ScopedArrayList<SharedTerm> mUnshareLA = new ScopedArrayList<SharedTerm>(); /** * Mapping from terms to their corresponding shared terms. */ final ScopedHashMap<Term, SharedTerm> mSharedTerms = new ScopedHashMap<Term, SharedTerm>(); /** * Map of differences to equality proxies. */ final ScopedHashMap<SMTAffineTerm, EqualityProxy> mEqualities = new ScopedHashMap<SMTAffineTerm, EqualityProxy>(); /** * Current assertion stack level. */ private int mStackLevel = 0; /** * The number of pushes that failed since the solver already detected * unsatisfiability. */ private int mNumFailedPushes = 0; /** * Did we emit a warning on a failed push? */ private boolean mWarnedFailedPush = false; private final LogProxy mLogger; /** * A tracker for proof production. */ private final IProofTracker mTracker; public Clausifier(DPLLEngine engine, int proofLevel) { mTheory = engine.getSMTTheory(); mEngine = engine; mLogger = engine.getLogger(); mTracker = proofLevel == 2 ? new ProofTracker() : new NoopProofTracker(); mCompiler.setProofTracker(mTracker); } public void setAssignmentProduction(boolean on) { mCompiler.setAssignmentProduction(on); } void pushOperation(Operation op) { mTodoStack.push(op); } /** * Transform a term to a positive normal term. A term is a positive normal * term if it * <ul> * <li>is an {@link ApplicationTerm} that does not correspond to a negation * </li> * <li>is a {@link QuantifiedFormula} that is universally quantified</li> * </ul> * @param t The term to compute the positive for. * @return The positive of this term. */ private static Term toPositive(Term t) { assert !(t instanceof AnnotatedTerm); if (t instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) t; return (at.getFunction() == at.getTheory().mNot) ? at.getParameters()[0] : at; } // FIXME: Think about how to get Utils in here... // else if (t instanceof QuantifiedFormula) { // QuantifiedFormula qf = (QuantifiedFormula) t; // if (qf.getQuantifier() == QuantifiedFormula.EXISTS) { // // (exists (x) (phi x)) is (not (forall (x) (not (phi x)))) // return t.getTheory().forall(qf.getVariables(), // Utils.createNot(qf.getSubformula())); // } // return qf; // } throw new InternalError("Unclear how to compute positive for " + t); } NamedAtom createAnonAtom(Term smtFormula) { final NamedAtom atom = new NamedAtom(smtFormula, mStackLevel); mEngine.addAtom(atom); mTracker.quoted(smtFormula, atom); mNumAtoms++; return atom; } BooleanVarAtom createBooleanVar(Term smtFormula) { final BooleanVarAtom atom = new BooleanVarAtom(smtFormula, mStackLevel); mUndoTrail = new RemoveAtom(mUndoTrail, smtFormula); mEngine.addAtom(atom); mNumAtoms++; return atom; } // QuantifiedAtom createQuantifiedAtom(Term smtFormula) { // String name = "quantproxy!" + numAtoms; // QuantifiedAtom atom = new QuantifiedAtom(name, smtFormula, m_StackLevel); // m_LiteralData.put(smtFormula, atom); // m_UndoTrail = new RemoveAtom(m_UndoTrail, smtFormula); // m_Engine.addAtom(atom); // ++numAtoms; // return atom; // } public EqualityProxy createEqualityProxy(SharedTerm lhs, SharedTerm rhs) { SMTAffineTerm diff = SMTAffineTerm.create(lhs.getTerm()).addUnchecked( SMTAffineTerm.create(rhs.getTerm()).negate(), lhs.getSort() == rhs.getSort()); if (diff.isConstant()) { if (diff.getConstant().equals(Rational.ZERO)) { return EqualityProxy.getTrueProxy(); } else { return EqualityProxy.getFalseProxy(); } } diff = diff.div(diff.getGcd()); // normalize equality to integer logic if all variables are integer. if (mTheory.getLogic().isIRA() && !diff.isIntegral() && diff.isAllIntSummands()) { diff = diff.typecast(getTheory().getSort("Int")); } // check for unsatisfiable integer formula, e.g. 2x + 2y = 1. if (diff.isIntegral() && !diff.getConstant().isIntegral()) { return EqualityProxy.getFalseProxy(); } // we cannot really normalize the sign of the term. Try both signs. EqualityProxy eqForm = mEqualities.get(diff); if (eqForm != null) { return eqForm; } eqForm = mEqualities.get(diff.negate()); if (eqForm != null) { return eqForm; } eqForm = new EqualityProxy(this, lhs, rhs); mEqualities.put(diff, eqForm); return eqForm; } Literal getLiteralForPolarity(Term t, boolean positive) { assert t == toPositive(t); final ClausifierInfo ci = mClauseData.get(t); if (ci != null && ci.getLiteral() != null) { if (positive) { if (!ci.testFlags(ClausifierInfo.POS_AUX_AXIOMS_ADDED)) { pushOperation( new AddAuxAxioms(t, ci.getLiteral(), positive)); } return ci.getLiteral(); } else { if (!ci.testFlags(ClausifierInfo.NEG_AUX_AXIOMS_ADDED)) { pushOperation( new AddAuxAxioms(t, ci.getLiteral(), positive)); } return ci.getLiteral().negate(); } } return null; } Literal getLiteral(Term t) { final Term idx = toPositive(t); final boolean pos = t == idx; ClausifierInfo ci = mClauseData.get(idx); if (ci == null) { ci = new ClausifierInfo(); mClauseData.put(idx, ci); mUndoTrail = new RemoveClausifierInfo(mUndoTrail, idx); } if (ci.getLiteral() == null) { final Literal lit = createAnonAtom(idx); ci.setLiteral(lit); mUndoTrail = new RemoveLiteral(mUndoTrail, ci); pushOperation(new AddAuxAxioms(idx, lit, pos)); return pos ? lit : lit.negate(); } if (pos) { if (!ci.testFlags(ClausifierInfo.POS_AUX_AXIOMS_ADDED)) { pushOperation(new AddAuxAxioms(idx, ci.getLiteral(), true)); } return ci.getLiteral(); } if (!ci.testFlags(ClausifierInfo.NEG_AUX_AXIOMS_ADDED)) { pushOperation(new AddAuxAxioms(idx, ci.getLiteral(), false)); } return ci.getLiteral().negate(); } Literal getLiteralTseitin(Term t) { final Term idx = toPositive(t); final boolean pos = t == idx; ClausifierInfo ci = mClauseData.get(idx); if (ci == null) { ci = new ClausifierInfo(); mClauseData.put(idx, ci); mUndoTrail = new RemoveClausifierInfo(mUndoTrail, idx); } if (ci.getLiteral() == null) { final Literal lit = createAnonAtom(idx); ci.setLiteral(lit); mUndoTrail = new RemoveLiteral(mUndoTrail, ci); pushOperation(new AddAuxAxioms(idx, lit, true)); pushOperation(new AddAuxAxioms(idx, lit, false)); return pos ? lit : lit.negate(); } if (!ci.testFlags(ClausifierInfo.POS_AUX_AXIOMS_ADDED)) { pushOperation(new AddAuxAxioms(idx, ci.getLiteral(), true)); } if (!ci.testFlags(ClausifierInfo.NEG_AUX_AXIOMS_ADDED)) { pushOperation(new AddAuxAxioms(idx, ci.getLiteral(), false)); } return pos ? ci.getLiteral() : ci.getLiteral().negate(); } ClausifierInfo getInfo(Term idx) { assert(idx == toPositive(idx)); ClausifierInfo res = mClauseData.get(idx); if (res == null) { res = new ClausifierInfo(); mClauseData.put(idx, res); mUndoTrail = new RemoveClausifierInfo(mUndoTrail, idx); } return res; } void addClause(Literal[] lits, ClauseDeletionHook hook, ProofNode proof) { if (mInstantiationMode) { // TODO Add instantiation clauses to DPLL } else { mEngine.addFormulaClause(lits, proof, hook); } } void addToUndoTrail(TrailObject obj) { obj.setPrevious(mUndoTrail); mUndoTrail = obj; } private void pushUndoTrail() { mUndoTrail = new ScopeMarker(mUndoTrail); } private void popUndoTrail() { while (!mUndoTrail.isScopeMarker()) { mUndoTrail.undo(); mUndoTrail = mUndoTrail.getPrevious(); } // Skip the scope marker mUndoTrail = mUndoTrail.getPrevious(); } void addUnshareCC(SharedTerm shared) { mUnshareCC.add(shared); } void addUnshareLA(SharedTerm shared) { mUnshareLA.add(shared); } private void setupCClosure() { if (mCClosure == null) { mCClosure = new CClosure(mEngine); mEngine.addTheory(mCClosure); /* If we do not setup the cclosure at the root level, we remove it * with the corresponding pop since the axiom true != false will be * removed from the assertion stack as well. */ if (mStackLevel != 0) { mUndoTrail = new RemoveTheory(mUndoTrail); } mSharedTrue = new SharedTerm(this, mTheory.mTrue); mSharedTrue.mCCterm = mCClosure.createAnonTerm(mSharedTrue); mSharedTerms.put(mTheory.mTrue, mSharedTrue); mSharedFalse = new SharedTerm(this, mTheory.mFalse); mSharedFalse.mCCterm = mCClosure.createAnonTerm(mSharedFalse); mSharedTerms.put(mTheory.mFalse, mSharedFalse); final Literal[] lits = new Literal[] { mCClosure.createCCEquality( mStackLevel, mSharedTrue.mCCterm, mSharedFalse.mCCterm).negate()}; mEngine.addFormulaClause(lits, getProofNewSource(ProofConstants.AUX_TRUE_NOT_FALSE, mTracker.auxAxiom( ProofConstants.AUX_TRUE_NOT_FALSE, lits[0], mTheory.mTrue, null, null))); } } private void setupLinArithmetic() { if (mLASolver == null) { mLASolver = new LinArSolve(mEngine); mEngine.addTheory(mLASolver); } } private void setupArrayTheory() { if (mArrayTheory == null) { mArrayTheory = new ArrayTheory(this, mCClosure); mEngine.addTheory(mArrayTheory); } } // private void setupQuantifiers() { // TODO Implement // setupCClosure(); // try { // Class<?> pcclass = getClass().getClassLoader().loadClass( // System.getProperty( // Config.PATTERNCOMPILER, // Config.DEFAULT_PATTERNCOMPILER)); // patternCompiler = (IPatternCompiler)pcclass.newInstance(); // } catch (Exception e) { // logger.fatal("Could not load Pattern Compiler",e); // throw new RuntimeException("Could not load Pattern Compiler",e); // } // quantTheory = new QuantifierTheory(cclosure); // dpllEngine.addTheory(quantTheory); // } public void setLogic(Logics logic) { if (mEngine.isProofGenerationEnabled()) { setSourceAnnotation(LeafNode.NO_THEORY, SourceAnnotation.EMPTY_SOURCE_ANNOT); } if (logic.isBitVector() || logic.isQuantified() || logic.isNonLinearArithmetic()) { throw new UnsupportedOperationException( "Logic " + logic.toString() + " unsupported"); } if (logic.isUF() || logic.isArray()) { setupCClosure(); } if (logic.isArithmetic()) { setupLinArithmetic(); } if (logic.isArray()) { setupArrayTheory(); } } public Iterable<BooleanVarAtom> getBooleanVars() { return new Iterable<BooleanVarAtom>() { @Override public Iterator<BooleanVarAtom> iterator() { return new BooleanVarIterator(mLiteralData.values().iterator()); } }; } private static final class BooleanVarIterator implements Iterator<BooleanVarAtom> { private final Iterator<Literal> mIt; private BooleanVarAtom mNext; public BooleanVarIterator(Iterator<Literal> it) { mIt = it; computeNext(); } private final void computeNext() { while (mIt.hasNext()) { final Literal lit = mIt.next(); if (lit instanceof BooleanVarAtom) { mNext = (BooleanVarAtom) lit; return; } } mNext = null; } @Override public boolean hasNext() { return mNext != null; } @Override public BooleanVarAtom next() { final BooleanVarAtom res = mNext; if (res == null) { throw new NoSuchElementException(); } computeNext(); return res; } @Override public void remove() { throw new UnsupportedOperationException(); } } private final void run() { while (!mTodoStack.isEmpty()) { if (mEngine.isTerminationRequested()) { /* Note: Engine remembers incompleteness */ mTodoStack.clear(); return; } final Operation op = mTodoStack.pop(); op.perform(); } } public DPLLEngine getEngine() { return mEngine; } public CClosure getCClosure() { return mCClosure; } public LinArSolve getLASolver() { return mLASolver; } public LogProxy getLogger() { return mLogger; } public int getStackLevel() { return mStackLevel; } public void addFormula(Term f) { if (mEngine.inconsistent()) { mLogger.warn("Already inconsistent."); return; } if (mEngine.isProofGenerationEnabled()) { setSourceAnnotation(LeafNode.NO_THEORY, SourceAnnotation.EMPTY_SOURCE_ANNOT); if (f instanceof AnnotatedTerm) { final AnnotatedTerm at = (AnnotatedTerm)f; final Annotation[] annots = at.getAnnotations(); for (final Annotation a : annots) { if (a.getKey().equals(":named")) { final String name = ((String) a.getValue()).intern(); setSourceAnnotation(LeafNode.NO_THEORY, new SourceAnnotation(name, null)); break; } } } } Term tmp = mUnlet.unlet(f); // f = null; // System.err.println(tmp.toStringDirect()); Term tmp2; try { tmp2 = mCompiler.transform(tmp); } finally { mCompiler.reset(); } tmp = null; // System.err.println("Transformed"); // System.err.println(SMTAffineTerm.cleanup(tmp2).toStringDirect()); final Term proof = mTracker.getRewriteProof(f); mTracker.reset(); mOccCounter.count(tmp2); final Map<Term, Set<String>> names = mCompiler.getNames(); if (names != null) { for (final Map.Entry<Term, Set<String>> me : names.entrySet()) { trackAssignment(me.getKey(), me.getValue()); } } // System.err.println("Started convertion"); pushOperation(new AddAsAxiom(tmp2, proof)); mInstantiationMode = false; run(); // System.err.println("Ended convertion"); mOccCounter.reset(tmp2); tmp2 = null; // logger.info("Added " + numClauses + " clauses, " + numAtoms // + " auxiliary atoms."); if (mEngine.isProofGenerationEnabled()) { setSourceAnnotation(LeafNode.NO_THEORY, SourceAnnotation.EMPTY_SOURCE_ANNOT); } } // TODO need an instantiation mode here to add clauses to DPLL as deletable instantiations // public void addInstantiation(Term f, Map<TermVariable, Term> inst, // Literal quantproxy) { // if (m_Engine.isProofGenerationEnabled()) { // // TODO // } // m_Unlet.beginScope(); // m_Unlet.addSubstitutions(inst); // Term tmp = m_Unlet.unlet(f); // m_Unlet.endScope(); // Term tmp2 = m_Compiler.transform(tmp); // // /* // * This is an ugly hack. Since operations might introduce proxy // * literals with definitions that might be deleted once an instantiation // * is deleted, we cannot permanently store the proxy literals and the // * knowledge about their aux axioms. // */ // pushUndoTrail(); // m_InstantiationMode = true; // if (quantproxy == null) { // // toplevel match // pushOperation(new AddAsAxiom(tmp2)); // } else { // BuildClause bc = new BuildClause(LeafNode.QUANT_INST); // bc.addLiteral(quantproxy.negate()); // pushOperation(new CollectLiterals(tmp2, bc)); // } // run(); // popUndoTrail(); // // if (m_Engine.isProofGenerationEnabled()) { // // TODO // } // } public void push() { if (mEngine.inconsistent()) { if (!mWarnedFailedPush) { mLogger.warn("Already inconsistent."); mWarnedFailedPush = true; } ++mNumFailedPushes; } else { mEngine.push(); ++mStackLevel; mEqualities.beginScope(); mUnshareCC.beginScope(); mUnshareLA.beginScope(); mSharedTerms.beginScope(); pushUndoTrail(); } } public void pop(int numpops) { if (numpops <= mNumFailedPushes) { mNumFailedPushes -= numpops; return; } mWarnedFailedPush = false; numpops -= mNumFailedPushes; mNumFailedPushes = 0; mEngine.pop(numpops); for (int i = 0; i < numpops; ++i) { for (final SharedTerm t : mUnshareCC.currentScope()) { t.unshareCC(); } mUnshareCC.endScope(); for (final SharedTerm t : mUnshareLA.currentScope()) { t.unshareLA(); } mUnshareLA.endScope(); mEqualities.endScope(); popUndoTrail(); mSharedTerms.endScope(); } mStackLevel -= numpops; } public void setSourceAnnotation(int leafKind, IAnnotation sourceAnnot) { mProof = new LeafNode(leafKind, sourceAnnot); } private ProofNode getProofNewSource(Term source) { return getProofNewSource(LeafNode.NO_THEORY, source); } private ProofNode getProofNewSource(int leafKind, Term source) { if (source == null) { return mProof; } if (mProof instanceof LeafNode) { final LeafNode ln = (LeafNode) mProof; final SourceAnnotation annot = new SourceAnnotation( (SourceAnnotation) ln.getTheoryAnnotation(), source); return new LeafNode(leafKind, annot); } return mProof; } private ProofNode getClauseProof(Term proofTerm) { proofTerm = mTracker.clause(proofTerm); return getProofNewSource(proofTerm); } public IAnnotation getAnnotation() { if (mProof instanceof LeafNode) { return ((LeafNode) mProof).getTheoryAnnotation(); } return null; } public Theory getTheory() { return mTheory; } private void trackAssignment(Term term, Set<String> names) { Literal lit; final Term idx = toPositive(term); final boolean pos = idx == term; if (idx instanceof ApplicationTerm) { final ApplicationTerm at = (ApplicationTerm) idx; final FunctionSymbol fs = at.getFunction(); if (fs.getName().equals("<=")) { lit = createLeq0(at); } else if (fs.getName().equals("=") && at.getParameters()[0].getSort() != mTheory.getBooleanSort()) { final SharedTerm lhs = getSharedTerm(at.getParameters()[0]); final SharedTerm rhs = getSharedTerm(at.getParameters()[1]); final EqualityProxy ep = createEqualityProxy(lhs, rhs); if (ep == EqualityProxy.getTrueProxy()) { lit = new DPLLAtom.TrueAtom(); } else if (ep == EqualityProxy.getFalseProxy()) { lit = new DPLLAtom.TrueAtom().negate(); } else { lit = ep.getLiteral(); } } else if ((!fs.isIntern() || fs.getName().equals("select")) && fs.getReturnSort() == mTheory.getBooleanSort()) { lit = createBooleanLit(at); } else if (at == mTheory.mTrue) { lit = new DPLLAtom.TrueAtom(); } else if (at == mTheory.mFalse) { lit = new DPLLAtom.TrueAtom().negate(); } else { lit = getLiteralTseitin(idx); } } else { lit = getLiteralTseitin(idx); } if (!pos) { lit = lit.negate(); } for (final String name : names) { mEngine.trackAssignment(name, lit); } } private Literal createLeq0(ApplicationTerm leq0term) { Literal lit = mLiteralData.get(leq0term); if (lit == null) { final SMTAffineTerm sum = SMTAffineTerm.create(leq0term.getParameters()[0]); final MutableAffinTerm msum = createMutableAffinTerm(sum); lit = mLASolver.generateConstraint(msum, false, mStackLevel); mLiteralData.put(leq0term, lit); mUndoTrail = new RemoveAtom(mUndoTrail, leq0term); } return lit; } private Literal createBooleanLit(ApplicationTerm term) { Literal lit = mLiteralData.get(term); if (lit == null) { if (term.getParameters().length == 0) { lit = createBooleanVar(term); } else { final SharedTerm st = getSharedTerm(term); final EqualityProxy eq = createEqualityProxy(st, mSharedTrue); // Safe since m_Term is neither true nor false assert eq != EqualityProxy.getTrueProxy(); assert eq != EqualityProxy.getFalseProxy(); lit = eq.getLiteral(); } mLiteralData.put(term, lit); mUndoTrail = new RemoveAtom(mUndoTrail, term); } return lit; } public boolean resetBy0Seen() { return mCompiler.resetBy0Seen(); } public IProofTracker getTracker() { return mTracker; } public Literal getCreateLiteral(Term f) { Term tmp = mUnlet.unlet(f); Term tmp2; try { tmp2 = mCompiler.transform(tmp); } finally { mCompiler.reset(); } tmp = null; mOccCounter.count(tmp2); ApplicationTerm at = (ApplicationTerm) tmp2; boolean negated = false; FunctionSymbol fs = at.getFunction(); if (fs == mTheory.mNot) { at = (ApplicationTerm) at.getParameters()[0]; fs = at.getFunction(); negated = true; } Literal res; if (!fs.isIntern() || fs.getName().equals("select")) { res = createBooleanLit(at); } else if (at == mTheory.mTrue) { res = new TrueAtom(); } else if (at == mTheory.mFalse) { res = new TrueAtom().negate(); } else if (fs.getName().equals("=")) { if (at.getParameters()[0].getSort() == mTheory.getBooleanSort()) { res = getLiteralTseitin(at); } else { final EqualityProxy ep = createEqualityProxy( getSharedTerm(at.getParameters()[0]), getSharedTerm(at.getParameters()[1])); if (ep == EqualityProxy.getFalseProxy()) { res = new TrueAtom().negate(); } else if (ep == EqualityProxy.getTrueProxy()) { res = new TrueAtom(); } else { res = ep.getLiteral(); } } } else if (fs.getName().equals("<=")) { res = createLeq0(at); } else { res = getLiteralTseitin(at); } mInstantiationMode = false; run(); mOccCounter.reset(tmp2); tmp2 = null; return negated ? res.negate() : res; } public static boolean shouldFlatten(ApplicationTerm term) { return term.getFunction() == term.getTheory().mOr && term.mTmpCtr <= Config.OCC_INLINE_THRESHOLD; } }