/*
* 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.smtlib2;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
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.Assignments;
import de.uni_freiburg.informatik.ultimate.logic.CheckClosedTerm;
import de.uni_freiburg.informatik.ultimate.logic.ConstantTerm;
import de.uni_freiburg.informatik.ultimate.logic.FormulaUnLet;
import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbol;
import de.uni_freiburg.informatik.ultimate.logic.FunctionSymbolFactory;
import de.uni_freiburg.informatik.ultimate.logic.Logics;
import de.uni_freiburg.informatik.ultimate.logic.Model;
import de.uni_freiburg.informatik.ultimate.logic.NoopScript;
import de.uni_freiburg.informatik.ultimate.logic.PrintTerm;
import de.uni_freiburg.informatik.ultimate.logic.QuotedObject;
import de.uni_freiburg.informatik.ultimate.logic.Rational;
import de.uni_freiburg.informatik.ultimate.logic.ReasonUnknown;
import de.uni_freiburg.informatik.ultimate.logic.SMTLIBException;
import de.uni_freiburg.informatik.ultimate.logic.Sort;
import de.uni_freiburg.informatik.ultimate.logic.Term;
import de.uni_freiburg.informatik.ultimate.logic.Theory;
import de.uni_freiburg.informatik.ultimate.logic.simplification.SimplifyDDA;
import de.uni_freiburg.informatik.ultimate.smtinterpol.Config;
import de.uni_freiburg.informatik.ultimate.smtinterpol.DefaultLogger;
import de.uni_freiburg.informatik.ultimate.smtinterpol.LogProxy;
import de.uni_freiburg.informatik.ultimate.smtinterpol.Main;
import de.uni_freiburg.informatik.ultimate.smtinterpol.convert.Clausifier;
import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Clause;
import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.DPLLEngine;
import de.uni_freiburg.informatik.ultimate.smtinterpol.dpll.Literal;
import de.uni_freiburg.informatik.ultimate.smtinterpol.interpolate.Interpolator;
import de.uni_freiburg.informatik.ultimate.smtinterpol.interpolate.SymbolChecker;
import de.uni_freiburg.informatik.ultimate.smtinterpol.interpolate.SymbolCollector;
import de.uni_freiburg.informatik.ultimate.smtinterpol.option.OptionMap;
import de.uni_freiburg.informatik.ultimate.smtinterpol.option.OptionMap.CopyMode;
import de.uni_freiburg.informatik.ultimate.smtinterpol.option.SolverOptions;
import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ProofChecker;
import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.ProofTermGenerator;
import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.PropProofChecker;
import de.uni_freiburg.informatik.ultimate.smtinterpol.proof.UnsatCoreCollector;
import de.uni_freiburg.informatik.ultimate.util.datastructures.ScopedArrayList;
/**
* Implementation of the
* {@link de.uni_freiburg.informatik.ultimate.logic.Script} interface to
* interact with SMTInterpol.
*
* Users should however stick to the
* {@link de.uni_freiburg.informatik.ultimate.logic.Script} interface which
* provides most of the methods provided in this class.
* @author Juergen Christ
*/
public class SMTInterpol extends NoopScript {
private static class TimeoutHandler implements TerminationRequest {
TerminationRequest mStackedCancellation;
long mTimeout;
public TimeoutHandler(TerminationRequest stacked) {
mStackedCancellation = stacked;
clearTimeout();
}
public void clearTimeout() {
mTimeout = Long.MAX_VALUE;
}
public void setTimeout(long millis) {
mTimeout = System.currentTimeMillis() + millis;
}
@Override
public boolean isTerminationRequested() {
if (mStackedCancellation != null
&& mStackedCancellation.isTerminationRequested()) {
return true;
}
return System.currentTimeMillis() >= mTimeout;
}
}
public static enum CheckType {
FULL {
@Override
boolean check(DPLLEngine engine) {
engine.provideCompleteness(DPLLEngine.COMPLETE);
return engine.solve();
}
},
PROPAGATION {
@Override
boolean check(DPLLEngine engine) {
engine.provideCompleteness(DPLLEngine.INCOMPLETE_CHECK);
return engine.propagate();
}
},
QUICK {
@Override
boolean check(DPLLEngine engine) {
engine.provideCompleteness(DPLLEngine.INCOMPLETE_CHECK);
return engine.quickCheck();
}
};
abstract boolean check(DPLLEngine engine);
}
private static class SMTInterpolSetup extends Theory.SolverSetup {
private static class RewriteProofFactory extends FunctionSymbolFactory {
Sort mProofSort;
public RewriteProofFactory(String name, Sort proofSort) {
super(name);
mProofSort = proofSort;
}
@Override
public int getFlags(
BigInteger[] indices, Sort[] paramSorts, Sort resultSort) {
return paramSorts.length == 1 ? FunctionSymbol.INTERNAL
: FunctionSymbol.LEFTASSOC | FunctionSymbol.INTERNAL;
}
@Override
public Sort getResultSort(BigInteger[] indices, Sort[] paramSorts,
Sort resultSort) {
if (indices != null
|| paramSorts.length == 0 || paramSorts.length > 2
|| resultSort != null
|| paramSorts[0] != mProofSort) {
return null;
}
if (paramSorts.length == 2 && paramSorts[0] != paramSorts[1]) {
return null;
}
return paramSorts[0];
}
}
private final int mProofMode;
public SMTInterpolSetup(int proofMode) {
mProofMode = proofMode;
}
@Override
public void setLogic(Theory theory, Logics logic) {
final int leftassoc = FunctionSymbol.LEFTASSOC;
// Damn Java compiler...
Sort proof = null;
Sort[] proof2 = null;
final Sort bool = theory.getSort("Bool");
final Sort[] bool1 = {bool};
if (mProofMode > 0) {
// Partial proofs.
// Declare all symbols needed for proof production
declareInternalSort(theory, "@Proof", 0, 0);
proof = theory.getSort("@Proof");
proof2 = new Sort[] { proof, proof };
declareInternalFunction(
theory, "@res", proof2, proof, leftassoc);
declareInternalFunction(theory, "@tautology", bool1, proof, 0);
declareInternalFunction(theory, "@lemma", bool1, proof, 0);
declareInternalFunction(theory, "@asserted", bool1, proof, 0);
declareInternalFunction(theory, "@assumption", bool1, proof, 0);
}
if (mProofMode > 1) {
// Full proofs.
declareInternalFunction(theory, "@intern", bool1, proof, 0);
declareInternalFunction(
theory, "@split", new Sort[] {proof, bool}, proof, 0);
defineFunction(theory, new RewriteProofFactory("@eq", proof));
declareInternalFunction(theory, "@rewrite", bool1, proof, 0);
declareInternalFunction(
theory, "@clause", new Sort[] {proof, bool}, proof, 0);
}
defineFunction(theory, new FunctionSymbolFactory("@undefined") {
@Override
public int getFlags(
BigInteger[] indices, Sort[] paramSorts, Sort resultSort) {
return FunctionSymbol.INTERNAL | FunctionSymbol.RETURNOVERLOAD;
}
@Override
public Sort getResultSort(BigInteger[] indices, Sort[] paramSorts,
Sort resultSort) {
if (indices != null || paramSorts.length != 0) {
return null;
}
return resultSort;
}
});
if (logic.isArray()) {
declareArraySymbols(theory);
}
if (logic.hasIntegers()) {
declareIntSymbols(theory);
}
if (logic.hasReals()) {
declareRealSymbols(theory);
}
}
private final void declareIntSymbols(Theory theory) {
final Sort intSort = theory.getSort("Int");
final Sort[] sort1 = {intSort};
declareInternalFunction(theory, "@mod0", sort1, intSort, 0);
declareInternalFunction(theory, "@div0", sort1, intSort, 0);
}
private final void declareRealSymbols(Theory theory) {
final Sort realSort = theory.getSort("Real");
final Sort[] sort1 = {realSort};
declareInternalFunction(theory, "@/0", sort1, realSort, 0);
}
private final void declareArraySymbols(Theory theory) {
// Currently only diff
final Sort[] vars = theory.createSortVariables("Index", "Elem");
final Sort array = theory.getSort("Array", vars);
declareInternalPolymorphicFunction(
theory, "@diff", vars, new Sort[]{array, array}, vars[0], 0);
}
}
private final OptionMap mOptions;
private final SolverOptions mSolverOptions;
private DPLLEngine mEngine;
private Clausifier mClausifier;
private ScopedArrayList<Term> mAssertions;
private final TimeoutHandler mCancel;
private final LogProxy mLogger;
de.uni_freiburg.informatik.ultimate.smtinterpol.model.Model mModel = null;
private final static Object NAME = new QuotedObject("SMTInterpol");
private final static Object AUTHORS = new QuotedObject(
"Juergen Christ, Jochen Hoenicke, Alexander Nutz, and Tanja Schindler");
private final static Object INTERPOLATION_METHOD = new QuotedObject("tree");
// I assume an initial check s.t. first (get-info :status) returns sat
private LBool mStatus = LBool.SAT;
// The status set in the benchmark
private String mStatusSet = null;
private ReasonUnknown mReasonUnknown = null;
// The assertion stack was modified after the last check-sat, i.e., the
// m_status field is not valid and we have to deactivate
// get-{value,model,interpolants,proof}.
private boolean mAssertionStackModified = true;
// The assertion stack level at which the first division-by-0 was
// encountered. If it is -1, it means "never"
private int mBy0Seen = -1;
private long mNextQuickCheck = 1;
private long mNumAsserts = 0;
/**
* Delta debugger friendly version. Exits with following codes:
* model-check-mode fails: 1
* interpolant-check-mode fails: 2
* exception during check-sat: 3
* command that needed sat after last check got unsat: 4
* command that needed unsat after last check got sat: 5
*/
private final boolean mDDFriendly =
!Config.COMPETITION
&& System.getProperty("smtinterpol.ddfriendly") != null;
/**
* Default constructor using a default logger and no user termination
* request. If this constructor is used, SMTInterpol assumes ownership of
* the logger.
*/
public SMTInterpol() {
this(new DefaultLogger(), null);
}
/**
* Construct SMTInterpol with a user-owned logger but without user
* termination request.
* @param logger The logger owned by the caller.
*/
public SMTInterpol(LogProxy logger) {
this(logger, null);
}
/**
* Construct SMTInterpol with a logger but without user termination request.
* The logger is assumed to be configured by the user.
* @param logger The logger owned by the caller.
* @param ignored This parameter is ignored!
* @deprecated Use a constructor version without the boolean parameter!
*/
@Deprecated
public SMTInterpol(LogProxy logger, boolean ignored) {
this(logger, null);
}
/**
* Default constructor using a default logger and a given user termination
* request.
* @param cancel User termination request to poll during checks.
*/
public SMTInterpol(TerminationRequest cancel) {
this(new DefaultLogger(), cancel);
}
/**
* Construct SMTInterpol with a logger and a user termination
* request. This is the main constructor of SMTInterpol.
* @param logger The logger owned by the caller.
* @param cancel User termination request to poll during checks.
*/
public SMTInterpol(LogProxy logger, TerminationRequest cancel) {
this(cancel, new OptionMap(logger));
}
/**
* Construct SMTInterpol with an option map. SMTInterpol will use the
* logger used to initialize the option map.
* @param options The option map used to handle all options.
*/
public SMTInterpol(OptionMap options) {
this(null, options);
}
/**
* Construct SMTInterpol with a user termination request and a user created
* option map. This constructor is mainly used by the front
* ends to set an option map including front end options.
* @param cancel User termination request to poll during checks.
* @param options Option map to handle options.
*/
public SMTInterpol(TerminationRequest cancel,
OptionMap options) {
mLogger = options.getLogProxy();
mOptions = options;
mSolverOptions = options.getSolverOptions();
mCancel = new TimeoutHandler(cancel);
reset();
}
/**
* Construct SMTInterpol with a user-owned logger but without user
* termination request. Note that the logger is assumed to be correctly set
* up.
* @param logger The logger owned by the caller.
* @param ignored This option is ignored!
* @param cancel User termination request to poll during checks.
* @deprecated Use a constructor version without the boolean parameter.
*/
@Deprecated
public SMTInterpol(
LogProxy logger, boolean ignored, TerminationRequest cancel) {
this(logger, cancel);
}
/**
* Copy the current context and modify some pre-theory options. The copy
* shares the push/pop stack on the symbols but not on the assertions.
* Users should be careful not to mess up the push/pop stack, i.e., not to
* push on one context and pop on another one.
*
* Note that this cloning does not clone the assertion stack and should not
* be used in multi-threaded contexts since users cannot guarantee correct
* push/pop-stack treatment.
* @param other The context to clone.
* @param options The options to set before setting the logic.
* @param mode What to do when copying existing options.
*/
public SMTInterpol(SMTInterpol other, Map<String, Object> options,
OptionMap.CopyMode mode) {
super(other.getTheory());
mLogger = other.mLogger;
mOptions = other.mOptions.copy(mode);
mSolverOptions = mOptions.getSolverOptions();
if (options != null) {
for (final Map.Entry<String, Object> me : options.entrySet()) {
setOption(me.getKey(), me.getValue());
}
}
mCancel = other.mCancel;
setupClausifier(getTheory().getLogic());
}
// Called in ctor => make it final
/**
* Unset the logic and clear the assertion stack. This does not reset
* online modifiable options.
*/
@Override
public final void reset() {
super.reset();
mEngine = null;
mModel = null;
mAssertionStackModified = true;
if (mAssertions != null) {
mAssertions.clear();
}
mOptions.reset();
}
@Override
public final void resetAssertions() {
super.resetAssertions();
mAssertionStackModified = true;
if (mAssertions != null) {
mAssertions.clear();
}
setupClausifier(mEngine.getSMTTheory().getLogic());
}
@Override
public void push(int n) throws SMTLIBException {
super.push(n);
modifyAssertionStack();
while (n-- > 0) {
if (mAssertions != null) {
mAssertions.beginScope();
}
mClausifier.push();
}
}
@Override
public void pop(int n) throws SMTLIBException {
try {
super.pop(n);
} catch (final SMTLIBException eBug) {
if (mDDFriendly) {
System.exit(123);
}
throw eBug;
}
modifyAssertionStack();
int i = n;
while (i-- > 0) {
if (mAssertions != null) {
mAssertions.endScope();
}
}
mClausifier.pop(n);
if (mStackLevel < mBy0Seen) {
// We've popped all division-by-0s.
mBy0Seen = -1;
}
}
@Override
public LBool checkSat() throws SMTLIBException {
return checkSatAssuming();
}
@Override
public LBool checkSatAssuming(Term... assumptions) throws SMTLIBException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
mModel = null;
mAssertionStackModified = false;
mEngine.clearAssumptions();
if (assumptions != null && assumptions.length != 0) {
if (Config.STRONG_USAGE_CHECKS) {
// Check that every literal is a Boolean constant or its negation
for (final Term ass : assumptions) {
if (!(ass instanceof ApplicationTerm)) {
throw new SMTLIBException("Assumption is not a boolean constant");
}
final ApplicationTerm at = (ApplicationTerm) ass;
if (at.getSort() != getTheory().getBooleanSort()) {
throw new SMTLIBException("Assumption is not a boolean constant");
}
if (at.getParameters().length > 1
|| (at.getParameters().length == 1 && !at.getFunction().getName().equals("not"))) {
throw new SMTLIBException("Assumption is not a boolean constant");
}
}
}
// Since checkSatAssuming does not first do bcp and we might have
// popped, we manually trigger bcp
if (!mEngine.quickCheck()) {
return LBool.UNSAT;
}
final Literal[] assumptionlits = new Literal[assumptions.length];
for (int i = 0; i < assumptions.length; ++i) {
assumptionlits[i] = mClausifier.getCreateLiteral(assumptions[i]);
}
mEngine.assume(assumptionlits);
}
final long timeout = mSolverOptions.getTimeout();
if (timeout > 0) {
mCancel.setTimeout(timeout);
}
LBool result = LBool.UNKNOWN;
mReasonUnknown = ReasonUnknown.INCOMPLETE;
mEngine.setRandomSeed(mSolverOptions.getRandomSeed());
if (mSolverOptions.getCheckType().check(mEngine)) {
if (mEngine.hasModel()) {
result = LBool.SAT;
if (mSolverOptions.isModelCheckModeActive()) {
mModel = new de.uni_freiburg.informatik.ultimate.
smtinterpol.model.Model(
mClausifier, getTheory(),
mSolverOptions.isModelsPartial());
if (mDDFriendly && !mModel.checkTypeValues(mLogger)) {
System.exit(1);
}
for (final Term asserted : mAssertions) {
final Term checkedResult = mModel.evaluate(asserted);
if (checkedResult != getTheory().mTrue) {
if (mDDFriendly) {
System.exit(1);
}
mLogger.fatal("Model does not satisfy "
+ asserted.toStringDirect());
// for (Term t : getSatisfiedLiterals())
// if (m_Model.evaluate(t) != getTheory().TRUE)
// m_Logger.fatal("Unsat lit: " + t.toStringDirect());
}
}
}
} else {
result = LBool.UNKNOWN;
switch(mEngine.getCompleteness()) {
case DPLLEngine.COMPLETE:
if (mSolverOptions.getCheckType() == CheckType.FULL) {
throw new InternalError("Complete but no model?");
}
mReasonUnknown = ReasonUnknown.INCOMPLETE;
break;
case DPLLEngine.INCOMPLETE_MEMOUT:
mReasonUnknown = ReasonUnknown.MEMOUT;
break;
case DPLLEngine.INCOMPLETE_TIMEOUT:
mReasonUnknown = ReasonUnknown.TIMEOUT;
break;
case DPLLEngine.INCOMPLETE_QUANTIFIER:
case DPLLEngine.INCOMPLETE_THEORY:
mReasonUnknown = ReasonUnknown.INCOMPLETE;
break;
case DPLLEngine.INCOMPLETE_UNKNOWN:
mReasonUnknown = ReasonUnknown.CRASHED;
break;
case DPLLEngine.INCOMPLETE_CHECK:
mReasonUnknown = ReasonUnknown.INCOMPLETE;
break;
case DPLLEngine.INCOMPLETE_CANCELLED:
mReasonUnknown = ReasonUnknown.CANCELLED;
break;
default:
throw new InternalError("Unknown incompleteness reason");
}
mLogger.info("Got %s as reason to return unknown",
mEngine.getCompletenessReason());
}
} else {
result = LBool.UNSAT;
if (mSolverOptions.isProofCheckModeActive()) {
final ProofChecker proofchecker =
new ProofChecker(this, getLogger());
if (!proofchecker.check(getProof())) {
if (mDDFriendly) {
System.exit(2);
}
mLogger.fatal("Proof-checker did not verify");
}
}
}
mStatus = result;
if (Config.CHECK_STATUS_SET && isStatusSet()
&& mReasonUnknown != ReasonUnknown.MEMOUT
&& !mStatus.toString().equals(mStatusSet)) {
mLogger.warn("Status differs: User said %s but we got %s",
mStatusSet, mStatus);
if (mDDFriendly) {
System.exit(13);
}
}
mStatusSet = null;
mCancel.clearTimeout();
return result;
}
private final boolean isStatusSet() {
return mStatusSet != null && !mStatusSet.equals("unknown");
}
@Override
public void setLogic(String logic)
throws UnsupportedOperationException, SMTLIBException {
try {
setLogic(Logics.valueOf(logic));
} catch (final IllegalArgumentException ex) {
/* Logic is not in enumeration */
throw new
UnsupportedOperationException("Logic " + logic + " not supported");
}
}
@Override
public void setLogic(Logics logic)
throws UnsupportedOperationException, SMTLIBException {
mSolverSetup = new SMTInterpolSetup(getProofMode());
super.setLogic(logic);
setupClausifier(logic);
}
/**
* Setup the clausifier and the engine according to the logic,
* the current proof production mode, and some other options.
* @param logic the SMT-LIB logic to use.
* @throws UnsupportedOperationException if the logic is not supported
* by SMTInterpol.
*/
private void setupClausifier(Logics logic) {
try {
final int proofMode = getProofMode();
mEngine = new DPLLEngine(getTheory(), mLogger, mCancel);
mClausifier = new Clausifier(mEngine, proofMode);
// This has to be before set-logic since we need to capture
// initialization of CClosure.
mEngine.setProofGeneration(proofMode > 0);
mClausifier.setLogic(logic);
final boolean produceAssignment = getBooleanOption(":produce-assignments");
mClausifier.setAssignmentProduction(produceAssignment);
mEngine.setProduceAssignments(produceAssignment);
mEngine.setRandomSeed(mSolverOptions.getRandomSeed());
if (getBooleanOption(":interactive-mode")
|| mSolverOptions.isInterpolantCheckModeActive()
|| mSolverOptions.isModelCheckModeActive()
|| getBooleanOption(":unsat-core-check-mode")
|| getBooleanOption(":unsat-assumptions-check-mode")) {
mAssertions = new ScopedArrayList<Term>();
}
mOptions.setOnline();
mEngine.getSMTTheory().setGlobalSymbols(
((Boolean) mOptions.get(":global-declarations")).booleanValue());
} catch (final UnsupportedOperationException eLogicUnsupported) {
super.reset();
mEngine = null;
mClausifier = null;
throw eLogicUnsupported;
}
}
@Override
public LBool assertTerm(Term term) throws SMTLIBException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
super.assertTerm(term);
if (!term.getSort().equals(getTheory().getBooleanSort())) {
if (term.getSort().getTheory() == getTheory()) {
throw new SMTLIBException("Asserted terms must have sort Bool");
} else {
throw new SMTLIBException("Asserted terms created with incompatible theory");
}
}
if (Config.STRONG_USAGE_CHECKS && !new CheckClosedTerm().isClosed(term)) {
throw new SMTLIBException("Asserted terms must be closed");
}
if (mAssertions != null) {
mAssertions.add(term);
}
if (mEngine.inconsistent()) {
mLogger.info("Asserting into inconsistent context");
return LBool.UNSAT;
}
try {
modifyAssertionStack();
mClausifier.addFormula(term);
/* We always have to reset the flag, but only need to set the stack
* level if it is not already set.
*/
if (mClausifier.resetBy0Seen() && mBy0Seen == -1) {
mBy0Seen = mStackLevel;
}
if (mNumAsserts++ >= mNextQuickCheck) {
mNextQuickCheck *= 2;
if (!mEngine.quickCheck()) {
mLogger.info("Assertion made context inconsistent");
return LBool.UNSAT;
}
}
} catch (final UnsupportedOperationException ex) {
throw new SMTLIBException(ex.getMessage());
} catch (final RuntimeException exc) {
if (mDDFriendly)
{
System.exit(7);// NOCHECKSTYLE
}
throw exc;
} catch (final AssertionError exc) {
if (mDDFriendly)
{
System.exit(7);// NOCHECKSTYLE
}
throw exc;
}
return LBool.UNKNOWN;
}
@Override
public Term[] getAssertions() throws SMTLIBException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
if (mAssertions != null) {
return mAssertions.toArray(new Term[mAssertions.size()]);
}
throw new SMTLIBException(
"Set option :interactive-mode to true to get assertions!");
}
@Override
public Assignments getAssignment() throws SMTLIBException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
if (!mEngine.isProduceAssignments()) {
throw new SMTLIBException(
"Set option :produce-assignments to true to generate assignments!");
}
checkAssertionStackModified();
return mEngine.getAssignments();
}
@Override
public Object getInfo(String info) throws UnsupportedOperationException {
if (":status".equals(info)) {
return mStatus;
}
if (":name".equals(info)) {
return NAME;
}
if (":version".equals(info)) {
return new QuotedObject(Main.getVersion());
}
if (":authors".equals(info)) {
return AUTHORS;
}
if (":all-statistics".equals(info)) {
return mEngine == null ? new Object[0] : mEngine.getStatistics();
}
if (":status-set".equals(info)) {
return mStatusSet;
}
if (":options".equals(info)) {
return mOptions.getInfo();
}
if (":reason-unknown".equals(info)) {
if (mStatus != LBool.UNKNOWN) {
throw new SMTLIBException("Status not unknown");
}
return mReasonUnknown;
}
if (":assertion-stack-levels".equals(info)) {
return mStackLevel;
}
// Info from our SMTLIB interpolation proposal
if (":interpolation-method".equals(info)) {
return INTERPOLATION_METHOD;
}
return mOptions.getInfo(info);
}
@Override
public Object getOption(String opt) throws UnsupportedOperationException {
return mOptions.get(opt);
}
/**
* Get the proofMode according to the options that are set.
* @returns 2 for full proofs, 1 for propositional only proofs, 0
* for no proofs.
*/
private int getProofMode() {
if (mSolverOptions.isProofCheckModeActive()
|| mSolverOptions.isProduceProofs()) {
return 2;
} else if (mSolverOptions.isProduceInterpolants()
|| getBooleanOption(":produce-unsat-cores")) {
return 1;
} else {
return 0;
}
}
@Override
public Term getProof()
throws SMTLIBException, UnsupportedOperationException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
final int proofMode = getProofMode();
if (proofMode == 0) {
throw new SMTLIBException("Option :produce-proofs not set to true");
}
if (proofMode == 1) {
mLogger.warn("Using partial proofs (cut at CNF-level). "
+ "Set option :produce-proofs to true to get complete proofs."
);
}
checkAssertionStackModified();
final Clause unsat = retrieveProof();
if (Config.CHECK_PROP_PROOF) {
final PropProofChecker ppc = new PropProofChecker();
final boolean correct = ppc.check(unsat);
assert correct;
}
try {
final ProofTermGenerator generator = new ProofTermGenerator(getTheory());
Term res = generator.convert(unsat);
if (mBy0Seen != -1) {
res = new Div0Remover().transform(res);
}
return res;
} catch (final Exception exc) {
throw new SMTLIBException(exc.getMessage() == null
? exc.toString() : exc.getMessage());
}
}
@SuppressWarnings("unchecked")
@Override
public Term[] getInterpolants(Term[] partition, int[] startOfSubtree) {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
if (!mSolverOptions.isProduceProofs()
&& !mSolverOptions.isProduceInterpolants()) {
throw new SMTLIBException(
"Interpolant production not enabled. Set either :produce-interpolants or :produce-proofs to true");
}
final long timeout = mSolverOptions.getTimeout();
if (timeout > 0) {
mCancel.setTimeout(timeout);
}
try {
checkAssertionStackModified();
if (partition.length != startOfSubtree.length) {
throw new SMTLIBException(
"Partition table and subtree array need to have equal length");
}
if (Config.STRONG_USAGE_CHECKS) {
for (int i = 0; i < partition.length; i++) {
if (startOfSubtree[i] < 0) {
throw new SMTLIBException(
"subtree array must not contain negative element");
}
int j = i;
while (startOfSubtree[i] < j) {
j = startOfSubtree[j - 1];
}
if (startOfSubtree[i] != j) {
throw new SMTLIBException("malformed subtree array.");
}
}
if (startOfSubtree[partition.length - 1] != 0) {
throw new SMTLIBException("malformed subtree array.");
}
}
final Set<String>[] parts = new Set[partition.length];
final String errormsg =
"arguments must be named terms or conjunctions of named terms";
for (int i = 0; i < partition.length; i++) {
if (!(partition[i] instanceof ApplicationTerm)) {
throw new SMTLIBException(errormsg);
}
final FunctionSymbol fsym = ((ApplicationTerm) partition[i]).getFunction();
Term[] terms;
if (fsym.isIntern()) {
if (!fsym.getName().equals("and")) {
throw new SMTLIBException(errormsg);
}
terms = ((ApplicationTerm) partition[i]).getParameters();
} else {
terms = new Term[] { partition[i] };
}
parts[i] = new HashSet<String>();
for (int j = 0; j < terms.length; j++) {
if (!(terms[j] instanceof ApplicationTerm)) {
throw new SMTLIBException(errormsg);
}
final ApplicationTerm appTerm = (ApplicationTerm) terms[j];
if (appTerm.getParameters().length != 0) {
throw new SMTLIBException(errormsg);
}
if (appTerm.getFunction().isIntern()) {
throw new SMTLIBException(errormsg);
}
parts[i].add(appTerm.getFunction().getName().intern());
}
}
SMTInterpol tmpBench = null;
SymbolCollector collector = null;
Set<FunctionSymbol> globals = null;
if (mSolverOptions.isInterpolantCheckModeActive()) {
HashSet<String> usedParts = new HashSet<String>();
for (final Set<String> part : parts) {
usedParts.addAll(part);
}
tmpBench = new SMTInterpol(this,
Collections.singletonMap(":interactive-mode",
(Object)Boolean.TRUE), CopyMode.CURRENT_VALUE);
final int old = tmpBench.mLogger.getLoglevel();
try {
tmpBench.mLogger.setLoglevel(LogProxy.LOGLEVEL_ERROR);
// Clone the current context except for the parts used in the
// interpolation problem
collector = new SymbolCollector();
collector.startCollectTheory();
termloop:
for (final Term asserted : mAssertions) {
if (asserted instanceof AnnotatedTerm) {
final AnnotatedTerm annot = (AnnotatedTerm) asserted;
for (final Annotation an : annot.getAnnotations()) {
if (":named".equals(an.getKey())
&& usedParts.contains(an.getValue())) {
continue termloop;
}
}
}
tmpBench.assertTerm(asserted);
collector.addGlobalSymbols(asserted);
}
globals = collector.getTheorySymbols();
} finally {
tmpBench.mLogger.setLoglevel(old);
}
// free space
usedParts = null;
}
final Interpolator interpolator =
new Interpolator(mLogger, this, tmpBench, getTheory(), parts, startOfSubtree);
final Clause refutation = retrieveProof();
final Term[] ipls = interpolator.getInterpolants(refutation);
if (mBy0Seen != -1) {
final Div0Remover rem = new Div0Remover();
for (int i = 0; i < ipls.length; ++i) {
ipls[i] = rem.transform(ipls[i]);
}
}
if (mSolverOptions.isInterpolantCheckModeActive()) {
boolean error = false;
final int old = tmpBench.mLogger.getLoglevel();
try {
tmpBench.mLogger.setLoglevel(LogProxy.LOGLEVEL_ERROR);
// Compute Symbol occurrence
final Map<FunctionSymbol, Integer>[] occs =
new Map[partition.length];
for (int i = 0; i < partition.length; ++i) {
occs[i] = collector.collect(partition[i]);
}
// Recompute the symbol occurrence:
// occs[i] should be the symbols occurring in the subtree of
// partition[i]
for (int i = 0; i < startOfSubtree.length; ++i) {
// Find children
int child = i - 1;
while (child >= startOfSubtree[i]) {
// join occurrence maps
for (final Map.Entry<FunctionSymbol, Integer> me
: occs[child].entrySet()) {
Integer ival = occs[i].get(me.getKey());
ival = ival == null ? me.getValue()
: ival + me.getValue();
occs[i].put(me.getKey(), ival);
}
child = startOfSubtree[child] - 1;
}
}
final SymbolChecker checker = new SymbolChecker(globals);
for (int i = 0; i < startOfSubtree.length; ++i) {
tmpBench.push(1);
// Find and assert children
int child = i - 1;
while (child >= startOfSubtree[i]) {
tmpBench.assertTerm(ipls[child]);
child = startOfSubtree[child] - 1;
}
// Assert node
tmpBench.assertTerm(partition[i]);
// Assert negated interpolant
try {
if (i != ipls.length) {
tmpBench.assertTerm(tmpBench.term("not", ipls[i]));
}
} catch (final SMTLIBException exc) {
mLogger.error("Could not assert interpolant", exc);
error = true;
}
final LBool res = tmpBench.checkSat();
if (res == LBool.SAT) {
if (mDDFriendly) {
System.exit(2);
}
mLogger.error("Interpolant %d not inductive: "
+ " (Check returned %s)", i, res);
error = true;
} else if (res == LBool.UNKNOWN) {
final ReasonUnknown ru = tmpBench.mReasonUnknown;
mLogger.warn("Unable to check validity of interpolant: "
+ ru);
// I don't set the error flag here since I am not sure
// whether this is a real error or not. Maybe we should
// base this on ru?
}
tmpBench.pop(1);
// Check symbol condition
if (i != ipls.length
&& checker.check(ipls[i], occs[i], occs[ipls.length])) {
mLogger.error("Symbol error in Interpolant %d. "
+ "Subtree only symbols: %s. "
+ "Non-subtree only symbols: %s.", i,
checker.getLeftErrors(),
checker.getRightErrors());
error = true;
}
}
} finally {
tmpBench.mLogger.setLoglevel(old);
// Not needed for now, but maybe later...
tmpBench.exit();
}
if (error) {
throw new SMTLIBException(
"generated interpolants did not pass sanity check");
}
}
if (mSolverOptions.isSimplifyInterpolants()) {
final SimplifyDDA simplifier = new SimplifyDDA(new SMTInterpol(this,
Collections.singletonMap(
":check-type",
(Object) mSolverOptions.getSimplifierCheckType()),
CopyMode.CURRENT_VALUE),
getBooleanOption(":simplify-repeatedly"));
for (int i = 0; i < ipls.length; ++i) {
ipls[i] = simplifier.getSimplifiedTerm(ipls[i]);
}
}
return ipls;
} finally {
mCancel.clearTimeout();
}
}
@Override
public Term[] getUnsatCore()
throws SMTLIBException, UnsupportedOperationException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
if (!getBooleanOption(":produce-unsat-cores")) {
throw new SMTLIBException(
"Set option :produce-unsat-cores to true before using get-unsat-cores");
}
checkAssertionStackModified();
final Clause unsat = mEngine.getProof();
if (unsat == null) {
throw new SMTLIBException("Logical context not inconsistent!");
}
final Term[] core = new UnsatCoreCollector(this).getUnsatCore(unsat);
if (getBooleanOption(":unsat-core-check-mode")) {
final HashSet<String> usedParts = new HashSet<String>();
for (final Term t : core) {
usedParts.add(((ApplicationTerm)t).getFunction().getName());
}
final SMTInterpol tmpBench = new SMTInterpol(this, null, CopyMode.CURRENT_VALUE);
final int old = tmpBench.mLogger.getLoglevel();
try {
tmpBench.mLogger.setLoglevel(LogProxy.LOGLEVEL_ERROR);
// Clone the current context except for the parts used in
// the unsat core
termloop:
for (final Term asserted : mAssertions) {
if (asserted instanceof AnnotatedTerm) {
final AnnotatedTerm annot = (AnnotatedTerm) asserted;
for (final Annotation an : annot.getAnnotations()) {
if (":named".equals(an.getKey())
&& usedParts.contains(an.getValue())) {
continue termloop;
}
}
}
tmpBench.assertTerm(asserted);
}
for (final Term t : core) {
tmpBench.assertTerm(t);
}
final LBool isUnsat = tmpBench.checkSat();
if (isUnsat != LBool.UNSAT) {
mLogger.error(
"Unsat core could not be proven unsat (Result is %s)",
isUnsat);
}
} finally {
tmpBench.mLogger.setLoglevel(old);
// Not needed for now, but maybe later...
tmpBench.exit();
}
}
return core;
}
@Override
public Term[] getUnsatAssumptions()
throws SMTLIBException, UnsupportedOperationException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
if (!getBooleanOption(":produce-unsat-assumptions")) {
throw new SMTLIBException(
"Set option :produce-unsat-assumptions to true before using get-unsat-assumptions");
}
checkAssertionStackModified();
if (!mEngine.inconsistent()) {
throw new SMTLIBException("Logical context not inconsistent!");
}
final Literal[] unsatAssumptionLits = mEngine.getUnsatAssumptions();
final Term[] unsatAssumptions = new Term[unsatAssumptionLits.length];
final Theory t = getTheory();
for (int i = 0; i < unsatAssumptionLits.length; ++i) {
unsatAssumptions[i] = unsatAssumptionLits[i].negate().getSMTFormula(t);
}
if (getBooleanOption(":unsat-assumptions-check-mode")) {
final SMTInterpol tmpBench = new SMTInterpol(this, null, CopyMode.CURRENT_VALUE);
final int old = tmpBench.mLogger.getLoglevel();
try {
tmpBench.mLogger.setLoglevel(LogProxy.LOGLEVEL_ERROR);
// Clone the current context
for (final Term asserted : mAssertions) {
tmpBench.assertTerm(asserted);
}
for (final Term ass : unsatAssumptions) {
tmpBench.assertTerm(ass);
}
final LBool isUnsat = tmpBench.checkSat();
if (isUnsat != LBool.UNSAT) {
mLogger.error(
"Unsat assumptions could not be proven unsat (Result is %s)",
isUnsat);
}
} finally {
tmpBench.mLogger.setLoglevel(old);
// Not needed for now, but maybe later...
tmpBench.exit();
}
}
return unsatAssumptions;
}
@Override
public Map<Term, Term> getValue(Term[] terms)
throws SMTLIBException, UnsupportedOperationException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
buildModel();
return mModel.evaluate(terms);
}
@Override
public Model getModel() throws SMTLIBException,
UnsupportedOperationException {
if (mEngine == null) {
throw new SMTLIBException("No logic set!");
}
buildModel();
return mModel;
}
@Override
public void setInfo(String info, Object value) {
if (info.equals(":status")
&& value instanceof String) {
if (value.equals("sat")) {
mStatus = LBool.SAT;
mStatusSet = "sat";
} else if (value.equals("unsat")) {
mStatus = LBool.UNSAT;
mStatusSet = "unsat";
} else if (value.equals("unknown")) {
mStatus = LBool.UNKNOWN;
mStatusSet = "unknown";
}
}
}
@Override
public void setOption(String opt, Object value)
throws UnsupportedOperationException, SMTLIBException {
mOptions.set(opt, value);
}
@Override
public Term simplify(Term term) throws SMTLIBException {
final CheckType old = mSolverOptions.getCheckType();
final int oldNumScopes = mStackLevel;
try {
mSolverOptions.setCheckType(mSolverOptions.getSimplifierCheckType());
return new SimplifyDDA(this, getBooleanOption(":simplify-repeatedly")).
getSimplifiedTerm(term);
} finally {
mSolverOptions.setCheckType(old);
assert (mStackLevel == oldNumScopes);
}
}
/**
* Perform a restart and switch the decisions of all undecided literals.
* This method should efficiently lead the solver to explore another path
* in the search tree.
*/
public void flipDecisions() {
mEngine.flipDecisions();
}
/**
* Flip the truth value decision for a name literal.
* @param name The name used in the annotation for this literal.
* @throws SMTLIBException If name not known.
*/
public void flipNamedLiteral(String name) throws SMTLIBException {
mEngine.flipNamedLiteral(name);
}
/**
* Access to the internal CNF transformer. Should not be used by users.
* @return Internal CNF transformer.
*/
public Clausifier getClausifier() {
return mClausifier;
}
/**
* Access to the internal DPLL engine. Should not be used by users.
* @return Internal DPLL engine.
*/
public DPLLEngine getEngine() {
return mEngine;
}
/**
* Access to the logger used by SMTInterpol.
* @return The logger used by SMTInterpol.
*/
public LogProxy getLogger() {
return mLogger;
}
protected void setEngine(DPLLEngine engine) {
mEngine = engine;
}
protected void setClausifier(Clausifier clausifier) {
mClausifier = clausifier;
}
private void checkAssertionStackModified() throws SMTLIBException {
if (mAssertionStackModified) {
throw new SMTLIBException(
"Assertion stack has been modified since last check-sat!");
}
}
private void modifyAssertionStack() {
mAssertionStackModified = true;
mModel = null;
mEngine.clearAssumptions();
}
private void buildModel() throws SMTLIBException {
checkAssertionStackModified();
if (mEngine.inconsistent()) {
if (mDDFriendly)
{
System.exit(4); // NOCHECKSTYLE
}
throw new SMTLIBException("Context is inconsistent");
}
if (mStatus != LBool.SAT) {
// Once we have incomplete solvers we might check mReasonUnknown...
if (mDDFriendly) {
System.exit(9);
}
throw new SMTLIBException(
"Cannot construct model since solving did not complete");
}
if (mModel == null) {
mModel = new
de.uni_freiburg.informatik.ultimate.smtinterpol.model.Model(
mClausifier, getTheory(), mSolverOptions.isModelsPartial());
}
}
/**
* Retrieve the proof in its internal format. Users should use
* {@link #getProof()} to retrieve the proof as a proof term.
* @return Internal proof.
* @throws SMTLIBException If no proof is present or the proof is found to
* to be incorrect.
*/
@SuppressWarnings("unused")
public Clause retrieveProof() throws SMTLIBException {
final Clause unsat = mEngine.getProof();
if (unsat == null) {
if (mDDFriendly)
{
System.exit(5); // NOCHECKSTYLE
}
throw new SMTLIBException("Logical context not inconsistent!");
}
final Clause proof = mSolverOptions.getProofTransformation().transform(unsat);
if (Config.CHECK_PROP_PROOF
&& (proof.getSize() != 0 || !new PropProofChecker().check(proof))) {
throw new SMTLIBException("Proof incorrect");
}
return proof;
}
/**
* Get all literals currently set to true. Note that this function might
* also be called if SMTInterpol is currently in an unsat state. Then, it
* will simply return an empty array.
* @return All literals currently set to true.
* @throws SMTLIBException If the assertion stack is modified since the last
* clean state.
*/
public Term[] getSatisfiedLiterals() throws SMTLIBException {
checkAssertionStackModified();
return mEngine.getSatisfiedLiterals();
}
/**
* A helper function to be called from the debugger...
*/
@SuppressWarnings("unused")
private boolean dumpInterpolationBug(
int[] startOfSubtree, Term[] partition, Term[] ipls, int num) {
try {
final FileWriter fw = new FileWriter("iplBug.txt");
final FormulaUnLet unlet = new FormulaUnLet();
final PrintTerm outputter = new PrintTerm();
// Find and assert children
int child = num - 1;
while (child >= startOfSubtree[num]) {
outputter.append(fw, unlet.unlet(ipls[child]));
child = startOfSubtree[child] - 1;
fw.append("\nand\n");
}
// Assert node
outputter.append(fw, ((ApplicationTerm) partition[num]).
getFunction().getDefinition());
fw.append('\n');
// Assert negated interpolant
if (num != ipls.length) {
fw.append("==>\n");
outputter.append(fw, unlet.unlet(ipls[num]));
fw.append('\n');
}
fw.flush();
fw.close();
return true;
} catch (final IOException eioe) {
eioe.printStackTrace();
return false;
}
}
@Override
public Iterable<Term[]> checkAllsat(final Term[] input) {
final Literal[] lits = new Literal[input.length];
for (int i = 0; i < input.length; ++i) {
if (input[i].getSort() != getTheory().getBooleanSort()) {
throw new SMTLIBException("AllSAT over non-Boolean");
}
lits[i] = mClausifier.getCreateLiteral(input[i]);
}
return new Iterable<Term[]>() {
@Override
public Iterator<Term[]> iterator() {
return mEngine.new AllSatIterator(lits, input);
}
};
}
@Override
public Term[] findImpliedEquality(Term[] x, Term[] y)
throws SMTLIBException, UnsupportedOperationException {
if (x.length != y.length) {
throw new SMTLIBException("Different number of x's and y's");
}
if (x.length < 2) {
throw new SMTLIBException("Need at least two elements to find equality");
}
for (int i = 0; i < x.length; ++i) {
if (!x[i].getSort().isNumericSort()
|| !y[i].getSort().isNumericSort()) {
throw new SMTLIBException("Only numeric types supported");
}
}
final LBool isSat = checkSat();
if (isSat == LBool.UNSAT) {
throw new SMTLIBException("Context is inconsistent!");
}
// TODO: If we get unknown, we can nevertheless try. But quick-check
// on numerals won't work since it produces a really dull model
// since it does not allow pivoting
// if (isSat == LBool.UNKNOWN)
// // We cannot even prove satisfiability of the context. No chance to
// // prove inductivity of an equality!
// return new Term[0];
final Term[] terms = new Term[x.length + y.length];
System.arraycopy(x, 0, terms, 0, x.length);
System.arraycopy(y, 0, terms, x.length, y.length);
final Map<Term, Term> vals = getValue(terms);
final Rational x0 = (Rational) ((ConstantTerm) vals.get(x[0])).getValue();
final Rational y0 = (Rational) ((ConstantTerm) vals.get(y[0])).getValue();
Rational x1 = null, y1 = null;
for (int i = 1; i < x.length; ++i) {
x1 = (Rational) ((ConstantTerm) vals.get(x[i])).getValue();
y1 = (Rational) ((ConstantTerm) vals.get(y[i])).getValue();
if (x1.equals(x0)) {
if (!y1.equals(y0)) {
// There is no implied equality!
return new Term[0];
}
} else {
break;
}
}
final Rational xdiff = x0.sub(x1);
if (xdiff.equals(Rational.ZERO)) {
// There is no implied equality
return new Term[0];
}
Rational a = y0.subdiv(y1, xdiff);
Rational b = Rational.ONE;
Rational c = y0.mul(x1).subdiv(x0.mul(y1), xdiff);
Sort s = x[0].getSort();
// Check for integers
if (x[0].getSort().getName().equals("Int")
&& y[0].getSort().getName().equals("Int")) {
if (!a.isIntegral()) {
final BigInteger denom = a.denominator();
a = a.mul(denom);
b = b.mul(denom);
c = c.mul(denom);
}
if (!c.isIntegral()) {
final BigInteger denom = c.denominator();
a = a.mul(denom);
b = b.mul(denom);
c = c.mul(denom);
}
} else if (s.getName().equals("Int")) {
s = sort("Real");
}
final Term at = a.toTerm(s), bt = b.toTerm(s), ct = c.toTerm(s);
// Check implication
if (mSolverOptions.getCheckType() == CheckType.FULL) {
// This version only works with full checks. If we forbid case
// splits, we cannot refute the disjunction created by this method.
final Term[] disj = new Term[x.length];
for (int i = 0; i < x.length; ++i) {
disj[i] = term("not", term("=", term("*", at, x[i]),
term("+", term("*", bt, y[i]), ct)));
}
try {
push(1);
assertTerm(term("or", disj));
final LBool isImplied = checkSat();
if (isImplied != LBool.UNSAT) {
return new Term[] {};
}
} finally {
pop(1);
}
} else {
// This method works for all modes
for (int i = 0; i < x.length; ++i) {
final Term neq = term("not", term("=", term("*", at, x[i]),
term("+", term("*", bt, y[i]), ct)));
try {
push(1);
assertTerm(neq);
final LBool isImplied = checkSat();
if (isImplied != LBool.UNSAT) {
return new Term[] {};
}
} finally {
pop(1);
}
}
}
return new Term[] {at, bt, ct};
}
@Override
public void declareFun(String fun, Sort[] paramSorts, Sort resultSort)
throws SMTLIBException {
final Sort realSort = resultSort.getRealSort();
if (realSort.isArraySort()
&& realSort.getArguments()[0] == getTheory().getBooleanSort()) {
throw new UnsupportedOperationException(
"SMTInterpol does not support Arrays with Boolean indices");
}
super.declareFun(fun, paramSorts, resultSort);
}
private final boolean getBooleanOption(String option) {
return ((Boolean) mOptions.get(option)).booleanValue();
}
public boolean isTerminationRequested() {
return mCancel.isTerminationRequested();
}
}