/********************* */ /*! \file smt_engine.cpp ** \verbatim ** Top contributors (to current version): ** Morgan Deters, Tim King, Andrew Reynolds ** This file is part of the CVC4 project. ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief The main entry point into the CVC4 library's SMT interface ** ** The main entry point into the CVC4 library's SMT interface. **/ #include "smt/smt_engine.h" #include #include #include #include #include #include #include #include #include #include #include "base/configuration.h" #include "base/configuration_private.h" #include "base/exception.h" #include "base/listener.h" #include "base/modal_exception.h" #include "base/output.h" #include "context/cdhashset.h" #include "context/cdlist.h" #include "context/context.h" #include "decision/decision_engine.h" #include "expr/attribute.h" #include "expr/expr.h" #include "expr/kind.h" #include "expr/metakind.h" #include "expr/node.h" #include "expr/node_builder.h" #include "expr/node_self_iterator.h" #include "options/arith_options.h" #include "options/arrays_options.h" #include "options/base_options.h" #include "options/booleans_options.h" #include "options/bv_options.h" #include "options/datatypes_options.h" #include "options/decision_mode.h" #include "options/decision_options.h" #include "options/main_options.h" #include "options/open_ostream.h" #include "options/option_exception.h" #include "options/printer_options.h" #include "options/proof_options.h" #include "options/prop_options.h" #include "options/quantifiers_options.h" #include "options/sep_options.h" #include "options/set_language.h" #include "options/smt_options.h" #include "options/strings_options.h" #include "options/theory_options.h" #include "options/uf_options.h" #include "preproc/preprocessing_pass_registry.h" #include "preproc/preprocessing_passes_core.h" #include "printer/printer.h" #include "proof/proof.h" #include "proof/proof_manager.h" #include "proof/theory_proof.h" #include "proof/unsat_core.h" #include "prop/prop_engine.h" #include "smt/command.h" #include "smt/command_list.h" #include "smt/logic_request.h" #include "smt/managed_ostreams.h" #include "smt/smt_engine_scope.h" #include "smt/term_formula_removal.h" #include "smt/update_ostream.h" #include "smt_util/boolean_simplification.h" #include "smt_util/nary_builder.h" #include "smt_util/node_visitor.h" #include "theory/arith/pseudoboolean_proc.h" #include "theory/booleans/circuit_propagator.h" #include "theory/bv/bvintropow2.h" #include "theory/bv/theory_bv_rewriter.h" #include "theory/logic_info.h" #include "theory/quantifiers/ce_guided_instantiation.h" #include "theory/quantifiers/fun_def_process.h" #include "theory/quantifiers/macros.h" #include "theory/quantifiers/quantifiers_rewriter.h" #include "theory/quantifiers/term_database.h" #include "theory/sort_inference.h" #include "theory/strings/theory_strings.h" #include "theory/substitutions.h" #include "theory/theory_engine.h" #include "theory/theory_model.h" #include "theory/theory_traits.h" #include "util/hash.h" #include "util/proof.h" #include "util/resource_manager.h" #include "preproc/preprocessing_pass.h" using namespace std; using namespace CVC4; using namespace CVC4::smt; using namespace CVC4::prop; using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::preproc; namespace CVC4 { namespace smt { struct DeleteCommandFunction : public std::unary_function { void operator()(const Command* command) { delete command; } }; void DeleteAndClearCommandVector(std::vector& commands) { std::for_each(commands.begin(), commands.end(), DeleteCommandFunction()); commands.clear(); } /** Useful for counting the number of recursive calls. */ /** * Representation of a defined function. We keep these around in * SmtEngine to permit expanding definitions late (and lazily), to * support getValue() over defined functions, to support user output * in terms of defined functions, etc. */ /* * AssertionPipeline, ScopeCounter, and DefinedFunction classes * moved to preprocessing_pass.h */ struct SmtEngineStatistics { /** time spent in checkModel() */ TimerStat d_checkModelTime; /** time spent in checkProof() */ TimerStat d_checkProofTime; /** time spent in checkUnsatCore() */ TimerStat d_checkUnsatCoreTime; /** time spent in PropEngine::checkSat() */ TimerStat d_solveTime; /** time spent in pushing/popping */ TimerStat d_pushPopTime; /** time spent in processAssertions() */ TimerStat d_processAssertionsTime; /** Has something simplified to false? */ IntStat d_simplifiedToFalse; /** Number of resource units spent. */ ReferenceStat d_resourceUnitsUsed; SmtEngineStatistics() : d_checkModelTime("smt::SmtEngine::checkModelTime"), d_checkProofTime("smt::SmtEngine::checkProofTime"), d_checkUnsatCoreTime("smt::SmtEngine::checkUnsatCoreTime"), d_solveTime("smt::SmtEngine::solveTime"), d_pushPopTime("smt::SmtEngine::pushPopTime"), d_processAssertionsTime("smt::SmtEngine::processAssertionsTime"), d_simplifiedToFalse("smt::SmtEngine::simplifiedToFalse", 0), d_resourceUnitsUsed("smt::SmtEngine::resourceUnitsUsed") { smtStatisticsRegistry()->registerStat(&d_checkModelTime); smtStatisticsRegistry()->registerStat(&d_checkProofTime); smtStatisticsRegistry()->registerStat(&d_checkUnsatCoreTime); smtStatisticsRegistry()->registerStat(&d_solveTime); smtStatisticsRegistry()->registerStat(&d_pushPopTime); smtStatisticsRegistry()->registerStat(&d_processAssertionsTime); smtStatisticsRegistry()->registerStat(&d_simplifiedToFalse); smtStatisticsRegistry()->registerStat(&d_resourceUnitsUsed); } ~SmtEngineStatistics() { smtStatisticsRegistry()->unregisterStat(&d_checkModelTime); smtStatisticsRegistry()->unregisterStat(&d_checkProofTime); smtStatisticsRegistry()->unregisterStat(&d_checkUnsatCoreTime); smtStatisticsRegistry()->unregisterStat(&d_solveTime); smtStatisticsRegistry()->unregisterStat(&d_pushPopTime); smtStatisticsRegistry()->unregisterStat(&d_processAssertionsTime); smtStatisticsRegistry()->unregisterStat(&d_simplifiedToFalse); smtStatisticsRegistry()->unregisterStat(&d_resourceUnitsUsed); } };/* struct SmtEngineStatistics */ class SoftResourceOutListener : public Listener { public: SoftResourceOutListener(SmtEngine& smt) : d_smt(&smt) {} virtual void notify() { SmtScope scope(d_smt); Assert(smt::smtEngineInScope()); d_smt->interrupt(); } private: SmtEngine* d_smt; }; /* class SoftResourceOutListener */ class HardResourceOutListener : public Listener { public: HardResourceOutListener(SmtEngine& smt) : d_smt(&smt) {} virtual void notify() { SmtScope scope(d_smt); theory::Rewriter::clearCaches(); } private: SmtEngine* d_smt; }; /* class HardResourceOutListener */ class SetLogicListener : public Listener { public: SetLogicListener(SmtEngine& smt) : d_smt(&smt) {} virtual void notify() { LogicInfo inOptions(options::forceLogicString()); d_smt->setLogic(inOptions); } private: SmtEngine* d_smt; }; /* class SetLogicListener */ class BeforeSearchListener : public Listener { public: BeforeSearchListener(SmtEngine& smt) : d_smt(&smt) {} virtual void notify() { d_smt->beforeSearch(); } private: SmtEngine* d_smt; }; /* class BeforeSearchListener */ class UseTheoryListListener : public Listener { public: UseTheoryListListener(TheoryEngine* theoryEngine) : d_theoryEngine(theoryEngine) {} void notify() { std::stringstream commaList(options::useTheoryList()); std::string token; Debug("UseTheoryListListener") << "UseTheoryListListener::notify() " << options::useTheoryList() << std::endl; while(std::getline(commaList, token, ',')){ if(token == "help") { puts(theory::useTheoryHelp); exit(1); } if(theory::useTheoryValidate(token)) { d_theoryEngine->enableTheoryAlternative(token); } else { throw OptionException( std::string("unknown option for --use-theory : `") + token + "'. Try --use-theory=help."); } } } private: TheoryEngine* d_theoryEngine; }; /* class UseTheoryListListener */ class SetDefaultExprDepthListener : public Listener { public: virtual void notify() { int depth = options::defaultExprDepth(); Debug.getStream() << expr::ExprSetDepth(depth); Trace.getStream() << expr::ExprSetDepth(depth); Notice.getStream() << expr::ExprSetDepth(depth); Chat.getStream() << expr::ExprSetDepth(depth); Message.getStream() << expr::ExprSetDepth(depth); Warning.getStream() << expr::ExprSetDepth(depth); // intentionally exclude Dump stream from this list } }; class SetDefaultExprDagListener : public Listener { public: virtual void notify() { int dag = options::defaultDagThresh(); Debug.getStream() << expr::ExprDag(dag); Trace.getStream() << expr::ExprDag(dag); Notice.getStream() << expr::ExprDag(dag); Chat.getStream() << expr::ExprDag(dag); Message.getStream() << expr::ExprDag(dag); Warning.getStream() << expr::ExprDag(dag); Dump.getStream() << expr::ExprDag(dag); } }; class SetPrintExprTypesListener : public Listener { public: virtual void notify() { bool value = options::printExprTypes(); Debug.getStream() << expr::ExprPrintTypes(value); Trace.getStream() << expr::ExprPrintTypes(value); Notice.getStream() << expr::ExprPrintTypes(value); Chat.getStream() << expr::ExprPrintTypes(value); Message.getStream() << expr::ExprPrintTypes(value); Warning.getStream() << expr::ExprPrintTypes(value); // intentionally exclude Dump stream from this list } }; class DumpModeListener : public Listener { public: virtual void notify() { const std::string& value = options::dumpModeString(); Dump.setDumpFromString(value); } }; class PrintSuccessListener : public Listener { public: virtual void notify() { bool value = options::printSuccess(); Debug.getStream() << Command::printsuccess(value); Trace.getStream() << Command::printsuccess(value); Notice.getStream() << Command::printsuccess(value); Chat.getStream() << Command::printsuccess(value); Message.getStream() << Command::printsuccess(value); Warning.getStream() << Command::printsuccess(value); *options::out() << Command::printsuccess(value); } }; /** * This is an inelegant solution, but for the present, it will work. * The point of this is to separate the public and private portions of * the SmtEngine class, so that smt_engine.h doesn't * include "expr/node.h", which is a private CVC4 header (and can lead * to linking errors due to the improper inlining of non-visible symbols * into user code!). * * The "real" solution (that which is usually implemented) is to move * ALL the implementation to SmtEnginePrivate and maintain a * heap-allocated instance of it in SmtEngine. SmtEngine (the public * one) becomes an "interface shell" which simply acts as a forwarder * of method calls. */ class SmtEnginePrivate : public NodeManagerListener { SmtEngine& d_smt; typedef unordered_map NodeToNodeHashMap; typedef unordered_map NodeToBoolHashMap; /** * Manager for limiting time and abstract resource usage. */ ResourceManager* d_resourceManager; /** Manager for the memory of regular-output-channel. */ ManagedRegularOutputChannel d_managedRegularChannel; /** Manager for the memory of diagnostic-output-channel. */ ManagedDiagnosticOutputChannel d_managedDiagnosticChannel; /** Manager for the memory of --dump-to. */ ManagedDumpOStream d_managedDumpChannel; /** Manager for --replay-log. */ ManagedReplayLogOstream d_managedReplayLog; /** * This list contains: * softResourceOut * hardResourceOut * setForceLogic * beforeSearchListener * UseTheoryListListener * * This needs to be deleted before both NodeManager's Options, * SmtEngine, d_resourceManager, and TheoryEngine. */ ListenerRegistrationList* d_listenerRegistrations; /** Learned literals */ vector d_nonClausalLearnedLiterals; /** A circuit propagator for non-clausal propositional deduction */ booleans::CircuitPropagator d_propagator; bool d_propagatorNeedsFinish; std::vector d_boolVars; /** Assertions in the preprocessing pipeline */ AssertionPipeline d_assertions; /** Whether any assertions have been processed */ CDO d_assertionsProcessed; /** Index for where to store substitutions */ CDO d_substitutionsIndex; // Cached true value Node d_true; /** * A context that never pushes/pops, for use by CD structures (like * SubstitutionMaps) that should be "global". */ context::Context d_fakeContext; /** * A map of AbsractValues to their actual constants. Only used if * options::abstractValues() is on. */ SubstitutionMap d_abstractValueMap; /** * A mapping of all abstract values (actual value |-> abstract) that * we've handed out. This is necessary to ensure that we give the * same AbstractValues for the same real constants. Only used if * options::abstractValues() is on. */ NodeToNodeHashMap d_abstractValues; /** Number of calls of simplify assertions active. */ unsigned d_simplifyAssertionsDepth; /** TODO: whether certain preprocess steps are necessary */ //bool d_needsExpandDefs; public: /** * Map from skolem variables to index in d_assertions containing * corresponding introduced Boolean ite */ AssertionPipeline* getAssertions(){ return &d_assertions; } /** Instance of the ITE remover */ RemoveTermFormulas d_iteRemover; private: theory::arith::PseudoBooleanProcessor d_pbsProcessor; /** The top level substitutions */ SubstitutionMap d_topLevelSubstitutions; static const bool d_doConstantProp = true; /** * Perform non-clausal simplification of a Node. This involves * Theory implementations, but does NOT involve the SAT solver in * any way. * * Returns false if the formula simplifies to "false" */ bool simplifyAssertions() throw(TypeCheckingException, LogicException, UnsafeInterruptException); public: SmtEnginePrivate(SmtEngine& smt) : d_smt(smt), d_managedRegularChannel(), d_managedDiagnosticChannel(), d_managedDumpChannel(), d_managedReplayLog(), d_listenerRegistrations(new ListenerRegistrationList()), d_nonClausalLearnedLiterals(), d_propagator(d_nonClausalLearnedLiterals, true, true), d_propagatorNeedsFinish(false), d_assertions(), d_assertionsProcessed(smt.d_userContext, false), d_substitutionsIndex(smt.d_userContext, 0), d_fakeContext(), d_abstractValueMap(&d_fakeContext), d_abstractValues(), d_simplifyAssertionsDepth(0), //d_needsExpandDefs(true), //TODO? d_iteRemover(smt.d_userContext), d_pbsProcessor(smt.d_userContext), d_topLevelSubstitutions(smt.d_userContext) { d_smt.d_nodeManager->subscribeEvents(this); d_true = NodeManager::currentNM()->mkConst(true); d_resourceManager = NodeManager::currentResourceManager(); d_listenerRegistrations->add(d_resourceManager->registerSoftListener( new SoftResourceOutListener(d_smt))); d_listenerRegistrations->add(d_resourceManager->registerHardListener( new HardResourceOutListener(d_smt))); Options& nodeManagerOptions = NodeManager::currentNM()->getOptions(); d_listenerRegistrations->add( nodeManagerOptions.registerForceLogicListener( new SetLogicListener(d_smt), true)); // Multiple options reuse BeforeSearchListener so registration requires an // extra bit of care. // We can safely not call notify on this before search listener at // registration time. This d_smt cannot be beforeSearch at construction // time. Therefore the BeforeSearchListener is a no-op. Therefore it does // not have to be called. d_listenerRegistrations->add( nodeManagerOptions.registerBeforeSearchListener( new BeforeSearchListener(d_smt))); // These do need registration calls. d_listenerRegistrations->add( nodeManagerOptions.registerSetDefaultExprDepthListener( new SetDefaultExprDepthListener(), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetDefaultExprDagListener( new SetDefaultExprDagListener(), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetPrintExprTypesListener( new SetPrintExprTypesListener(), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetDumpModeListener( new DumpModeListener(), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetPrintSuccessListener( new PrintSuccessListener(), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetRegularOutputChannelListener( new SetToDefaultSourceListener(&d_managedRegularChannel), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetDiagnosticOutputChannelListener( new SetToDefaultSourceListener(&d_managedDiagnosticChannel), true)); d_listenerRegistrations->add( nodeManagerOptions.registerDumpToFileNameListener( new SetToDefaultSourceListener(&d_managedDumpChannel), true)); d_listenerRegistrations->add( nodeManagerOptions.registerSetReplayLogFilename( new SetToDefaultSourceListener(&d_managedReplayLog), true)); } ~SmtEnginePrivate() throw() { delete d_listenerRegistrations; if(d_propagatorNeedsFinish) { d_propagator.finish(); d_propagatorNeedsFinish = false; } d_smt.d_nodeManager->unsubscribeEvents(this); } ResourceManager* getResourceManager() { return d_resourceManager; } void spendResource(unsigned amount) throw(UnsafeInterruptException) { d_resourceManager->spendResource(amount); } void nmNotifyNewSort(TypeNode tn, uint32_t flags) { DeclareTypeCommand c(tn.getAttribute(expr::VarNameAttr()), 0, tn.toType()); if((flags & ExprManager::SORT_FLAG_PLACEHOLDER) == 0) { d_smt.addToModelCommandAndDump(c, flags); } } void nmNotifyNewSortConstructor(TypeNode tn) { DeclareTypeCommand c(tn.getAttribute(expr::VarNameAttr()), tn.getAttribute(expr::SortArityAttr()), tn.toType()); d_smt.addToModelCommandAndDump(c); } void nmNotifyNewDatatypes(const std::vector& dtts) { DatatypeDeclarationCommand c(dtts); d_smt.addToModelCommandAndDump(c); } void nmNotifyNewVar(TNode n, uint32_t flags) { DeclareFunctionCommand c(n.getAttribute(expr::VarNameAttr()), n.toExpr(), n.getType().toType()); if((flags & ExprManager::VAR_FLAG_DEFINED) == 0) { d_smt.addToModelCommandAndDump(c, flags); } if(n.getType().isBoolean() && !options::incrementalSolving()) { d_boolVars.push_back(n); } } void nmNotifyNewSkolem(TNode n, const std::string& comment, uint32_t flags) { string id = n.getAttribute(expr::VarNameAttr()); DeclareFunctionCommand c(id, n.toExpr(), n.getType().toType()); if(Dump.isOn("skolems") && comment != "") { Dump("skolems") << CommentCommand(id + " is " + comment); } if((flags & ExprManager::VAR_FLAG_DEFINED) == 0) { d_smt.addToModelCommandAndDump(c, flags, false, "skolems"); } if(n.getType().isBoolean() && !options::incrementalSolving()) { d_boolVars.push_back(n); } } void nmNotifyDeleteNode(TNode n) {} Node applySubstitutions(TNode node) const { return Rewriter::rewrite(d_topLevelSubstitutions.apply(node)); } /** * Process the assertions that have been asserted. */ void processAssertions(); /** * Process a user pop. Clears out the non-context-dependent stuff in this * SmtEnginePrivate. Necessary to clear out our assertion vectors in case * someone does a push-assert-pop without a check-sat. */ void notifyPop() { d_assertions.getSkolemMap()->clear(); d_assertions.clear(); d_nonClausalLearnedLiterals.clear(); d_assertions.setRealAssertionsEnd(0); } /** * Adds a formula to the current context. Action here depends on * the SimplificationMode (in the current Options scope); the * formula might be pushed out to the propositional layer * immediately, or it might be simplified and kept, or it might not * even be simplified. * the 2nd and 3rd arguments added for bookkeeping for proofs */ void addFormula(TNode n, bool inUnsatCore, bool inInput = true) throw(TypeCheckingException, LogicException); /** Expand definitions in n. */ Node expandDefinitions(TNode n, NodeToNodeHashMap& cache, bool expandOnly = false) throw(TypeCheckingException, LogicException, UnsafeInterruptException); /** * Simplify node "in" by expanding definitions and applying any * substitutions learned from preprocessing. */ Node simplify(TNode in) { // Substitute out any abstract values in ex. // Expand definitions. NodeToNodeHashMap cache; Node n = expandDefinitions(in, cache).toExpr(); // Make sure we've done all preprocessing, etc. Assert(d_assertions.size() == 0); return applySubstitutions(n).toExpr(); } /** * Substitute away all AbstractValues in a node. */ Node substituteAbstractValues(TNode n) { // We need to do this even if options::abstractValues() is off, // since the setting might have changed after we already gave out // some abstract values. return d_abstractValueMap.apply(n); } /** * Make a new (or return an existing) abstract value for a node. * Can only use this if options::abstractValues() is on. */ Node mkAbstractValue(TNode n) { Assert(options::abstractValues()); Node& val = d_abstractValues[n]; if(val.isNull()) { val = d_smt.d_nodeManager->mkAbstractValue(n.getType()); d_abstractValueMap.addSubstitution(val, n); } // We are supposed to ascribe types to all abstract values that go out. NodeManager* current = d_smt.d_nodeManager; Node ascription = current->mkConst(AscriptionType(n.getType().toType())); Node retval = current->mkNode(kind::APPLY_TYPE_ASCRIPTION, ascription, val); return retval; } NodeToNodeHashMap d_rewriteApplyToConstCache; Node rewriteApplyToConst(TNode n) { Trace("rewriteApplyToConst") << "rewriteApplyToConst :: " << n << std::endl; if(n.getMetaKind() == kind::metakind::CONSTANT || n.getMetaKind() == kind::metakind::VARIABLE || n.getMetaKind() == kind::metakind::NULLARY_OPERATOR) { return n; } if(d_rewriteApplyToConstCache.find(n) != d_rewriteApplyToConstCache.end()) { Trace("rewriteApplyToConst") << "in cache :: " << d_rewriteApplyToConstCache[n] << std::endl; return d_rewriteApplyToConstCache[n]; } if(n.getKind() == kind::APPLY_UF) { if(n.getNumChildren() == 1 && n[0].isConst() && n[0].getType().isInteger()) { stringstream ss; ss << n.getOperator() << "_"; if(n[0].getConst() < 0) { ss << "m" << -n[0].getConst(); } else { ss << n[0]; } Node newvar = NodeManager::currentNM()->mkSkolem( ss.str(), n.getType(), "rewriteApplyToConst skolem", NodeManager::SKOLEM_EXACT_NAME); d_rewriteApplyToConstCache[n] = newvar; Trace("rewriteApplyToConst") << "made :: " << newvar << std::endl; return newvar; } else { stringstream ss; ss << "The rewrite-apply-to-const preprocessor is currently limited;" << std::endl << "it only works if all function symbols are unary and with Integer" << std::endl << "domain, and all applications are to integer values." << std::endl << "Found application: " << n; Unhandled(ss.str()); } } NodeBuilder<> builder(n.getKind()); if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { builder << n.getOperator(); } for(unsigned i = 0; i < n.getNumChildren(); ++i) { builder << rewriteApplyToConst(n[i]); } Node rewr = builder; d_rewriteApplyToConstCache[n] = rewr; Trace("rewriteApplyToConst") << "built :: " << rewr << std::endl; return rewr; } void addUseTheoryListListener(TheoryEngine* theoryEngine){ Options& nodeManagerOptions = NodeManager::currentNM()->getOptions(); d_listenerRegistrations->add( nodeManagerOptions.registerUseTheoryListListener( new UseTheoryListListener(theoryEngine), true)); } std::ostream* getReplayLog() const { return d_managedReplayLog.getReplayLog(); } };/* class SmtEnginePrivate */ }/* namespace CVC4::smt */ SmtEngine::SmtEngine(ExprManager* em) throw() : d_context(new Context()), d_userLevels(), d_userContext(new UserContext()), d_exprManager(em), d_nodeManager(d_exprManager->getNodeManager()), d_decisionEngine(NULL), d_theoryEngine(NULL), d_propEngine(NULL), d_proofManager(NULL), d_definedFunctions(NULL), d_fmfRecFunctionsDefined(NULL), d_assertionList(NULL), d_assignments(NULL), d_modelGlobalCommands(), d_modelCommands(NULL), d_dumpCommands(), d_defineCommands(), d_logic(), d_originalOptions(), d_pendingPops(0), d_fullyInited(false), d_problemExtended(false), d_queryMade(false), d_needPostsolve(false), d_earlyTheoryPP(true), d_status(), d_replayStream(NULL), d_private(NULL), d_statisticsRegistry(NULL), d_stats(NULL), d_channels(new LemmaChannels()) { SmtScope smts(this); d_originalOptions.copyValues(em->getOptions()); d_private = new smt::SmtEnginePrivate(*this); d_statisticsRegistry = new StatisticsRegistry(); d_stats = new SmtEngineStatistics(); d_stats->d_resourceUnitsUsed.setData( d_private->getResourceManager()->getResourceUsage()); // The ProofManager is constructed before any other proof objects such as // SatProof and TheoryProofs. The TheoryProofEngine and the SatProof are // initialized in TheoryEngine and PropEngine respectively. Assert(d_proofManager == NULL); // d_proofManager must be created before Options has been finished // being parsed from the input file. Because of this, we cannot trust // that options::proof() is set correctly yet. #ifdef CVC4_PROOF d_proofManager = new ProofManager(d_userContext); #endif // We have mutual dependency here, so we add the prop engine to the theory // engine later (it is non-essential there) d_theoryEngine = new TheoryEngine(d_context, d_userContext, d_private->d_iteRemover, const_cast(d_logic), d_channels); // Add the theories for(TheoryId id = theory::THEORY_FIRST; id < theory::THEORY_LAST; ++id) { TheoryConstructor::addTheory(d_theoryEngine, id); //register with proof engine if applicable #ifdef CVC4_PROOF ProofManager::currentPM()->getTheoryProofEngine()->registerTheory(d_theoryEngine->theoryOf(id)); #endif } d_private->addUseTheoryListListener(d_theoryEngine); // global push/pop around everything, to ensure proper destruction // of context-dependent data structures d_userContext->push(); d_context->push(); d_definedFunctions = new(true) DefinedFunctionMap(d_userContext); d_fmfRecFunctionsDefined = new(true) NodeList(d_userContext); d_modelCommands = new(true) smt::CommandList(d_userContext); } void SmtEngine::finishInit() { Trace("smt-debug") << "SmtEngine::finishInit" << std::endl; // ensure that our heuristics are properly set up setDefaults(); Trace("smt-debug") << "Making decision engine..." << std::endl; d_decisionEngine = new DecisionEngine(d_context, d_userContext); d_decisionEngine->init(); // enable appropriate strategies Trace("smt-debug") << "Making prop engine..." << std::endl; d_propEngine = new PropEngine(d_theoryEngine, d_decisionEngine, d_context, d_userContext, d_private->getReplayLog(), d_replayStream, d_channels); Trace("smt-debug") << "Setting up theory engine..." << std::endl; d_theoryEngine->setPropEngine(d_propEngine); d_theoryEngine->setDecisionEngine(d_decisionEngine); Trace("smt-debug") << "Finishing init for theory engine..." << std::endl; d_theoryEngine->finishInit(); Trace("smt-debug") << "Set up assertion list..." << std::endl; // [MGD 10/20/2011] keep around in incremental mode, due to a // cleanup ordering issue and Nodes/TNodes. If SAT is popped // first, some user-context-dependent TNodes might still exist // with rc == 0. if(options::produceAssertions() || options::incrementalSolving()) { // In the case of incremental solving, we appear to need these to // ensure the relevant Nodes remain live. d_assertionList = new(true) AssertionList(d_userContext); } // dump out a set-logic command if(Dump.isOn("benchmark")) { if (Dump.isOn("raw-benchmark")) { Dump("raw-benchmark") << SetBenchmarkLogicCommand(d_logic.getLogicString()); } else { LogicInfo everything; everything.lock(); Dump("benchmark") << CommentCommand("CVC4 always dumps the most general, all-supported logic (below), as some internals might require the use of a logic more general than the input.") << SetBenchmarkLogicCommand(everything.getLogicString()); } } Trace("smt-debug") << "Dump declaration commands..." << std::endl; // dump out any pending declaration commands for(unsigned i = 0; i < d_dumpCommands.size(); ++i) { Dump("declarations") << *d_dumpCommands[i]; delete d_dumpCommands[i]; } d_dumpCommands.clear(); PROOF( ProofManager::currentPM()->setLogic(d_logic); ); PROOF({ for(TheoryId id = theory::THEORY_FIRST; id < theory::THEORY_LAST; ++id) { ProofManager::currentPM()->getTheoryProofEngine()-> finishRegisterTheory(d_theoryEngine->theoryOf(id)); } }); Trace("smt-debug") << "SmtEngine::finishInit done" << std::endl; } void SmtEngine::finalOptionsAreSet() { if(d_fullyInited) { return; } if(! d_logic.isLocked()) { setLogicInternal(); } // finish initialization, create the prop engine, etc. finishInit(); AlwaysAssert( d_propEngine->getAssertionLevel() == 0, "The PropEngine has pushed but the SmtEngine " "hasn't finished initializing!" ); d_fullyInited = true; Assert(d_logic.isLocked()); d_propEngine->assertFormula(NodeManager::currentNM()->mkConst(true)); d_propEngine->assertFormula(NodeManager::currentNM()->mkConst(false).notNode()); } void SmtEngine::shutdown() { doPendingPops(); while(options::incrementalSolving() && d_userContext->getLevel() > 1) { internalPop(true); } // check to see if a postsolve() is pending if(d_needPostsolve) { d_theoryEngine->postsolve(); d_needPostsolve = false; } if(d_propEngine != NULL) { d_propEngine->shutdown(); } if(d_theoryEngine != NULL) { d_theoryEngine->shutdown(); } if(d_decisionEngine != NULL) { d_decisionEngine->shutdown(); } } SmtEngine::~SmtEngine() throw() { SmtScope smts(this); try { shutdown(); // global push/pop around everything, to ensure proper destruction // of context-dependent data structures d_context->popto(0); d_userContext->popto(0); if(d_assignments != NULL) { d_assignments->deleteSelf(); } if(d_assertionList != NULL) { d_assertionList->deleteSelf(); } for(unsigned i = 0; i < d_dumpCommands.size(); ++i) { delete d_dumpCommands[i]; d_dumpCommands[i] = NULL; } d_dumpCommands.clear(); DeleteAndClearCommandVector(d_modelGlobalCommands); if(d_modelCommands != NULL) { d_modelCommands->deleteSelf(); } d_definedFunctions->deleteSelf(); d_fmfRecFunctionsDefined->deleteSelf(); delete d_theoryEngine; d_theoryEngine = NULL; delete d_propEngine; d_propEngine = NULL; delete d_decisionEngine; d_decisionEngine = NULL; // d_proofManager is always created when proofs are enabled at configure time. // Becuase of this, this code should not be wrapped in PROOF() which // additionally checks flags such as options::proof(). #ifdef CVC4_PROOF delete d_proofManager; d_proofManager = NULL; #endif delete d_stats; d_stats = NULL; delete d_statisticsRegistry; d_statisticsRegistry = NULL; delete d_private; d_private = NULL; delete d_userContext; d_userContext = NULL; delete d_context; d_context = NULL; delete d_channels; d_channels = NULL; } catch(Exception& e) { Warning() << "CVC4 threw an exception during cleanup." << endl << e << endl; } } void SmtEngine::setLogic(const LogicInfo& logic) throw(ModalException) { SmtScope smts(this); if(d_fullyInited) { throw ModalException("Cannot set logic in SmtEngine after the engine has " "finished initializing."); } d_logic = logic; setLogicInternal(); } void SmtEngine::setLogic(const std::string& s) throw(ModalException, LogicException) { SmtScope smts(this); try { setLogic(LogicInfo(s)); } catch(IllegalArgumentException& e) { throw LogicException(e.what()); } } void SmtEngine::setLogic(const char* logic) throw(ModalException, LogicException) { setLogic(string(logic)); } LogicInfo SmtEngine::getLogicInfo() const { return d_logic; } void SmtEngine::setLogicInternal() throw() { Assert(!d_fullyInited, "setting logic in SmtEngine but the engine has already" " finished initializing for this run"); d_logic.lock(); } void SmtEngine::setDefaults() { if(options::forceLogicString.wasSetByUser()) { d_logic = LogicInfo(options::forceLogicString()); }else if (options::solveIntAsBV() > 0) { d_logic = LogicInfo("QF_BV"); }else if (d_logic.getLogicString() == "QF_NRA" && options::solveRealAsInt()) { d_logic = LogicInfo("QF_NIA"); } else if ((d_logic.getLogicString() == "QF_UFBV" || d_logic.getLogicString() == "QF_ABV") && options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { d_logic = LogicInfo("QF_BV"); } // set strings-exp /* - disabled for 1.4 release [MGD 2014.06.25] if(!d_logic.hasEverything() && d_logic.isTheoryEnabled(THEORY_STRINGS) ) { if(! options::stringExp.wasSetByUser()) { options::stringExp.set( true ); Trace("smt") << "turning on strings-exp, for the theory of strings" << std::endl; } } */ // for strings if(options::stringExp()) { if( !d_logic.isQuantified() ) { d_logic = d_logic.getUnlockedCopy(); d_logic.enableQuantifiers(); d_logic.lock(); Trace("smt") << "turning on quantifier logic, for strings-exp" << std::endl; } if(! options::fmfBound.wasSetByUser()) { options::fmfBound.set( true ); Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; } if(! options::fmfInstEngine.wasSetByUser()) { options::fmfInstEngine.set( true ); Trace("smt") << "turning on fmf-inst-engine, for strings-exp" << std::endl; } /* if(! options::rewriteDivk.wasSetByUser()) { options::rewriteDivk.set( true ); Trace("smt") << "turning on rewrite-divk, for strings-exp" << std::endl; }*/ /* if(! options::stringFMF.wasSetByUser()) { options::stringFMF.set( true ); Trace("smt") << "turning on strings-fmf, for strings-exp" << std::endl; } */ } if(options::checkModels()) { if(! options::produceAssertions()) { Notice() << "SmtEngine: turning on produce-assertions to support " << "check-models." << endl; setOption("produce-assertions", SExpr("true")); } } if(options::unsatCores()) { if(options::simplificationMode() != SIMPLIFICATION_MODE_NONE) { if(options::simplificationMode.wasSetByUser()) { throw OptionException("simplification not supported with unsat cores"); } Notice() << "SmtEngine: turning off simplification to support unsat-cores" << endl; options::simplificationMode.set(SIMPLIFICATION_MODE_NONE); } if(options::unconstrainedSimp()) { if(options::unconstrainedSimp.wasSetByUser()) { throw OptionException("unconstrained simplification not supported with unsat cores"); } Notice() << "SmtEngine: turning off unconstrained simplification to support unsat-cores" << endl; options::unconstrainedSimp.set(false); } if(options::pbRewrites()) { if(options::pbRewrites.wasSetByUser()) { throw OptionException("pseudoboolean rewrites not supported with unsat cores"); } Notice() << "SmtEngine: turning off pseudoboolean rewrites to support unsat-cores" << endl; setOption("pb-rewrites", false); } if(options::sortInference()) { if(options::sortInference.wasSetByUser()) { throw OptionException("sort inference not supported with unsat cores"); } Notice() << "SmtEngine: turning off sort inference to support unsat-cores" << endl; options::sortInference.set(false); } if(options::preSkolemQuant()) { if(options::preSkolemQuant.wasSetByUser()) { throw OptionException("pre-skolemization not supported with unsat cores"); } Notice() << "SmtEngine: turning off pre-skolemization to support unsat-cores" << endl; options::preSkolemQuant.set(false); } if(options::bitvectorToBool()) { if(options::bitvectorToBool.wasSetByUser()) { throw OptionException("bv-to-bool not supported with unsat cores"); } Notice() << "SmtEngine: turning off bitvector-to-bool to support unsat-cores" << endl; options::bitvectorToBool.set(false); } if(options::boolToBitvector()) { if(options::boolToBitvector.wasSetByUser()) { throw OptionException("bool-to-bv not supported with unsat cores"); } Notice() << "SmtEngine: turning off bool-to-bitvector to support unsat-cores" << endl; options::boolToBitvector.set(false); } if(options::bvIntroducePow2()) { if(options::bvIntroducePow2.wasSetByUser()) { throw OptionException("bv-intro-pow2 not supported with unsat cores"); } Notice() << "SmtEngine: turning off bv-introduce-pow2 to support unsat-cores" << endl; setOption("bv-intro-pow2", false); } if(options::repeatSimp()) { if(options::repeatSimp.wasSetByUser()) { throw OptionException("repeat-simp not supported with unsat cores"); } Notice() << "SmtEngine: turning off repeat-simp to support unsat-cores" << endl; setOption("repeat-simp", false); } } if(options::produceAssignments() && !options::produceModels()) { Notice() << "SmtEngine: turning on produce-models to support produce-assignments" << endl; setOption("produce-models", SExpr("true")); } // Set the options for the theoryOf if(!options::theoryOfMode.wasSetByUser()) { if(d_logic.isSharingEnabled() && !d_logic.isTheoryEnabled(THEORY_BV) && !d_logic.isTheoryEnabled(THEORY_STRINGS) && !d_logic.isTheoryEnabled(THEORY_SETS) ) { Trace("smt") << "setting theoryof-mode to term-based" << endl; options::theoryOfMode.set(THEORY_OF_TERM_BASED); } } // strings require LIA, UF; widen the logic if(d_logic.isTheoryEnabled(THEORY_STRINGS)) { LogicInfo log(d_logic.getUnlockedCopy()); // Strings requires arith for length constraints, and also UF if(!d_logic.isTheoryEnabled(THEORY_UF)) { Trace("smt") << "because strings are enabled, also enabling UF" << endl; log.enableTheory(THEORY_UF); } if(!d_logic.isTheoryEnabled(THEORY_ARITH) || d_logic.isDifferenceLogic() || !d_logic.areIntegersUsed()) { Trace("smt") << "because strings are enabled, also enabling linear integer arithmetic" << endl; log.enableTheory(THEORY_ARITH); log.enableIntegers(); log.arithOnlyLinear(); } d_logic = log; d_logic.lock(); } if(d_logic.isTheoryEnabled(THEORY_ARRAY) || d_logic.isTheoryEnabled(THEORY_DATATYPES) || d_logic.isTheoryEnabled(THEORY_SETS)) { if(!d_logic.isTheoryEnabled(THEORY_UF)) { LogicInfo log(d_logic.getUnlockedCopy()); Trace("smt") << "because a theory that permits Boolean terms is enabled, also enabling UF" << endl; log.enableTheory(THEORY_UF); d_logic = log; d_logic.lock(); } } // by default, symmetry breaker is on only for QF_UF if(! options::ufSymmetryBreaker.wasSetByUser()) { bool qf_uf = d_logic.isPure(THEORY_UF) && !d_logic.isQuantified() && !options::proof(); Trace("smt") << "setting uf symmetry breaker to " << qf_uf << endl; options::ufSymmetryBreaker.set(qf_uf); } // by default, nonclausal simplification is off for QF_SAT if(! options::simplificationMode.wasSetByUser()) { bool qf_sat = d_logic.isPure(THEORY_BOOL) && !d_logic.isQuantified(); Trace("smt") << "setting simplification mode to <" << d_logic.getLogicString() << "> " << (!qf_sat) << endl; //simplification=none works better for SMT LIB benchmarks with quantifiers, not others //options::simplificationMode.set(qf_sat || quantifiers ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); options::simplificationMode.set(qf_sat ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); } // If in arrays, set the UF handler to arrays if(d_logic.isTheoryEnabled(THEORY_ARRAY) && ( !d_logic.isQuantified() || (d_logic.isQuantified() && !d_logic.isTheoryEnabled(THEORY_UF)))) { Theory::setUninterpretedSortOwner(THEORY_ARRAY); } else { Theory::setUninterpretedSortOwner(THEORY_UF); } // Turn on ite simplification for QF_LIA and QF_AUFBV // WARNING: These checks match much more than just QF_AUFBV and // QF_LIA logics. --K [2014/10/15] if(! options::doITESimp.wasSetByUser()) { bool qf_aufbv = !d_logic.isQuantified() && d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_UF) && d_logic.isTheoryEnabled(THEORY_BV); bool qf_lia = !d_logic.isQuantified() && d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areRealsUsed(); bool iteSimp = (qf_aufbv || qf_lia); Trace("smt") << "setting ite simplification to " << iteSimp << endl; options::doITESimp.set(iteSimp); } if(! options::compressItes.wasSetByUser() ){ bool qf_lia = !d_logic.isQuantified() && d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areRealsUsed(); bool compressIte = qf_lia; Trace("smt") << "setting ite compression to " << compressIte << endl; options::compressItes.set(compressIte); } if(! options::simplifyWithCareEnabled.wasSetByUser() ){ bool qf_aufbv = !d_logic.isQuantified() && d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_UF) && d_logic.isTheoryEnabled(THEORY_BV); bool withCare = qf_aufbv; Trace("smt") << "setting ite simplify with care to " << withCare << endl; options::simplifyWithCareEnabled.set(withCare); } // Turn off array eager index splitting for QF_AUFLIA if(! options::arraysEagerIndexSplitting.wasSetByUser()) { if (not d_logic.isQuantified() && d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_UF) && d_logic.isTheoryEnabled(THEORY_ARITH)) { Trace("smt") << "setting array eager index splitting to false" << endl; options::arraysEagerIndexSplitting.set(false); } } // Turn on model-based arrays for QF_AX (unless models are enabled) // if(! options::arraysModelBased.wasSetByUser()) { // if (not d_logic.isQuantified() && // d_logic.isTheoryEnabled(THEORY_ARRAY) && // d_logic.isPure(THEORY_ARRAY) && // !options::produceModels() && // !options::checkModels()) { // Trace("smt") << "turning on model-based array solver" << endl; // options::arraysModelBased.set(true); // } // } // Turn on multiple-pass non-clausal simplification for QF_AUFBV if(! options::repeatSimp.wasSetByUser()) { bool repeatSimp = !d_logic.isQuantified() && (d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_UF) && d_logic.isTheoryEnabled(THEORY_BV)) && !options::unsatCores(); Trace("smt") << "setting repeat simplification to " << repeatSimp << endl; options::repeatSimp.set(repeatSimp); } // Turn on unconstrained simplification for QF_AUFBV if(!options::unconstrainedSimp.wasSetByUser() || options::incrementalSolving()) { // bool qf_sat = d_logic.isPure(THEORY_BOOL) && !d_logic.isQuantified(); // bool uncSimp = false && !qf_sat && !options::incrementalSolving(); bool uncSimp = !options::incrementalSolving() && !d_logic.isQuantified() && !options::produceModels() && !options::produceAssignments() && !options::checkModels() && !options::unsatCores() && (d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_BV)); Trace("smt") << "setting unconstrained simplification to " << uncSimp << endl; options::unconstrainedSimp.set(uncSimp); } // Unconstrained simp currently does *not* support model generation if (options::unconstrainedSimp.wasSetByUser() && options::unconstrainedSimp()) { if (options::produceModels()) { if (options::produceModels.wasSetByUser()) { throw OptionException("Cannot use unconstrained-simp with model generation."); } Notice() << "SmtEngine: turning off produce-models to support unconstrainedSimp" << endl; setOption("produce-models", SExpr("false")); } if (options::produceAssignments()) { if (options::produceAssignments.wasSetByUser()) { throw OptionException("Cannot use unconstrained-simp with model generation (produce-assignments)."); } Notice() << "SmtEngine: turning off produce-assignments to support unconstrainedSimp" << endl; setOption("produce-assignments", SExpr("false")); } if (options::checkModels()) { if (options::checkModels.wasSetByUser()) { throw OptionException("Cannot use unconstrained-simp with model generation (check-models)."); } Notice() << "SmtEngine: turning off check-models to support unconstrainedSimp" << endl; setOption("check-models", SExpr("false")); } } if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && options::incrementalSolving()) { if (options::incrementalSolving.wasSetByUser()) { throw OptionException(std::string("Eager bit-blasting does not currently support incremental mode. \n\ Try --bitblast=lazy")); } Notice() << "SmtEngine: turning off incremental to support eager bit-blasting" << endl; setOption("incremental", SExpr("false")); } if (! options::bvEagerExplanations.wasSetByUser() && d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_BV)) { Trace("smt") << "enabling eager bit-vector explanations " << endl; options::bvEagerExplanations.set(true); } if( !options::bitvectorEqualitySolver() ){ Trace("smt") << "disabling bvLazyRewriteExtf since equality solver is disabled" << endl; options::bvLazyRewriteExtf.set(false); } // Turn on arith rewrite equalities only for pure arithmetic if(! options::arithRewriteEq.wasSetByUser()) { bool arithRewriteEq = d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isQuantified(); Trace("smt") << "setting arith rewrite equalities " << arithRewriteEq << endl; options::arithRewriteEq.set(arithRewriteEq); } if(! options::arithHeuristicPivots.wasSetByUser()) { int16_t heuristicPivots = 5; if(d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified()) { if(d_logic.isDifferenceLogic()) { heuristicPivots = -1; } else if(!d_logic.areIntegersUsed()) { heuristicPivots = 0; } } Trace("smt") << "setting arithHeuristicPivots " << heuristicPivots << endl; options::arithHeuristicPivots.set(heuristicPivots); } if(! options::arithPivotThreshold.wasSetByUser()){ uint16_t pivotThreshold = 2; if(d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified()){ if(d_logic.isDifferenceLogic()){ pivotThreshold = 16; } } Trace("smt") << "setting arith arithPivotThreshold " << pivotThreshold << endl; options::arithPivotThreshold.set(pivotThreshold); } if(! options::arithStandardCheckVarOrderPivots.wasSetByUser()){ int16_t varOrderPivots = -1; if(d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified()){ varOrderPivots = 200; } Trace("smt") << "setting arithStandardCheckVarOrderPivots " << varOrderPivots << endl; options::arithStandardCheckVarOrderPivots.set(varOrderPivots); } // Turn off early theory preprocessing if arithRewriteEq is on if (options::arithRewriteEq()) { d_earlyTheoryPP = false; } // Set decision mode based on logic (if not set by user) if(!options::decisionMode.wasSetByUser()) { decision::DecisionMode decMode = // ALL d_logic.hasEverything() ? decision::DECISION_STRATEGY_JUSTIFICATION : ( // QF_BV (not d_logic.isQuantified() && d_logic.isPure(THEORY_BV) ) || // QF_AUFBV or QF_ABV or QF_UFBV (not d_logic.isQuantified() && (d_logic.isTheoryEnabled(THEORY_ARRAY) || d_logic.isTheoryEnabled(THEORY_UF)) && d_logic.isTheoryEnabled(THEORY_BV) ) || // QF_AUFLIA (and may be ends up enabling QF_AUFLRA?) (not d_logic.isQuantified() && d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_UF) && d_logic.isTheoryEnabled(THEORY_ARITH) ) || // QF_LRA (not d_logic.isQuantified() && d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areIntegersUsed() ) || // Quantifiers d_logic.isQuantified() || // Strings d_logic.isTheoryEnabled(THEORY_STRINGS) ? decision::DECISION_STRATEGY_JUSTIFICATION : decision::DECISION_STRATEGY_INTERNAL ); bool stoponly = // ALL d_logic.hasEverything() || d_logic.isTheoryEnabled(THEORY_STRINGS) ? false : ( // QF_AUFLIA (not d_logic.isQuantified() && d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_UF) && d_logic.isTheoryEnabled(THEORY_ARITH) ) || // QF_LRA (not d_logic.isQuantified() && d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areIntegersUsed() ) ? true : false ); Trace("smt") << "setting decision mode to " << decMode << endl; options::decisionMode.set(decMode); options::decisionStopOnly.set(stoponly); } if( options::incrementalSolving() ){ //disable modes not supported by incremental options::sortInference.set( false ); options::ufssFairnessMonotone.set( false ); options::quantEpr.set( false ); } if( d_logic.hasCardinalityConstraints() ){ //must have finite model finding on options::finiteModelFind.set( true ); } //if it contains a theory with non-termination, do not strictly enforce that quantifiers and theory combination must be interleaved if( d_logic.isTheoryEnabled(THEORY_STRINGS) || (d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear()) ){ if( !options::instWhenStrictInterleave.wasSetByUser() ){ options::instWhenStrictInterleave.set( false ); } } //local theory extensions if( options::localTheoryExt() ){ if( !options::instMaxLevel.wasSetByUser() ){ options::instMaxLevel.set( 0 ); } } if( options::instMaxLevel()!=-1 ){ Notice() << "SmtEngine: turning off cbqi to support instMaxLevel" << endl; options::cbqi.set(false); } //track instantiations? if( options::cbqiNestedQE() || ( options::proof() && !options::trackInstLemmas.wasSetByUser() ) ){ options::trackInstLemmas.set( true ); } if( ( options::fmfBoundLazy.wasSetByUser() && options::fmfBoundLazy() ) || ( options::fmfBoundInt.wasSetByUser() && options::fmfBoundInt() ) ) { options::fmfBound.set( true ); } //now have determined whether fmfBoundInt is on/off //apply fmfBoundInt options if( options::fmfBound() ){ //must have finite model finding on options::finiteModelFind.set( true ); if( ! options::mbqiMode.wasSetByUser() || ( options::mbqiMode()!=quantifiers::MBQI_NONE && options::mbqiMode()!=quantifiers::MBQI_FMC && options::mbqiMode()!=quantifiers::MBQI_FMC_INTERVAL ) ){ //if bounded integers are set, use no MBQI by default options::mbqiMode.set( quantifiers::MBQI_NONE ); } if( ! options::prenexQuant.wasSetByUser() ){ options::prenexQuant.set( quantifiers::PRENEX_QUANT_NONE ); } } if( options::mbqiMode()==quantifiers::MBQI_ABS ){ if( !d_logic.isPure(THEORY_UF) ){ //MBQI_ABS is only supported in pure quantified UF options::mbqiMode.set( quantifiers::MBQI_FMC ); } } if( options::ufssSymBreak() ){ options::sortInference.set( true ); } if( options::fmfFunWellDefinedRelevant() ){ if( !options::fmfFunWellDefined.wasSetByUser() ){ options::fmfFunWellDefined.set( true ); } } if( options::fmfFunWellDefined() ){ if( !options::finiteModelFind.wasSetByUser() ){ options::finiteModelFind.set( true ); } } //EPR if( options::quantEpr() ){ if( !options::preSkolemQuant.wasSetByUser() ){ options::preSkolemQuant.set( true ); } } //now, have determined whether finite model find is on/off //apply finite model finding options if( options::finiteModelFind() ){ //apply conservative quantifiers splitting if( !options::quantDynamicSplit.wasSetByUser() ){ options::quantDynamicSplit.set( quantifiers::QUANT_DSPLIT_MODE_DEFAULT ); } //do not eliminate extended arithmetic symbols from quantified formulas if( !options::elimExtArithQuant.wasSetByUser() ){ options::elimExtArithQuant.set( false ); } if( !options::eMatching.wasSetByUser() ){ options::eMatching.set( options::fmfInstEngine() ); } if( !options::instWhenMode.wasSetByUser() ){ //instantiate only on last call if( options::eMatching() ){ options::instWhenMode.set( quantifiers::INST_WHEN_LAST_CALL ); } } if( options::mbqiMode()==quantifiers::MBQI_ABS ){ if( !options::preSkolemQuant.wasSetByUser() ){ options::preSkolemQuant.set( true ); } if( !options::preSkolemQuantNested.wasSetByUser() ){ options::preSkolemQuantNested.set( true ); } if( !options::fmfOneInstPerRound.wasSetByUser() ){ options::fmfOneInstPerRound.set( true ); } } } //apply counterexample guided instantiation options if( options::cegqiSingleInvMode()!=quantifiers::CEGQI_SI_MODE_NONE ){ if( !options::ceGuidedInst.wasSetByUser() ){ options::ceGuidedInst.set( true ); } } if( options::ceGuidedInst() ){ //counterexample-guided instantiation for sygus if( !options::cegqiSingleInvMode.wasSetByUser() ){ options::cegqiSingleInvMode.set( quantifiers::CEGQI_SI_MODE_USE ); } if( !options::quantConflictFind.wasSetByUser() ){ options::quantConflictFind.set( false ); } if( !options::instNoEntail.wasSetByUser() ){ options::instNoEntail.set( false ); } //do not allow partial functions if( !options::bitvectorDivByZeroConst.wasSetByUser() ){ options::bitvectorDivByZeroConst.set( true ); } if( !options::dtRewriteErrorSel.wasSetByUser() ){ options::dtRewriteErrorSel.set( true ); } //do not miniscope if( !options::miniscopeQuant.wasSetByUser() ){ options::miniscopeQuant.set( false ); } if( !options::miniscopeQuantFreeVar.wasSetByUser() ){ options::miniscopeQuantFreeVar.set( false ); } //rewrite divk if( !options::rewriteDivk.wasSetByUser()) { options::rewriteDivk.set( true ); } //do not do macros if( !options::macrosQuant.wasSetByUser()) { options::macrosQuant.set( false ); } if( !options::cbqiPreRegInst.wasSetByUser()) { options::cbqiPreRegInst.set( true ); } } //counterexample-guided instantiation for non-sygus // enable if any possible quantifiers with arithmetic, datatypes or bitvectors if( d_logic.isQuantified() && ( ( options::decisionMode()!=decision::DECISION_STRATEGY_INTERNAL && ( d_logic.isTheoryEnabled(THEORY_ARITH) || d_logic.isTheoryEnabled(THEORY_DATATYPES) || d_logic.isTheoryEnabled(THEORY_BV) ) ) || d_logic.isPure(THEORY_ARITH) || d_logic.isPure(THEORY_BV) || options::cbqiAll() ) ){ if( !options::cbqi.wasSetByUser() ){ options::cbqi.set( true ); } } if( options::cbqi() ){ //must rewrite divk if( !options::rewriteDivk.wasSetByUser()) { options::rewriteDivk.set( true ); } if( d_logic.isPure(THEORY_ARITH) ){ options::cbqiAll.set( false ); if( !options::quantConflictFind.wasSetByUser() ){ options::quantConflictFind.set( false ); } if( !options::instNoEntail.wasSetByUser() ){ options::instNoEntail.set( false ); } if( !options::instWhenMode.wasSetByUser() && options::cbqiModel() ){ //only instantiation should happen at last call when model is avaiable options::instWhenMode.set( quantifiers::INST_WHEN_LAST_CALL ); } }else{ //only supported in pure arithmetic options::cbqiNestedQE.set(false); } } //implied options... if( options::strictTriggers() ){ if( !options::userPatternsQuant.wasSetByUser() ){ options::userPatternsQuant.set( quantifiers::USER_PAT_MODE_TRUST ); } } if( options::qcfMode.wasSetByUser() || options::qcfTConstraint() ){ options::quantConflictFind.set( true ); } if( options::cbqi() && ( options::cbqiNestedQE() || options::decisionMode()==decision::DECISION_STRATEGY_INTERNAL ) ){ //only complete with prenex = disj_normal or normal if( options::prenexQuant()<=quantifiers::PRENEX_QUANT_DISJ_NORMAL ){ options::prenexQuant.set( quantifiers::PRENEX_QUANT_DISJ_NORMAL ); } } if( options::cbqiNestedQE() ){ options::prenexQuantUser.set( true ); if( !options::preSkolemQuant.wasSetByUser() ){ options::preSkolemQuant.set( true ); } } //for induction techniques if( options::quantInduction() ){ if( !options::dtStcInduction.wasSetByUser() ){ options::dtStcInduction.set( true ); } if( !options::intWfInduction.wasSetByUser() ){ options::intWfInduction.set( true ); } } if( options::dtStcInduction() ){ //leads to unfairness FIXME if( !options::dtForceAssignment.wasSetByUser() ){ options::dtForceAssignment.set( true ); } //try to remove ITEs from quantified formulas if( !options::iteDtTesterSplitQuant.wasSetByUser() ){ options::iteDtTesterSplitQuant.set( true ); } if( !options::iteLiftQuant.wasSetByUser() ){ options::iteLiftQuant.set( quantifiers::ITE_LIFT_QUANT_MODE_ALL ); } } if( options::intWfInduction() ){ if( !options::purifyTriggers.wasSetByUser() ){ options::purifyTriggers.set( true ); } } if( options::conjectureNoFilter() ){ if( !options::conjectureFilterActiveTerms.wasSetByUser() ){ options::conjectureFilterActiveTerms.set( false ); } if( !options::conjectureFilterCanonical.wasSetByUser() ){ options::conjectureFilterCanonical.set( false ); } if( !options::conjectureFilterModel.wasSetByUser() ){ options::conjectureFilterModel.set( false ); } } if( options::conjectureGenPerRound.wasSetByUser() ){ if( options::conjectureGenPerRound()>0 ){ options::conjectureGen.set( true ); }else{ options::conjectureGen.set( false ); } } //can't pre-skolemize nested quantifiers without UF theory if( !d_logic.isTheoryEnabled(THEORY_UF) && options::preSkolemQuant() ){ if( !options::preSkolemQuantNested.wasSetByUser() ){ options::preSkolemQuantNested.set( false ); } } if( !d_logic.isTheoryEnabled(THEORY_DATATYPES) ){ options::quantDynamicSplit.set( quantifiers::QUANT_DSPLIT_MODE_NONE ); } //until bugs 371,431 are fixed if( ! options::minisatUseElim.wasSetByUser()){ //AJR: cannot use minisat elim for new implementation of sets TODO: why? if( d_logic.isTheoryEnabled(THEORY_SETS) || d_logic.isQuantified() || options::produceModels() || options::produceAssignments() || options::checkModels() ){ options::minisatUseElim.set( false ); } } else if (options::minisatUseElim()) { if (options::produceModels()) { Notice() << "SmtEngine: turning off produce-models to support minisatUseElim" << endl; setOption("produce-models", SExpr("false")); } if (options::produceAssignments()) { Notice() << "SmtEngine: turning off produce-assignments to support minisatUseElim" << endl; setOption("produce-assignments", SExpr("false")); } if (options::checkModels()) { Notice() << "SmtEngine: turning off check-models to support minisatUseElim" << endl; setOption("check-models", SExpr("false")); } } // For now, these array theory optimizations do not support model-building if (options::produceModels() || options::produceAssignments() || options::checkModels()) { options::arraysOptimizeLinear.set(false); options::arraysLazyRIntro1.set(false); } // Non-linear arithmetic does not support models unless nlExt is enabled if (d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear() && !options::nlExt() ) { if (options::produceModels()) { if(options::produceModels.wasSetByUser()) { throw OptionException("produce-model not supported with nonlinear arith"); } Warning() << "SmtEngine: turning off produce-models because unsupported for nonlinear arith" << endl; setOption("produce-models", SExpr("false")); } if (options::produceAssignments()) { if(options::produceAssignments.wasSetByUser()) { throw OptionException("produce-assignments not supported with nonlinear arith"); } Warning() << "SmtEngine: turning off produce-assignments because unsupported for nonlinear arith" << endl; setOption("produce-assignments", SExpr("false")); } if (options::checkModels()) { if(options::checkModels.wasSetByUser()) { throw OptionException("check-models not supported with nonlinear arith"); } Warning() << "SmtEngine: turning off check-models because unsupported for nonlinear arith" << endl; setOption("check-models", SExpr("false")); } } if(options::incrementalSolving() && options::proof()) { Warning() << "SmtEngine: turning off incremental solving mode (not yet supported with --proof, try --tear-down-incremental instead)" << endl; setOption("incremental", SExpr("false")); } } void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) throw(OptionException, ModalException) { SmtScope smts(this); Trace("smt") << "SMT setInfo(" << key << ", " << value << ")" << endl; if(Dump.isOn("benchmark")) { if(key == "status") { string s = value.getValue(); BenchmarkStatus status = (s == "sat") ? SMT_SATISFIABLE : ((s == "unsat") ? SMT_UNSATISFIABLE : SMT_UNKNOWN); Dump("benchmark") << SetBenchmarkStatusCommand(status); } else { Dump("benchmark") << SetInfoCommand(key, value); } } // Check for CVC4-specific info keys (prefixed with "cvc4-" or "cvc4_") if(key.length() > 5) { string prefix = key.substr(0, 5); if(prefix == "cvc4-" || prefix == "cvc4_") { string cvc4key = key.substr(5); if(cvc4key == "logic") { if(! value.isAtom()) { throw OptionException("argument to (set-info :cvc4-logic ..) must be a string"); } SmtScope smts(this); d_logic = value.getValue(); setLogicInternal(); return; } else { throw UnrecognizedOptionException(); } } } // Check for standard info keys (SMT-LIB v1, SMT-LIB v2, ...) if(key == "source" || key == "category" || key == "difficulty" || key == "notes") { // ignore these return; } else if(key == "name") { d_filename = value.getValue(); return; } else if(key == "smt-lib-version") { if( (value.isInteger() && value.getIntegerValue() == Integer(2)) || (value.isRational() && value.getRationalValue() == Rational(2)) || value.getValue() == "2" || value.getValue() == "2.0" ) { // supported SMT-LIB version if(!options::outputLanguage.wasSetByUser() && ( options::outputLanguage() == language::output::LANG_SMTLIB_V2_5 || options::outputLanguage() == language::output::LANG_SMTLIB_V2_6 )) { options::outputLanguage.set(language::output::LANG_SMTLIB_V2_0); *options::out() << language::SetLanguage(language::output::LANG_SMTLIB_V2_0); } return; } else if( (value.isRational() && value.getRationalValue() == Rational(5, 2)) || value.getValue() == "2.5" ) { // supported SMT-LIB version if(!options::outputLanguage.wasSetByUser() && options::outputLanguage() == language::output::LANG_SMTLIB_V2_0) { options::outputLanguage.set(language::output::LANG_SMTLIB_V2_5); *options::out() << language::SetLanguage(language::output::LANG_SMTLIB_V2_5); } return; } else if( (value.isRational() && value.getRationalValue() == Rational(13, 5)) || value.getValue() == "2.6" ) { // supported SMT-LIB version if(!options::outputLanguage.wasSetByUser() && options::outputLanguage() == language::output::LANG_SMTLIB_V2_0) { options::outputLanguage.set(language::output::LANG_SMTLIB_V2_6); *options::out() << language::SetLanguage(language::output::LANG_SMTLIB_V2_6); } return; } Warning() << "Warning: unsupported smt-lib-version: " << value << endl; throw UnrecognizedOptionException(); } else if(key == "status") { string s; if(value.isAtom()) { s = value.getValue(); } if(s != "sat" && s != "unsat" && s != "unknown") { throw OptionException("argument to (set-info :status ..) must be " "`sat' or `unsat' or `unknown'"); } d_status = Result(s, d_filename); return; } throw UnrecognizedOptionException(); } CVC4::SExpr SmtEngine::getInfo(const std::string& key) const { SmtScope smts(this); Trace("smt") << "SMT getInfo(" << key << ")" << endl; if(key == "all-statistics") { vector stats; for(StatisticsRegistry::const_iterator i = NodeManager::fromExprManager(d_exprManager)->getStatisticsRegistry()->begin(); i != NodeManager::fromExprManager(d_exprManager)->getStatisticsRegistry()->end(); ++i) { vector v; v.push_back((*i).first); v.push_back((*i).second); stats.push_back(v); } for(StatisticsRegistry::const_iterator i = d_statisticsRegistry->begin(); i != d_statisticsRegistry->end(); ++i) { vector v; v.push_back((*i).first); v.push_back((*i).second); stats.push_back(v); } return SExpr(stats); } else if(key == "error-behavior") { // immediate-exit | continued-execution if( options::continuedExecution() || options::interactive() ) { return SExpr(SExpr::Keyword("continued-execution")); } else { return SExpr(SExpr::Keyword("immediate-exit")); } } else if(key == "name") { return SExpr(Configuration::getName()); } else if(key == "version") { return SExpr(Configuration::getVersionString()); } else if(key == "authors") { return SExpr(Configuration::about()); } else if(key == "status") { // sat | unsat | unknown switch(d_status.asSatisfiabilityResult().isSat()) { case Result::SAT: return SExpr(SExpr::Keyword("sat")); case Result::UNSAT: return SExpr(SExpr::Keyword("unsat")); default: return SExpr(SExpr::Keyword("unknown")); } } else if(key == "reason-unknown") { if(!d_status.isNull() && d_status.isUnknown()) { stringstream ss; ss << d_status.whyUnknown(); string s = ss.str(); transform(s.begin(), s.end(), s.begin(), ::tolower); return SExpr(SExpr::Keyword(s)); } else { throw ModalException("Can't get-info :reason-unknown when the " "last result wasn't unknown!"); } } else if(key == "assertion-stack-levels") { AlwaysAssert(d_userLevels.size() <= std::numeric_limits::max()); return SExpr(static_cast(d_userLevels.size())); } else if(key == "all-options") { // get the options, like all-statistics std::vector< std::vector > current_options = Options::current()->getOptions(); return SExpr::parseListOfListOfAtoms(current_options); } else { throw UnrecognizedOptionException(); } } void SmtEngine::defineFunction(Expr func, const std::vector& formals, Expr formula) { SmtScope smts(this); doPendingPops(); Trace("smt") << "SMT defineFunction(" << func << ")" << endl; for(std::vector::const_iterator i = formals.begin(); i != formals.end(); ++i) { if((*i).getKind() != kind::BOUND_VARIABLE) { stringstream ss; ss << "All formal arguments to defined functions must be BOUND_VARIABLEs, but in the\n" << "definition of function " << func << ", formal\n" << " " << *i << "\n" << "has kind " << (*i).getKind(); throw TypeCheckingException(func, ss.str()); } } stringstream ss; ss << language::SetLanguage(language::SetLanguage::getLanguage(Dump.getStream())) << func; DefineFunctionCommand c(ss.str(), func, formals, formula); addToModelCommandAndDump(c, ExprManager::VAR_FLAG_DEFINED, true, "declarations"); PROOF( if (options::checkUnsatCores()) { d_defineCommands.push_back(c.clone()); }); // Substitute out any abstract values in formula Expr form = d_private->substituteAbstractValues(Node::fromExpr(formula)).toExpr(); // type check body Type formulaType = form.getType(options::typeChecking()); Type funcType = func.getType(); // We distinguish here between definitions of constants and functions, // because the type checking for them is subtly different. Perhaps we // should instead have SmtEngine::defineFunction() and // SmtEngine::defineConstant() for better clarity, although then that // doesn't match the SMT-LIBv2 standard... if(formals.size() > 0) { Type rangeType = FunctionType(funcType).getRangeType(); if(! formulaType.isComparableTo(rangeType)) { stringstream ss; ss << "Type of defined function does not match its declaration\n" << "The function : " << func << "\n" << "Declared type : " << rangeType << "\n" << "The body : " << formula << "\n" << "Body type : " << formulaType; throw TypeCheckingException(func, ss.str()); } } else { if(! formulaType.isComparableTo(funcType)) { stringstream ss; ss << "Declared type of defined constant does not match its definition\n" << "The constant : " << func << "\n" << "Declared type : " << funcType << " " << Type::getTypeNode(funcType)->getId() << "\n" << "The definition : " << formula << "\n" << "Definition type: " << formulaType << " " << Type::getTypeNode(formulaType)->getId(); throw TypeCheckingException(func, ss.str()); } } TNode funcNode = func.getTNode(); vector formalsNodes; for(vector::const_iterator i = formals.begin(), iend = formals.end(); i != iend; ++i) { formalsNodes.push_back((*i).getNode()); } TNode formNode = form.getTNode(); DefinedFunction def(funcNode, formalsNodes, formNode); // Permit (check-sat) (define-fun ...) (get-value ...) sequences. // Otherwise, (check-sat) (get-value ((! foo :named bar))) breaks // d_haveAdditions = true; Debug("smt") << "definedFunctions insert " << funcNode << " " << formNode << endl; d_definedFunctions->insert(funcNode, def); } bool SmtEngine::isDefinedFunction( Expr func ){ Node nf = Node::fromExpr( func ); Debug("smt") << "isDefined function " << nf << "?" << std::endl; return d_definedFunctions->find(nf) != d_definedFunctions->end(); } Node SmtEnginePrivate::expandDefinitions(TNode n, unordered_map& cache, bool expandOnly) throw(TypeCheckingException, LogicException, UnsafeInterruptException) { stack< triple > worklist; stack result; worklist.push(make_triple(Node(n), Node(n), false)); // The worklist is made of triples, each is input / original node then the output / rewritten node // and finally a flag tracking whether the children have been explored (i.e. if this is a downward // or upward pass). do { spendResource(options::preprocessStep()); n = worklist.top().first; // n is the input / original Node node = worklist.top().second; // node is the output / result bool childrenPushed = worklist.top().third; worklist.pop(); // Working downwards if(!childrenPushed) { Kind k = n.getKind(); // Apart from apply, we can short circuit leaves if(k != kind::APPLY && n.getNumChildren() == 0) { SmtEngine::DefinedFunctionMap::const_iterator i = d_smt.d_definedFunctions->find(n); if(i != d_smt.d_definedFunctions->end()) { // replacement must be closed if((*i).second.getFormals().size() > 0) { result.push(d_smt.d_nodeManager->mkNode(kind::LAMBDA, d_smt.d_nodeManager->mkNode(kind::BOUND_VAR_LIST, (*i).second.getFormals()), (*i).second.getFormula())); continue; } // don't bother putting in the cache result.push((*i).second.getFormula()); continue; } // don't bother putting in the cache result.push(n); continue; } // maybe it's in the cache unordered_map::iterator cacheHit = cache.find(n); if(cacheHit != cache.end()) { TNode ret = (*cacheHit).second; result.push(ret.isNull() ? n : ret); continue; } // otherwise expand it bool doExpand = k == kind::APPLY; if( !doExpand ){ if( options::macrosQuant() ){ //expand if we have inferred an operator corresponds to a defined function doExpand = k==kind::APPLY_UF && d_smt.isDefinedFunction( n.getOperator().toExpr() ); } } if (doExpand) { vector formals; TNode fm; if( n.getOperator().getKind() == kind::LAMBDA ){ TNode op = n.getOperator(); // lambda for( unsigned i=0; ifind(func); if(i == d_smt.d_definedFunctions->end()) { throw TypeCheckingException(n.toExpr(), string("Undefined function: `") + func.toString() + "'"); } DefinedFunction def = (*i).second; formals = def.getFormals(); if(Debug.isOn("expand")) { Debug("expand") << "found: " << n << endl; Debug("expand") << " func: " << func << endl; string name = func.getAttribute(expr::VarNameAttr()); Debug("expand") << " : \"" << name << "\"" << endl; } if(Debug.isOn("expand")) { Debug("expand") << " defn: " << def.getFunction() << endl << " ["; if(formals.size() > 0) { copy( formals.begin(), formals.end() - 1, ostream_iterator(Debug("expand"), ", ") ); Debug("expand") << formals.back(); } Debug("expand") << "]" << endl << " " << def.getFunction().getType() << endl << " " << def.getFormula() << endl; } fm = def.getFormula(); } Node instance = fm.substitute(formals.begin(), formals.end(), n.begin(), n.end()); Debug("expand") << "made : " << instance << endl; Node expanded = expandDefinitions(instance, cache, expandOnly); cache[n] = (n == expanded ? Node::null() : expanded); result.push(expanded); continue; } else if(! expandOnly) { // do not do any theory stuff if expandOnly is true theory::Theory* t = d_smt.d_theoryEngine->theoryOf(node); Assert(t != NULL); LogicRequest req(d_smt); node = t->expandDefinition(req, n); } // there should be children here, otherwise we short-circuited a result-push/continue, above if (node.getNumChildren() == 0) { Debug("expand") << "Unexpectedly no children..." << node << endl; } // This invariant holds at the moment but it is concievable that a new theory // might introduce a kind which can have children before definition expansion but doesn't // afterwards. If this happens, remove this assertion. Assert(node.getNumChildren() > 0); // the partial functions can fall through, in which case we still // consider their children worklist.push(make_triple(Node(n), node, true)); // Original and rewritten result for(size_t i = 0; i < node.getNumChildren(); ++i) { worklist.push(make_triple(node[i], node[i], false)); // Rewrite the children of the result only } } else { // Working upwards // Reconstruct the node from it's (now rewritten) children on the stack Debug("expand") << "cons : " << node << endl; //cout << "cons : " << node << endl; NodeBuilder<> nb(node.getKind()); if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { Debug("expand") << "op : " << node.getOperator() << endl; //cout << "op : " << node.getOperator() << endl; nb << node.getOperator(); } for(size_t i = 0; i < node.getNumChildren(); ++i) { Assert(!result.empty()); Node expanded = result.top(); result.pop(); //cout << "exchld : " << expanded << endl; Debug("expand") << "exchld : " << expanded << endl; nb << expanded; } node = nb; cache[n] = n == node ? Node::null() : node; // Only cache once all subterms are expanded result.push(node); } } while(!worklist.empty()); AlwaysAssert(result.size() == 1); return result.top(); } typedef std::unordered_map NodeMap; // do dumping (before/after any preprocessing pass) static void dumpAssertions(const char* key, const AssertionPipeline& assertionList) { if( Dump.isOn("assertions") && Dump.isOn(string("assertions:") + key) ) { // Push the simplified assertions to the dump output stream for(unsigned i = 0; i < assertionList.size(); ++ i) { TNode n = assertionList[i]; Dump("assertions") << AssertCommand(Expr(n.toExpr())); } } } // returns false if simplification led to "false" bool SmtEnginePrivate::simplifyAssertions() throw(TypeCheckingException, LogicException, UnsafeInterruptException) { spendResource(options::preprocessStep()); Assert(d_smt.d_pendingPops == 0); try { ScopeCounter depth(d_simplifyAssertionsDepth); if(options::simplificationMode() != SIMPLIFICATION_MODE_NONE) { // Perform non-clausal simplification bool noConflict = (PreprocessingPassRegistry::getInstance()->getPass("nonClausalSimplification")->apply(&d_assertions)).d_noConflict; if(!noConflict) { return false; } // We piggy-back off of the BackEdgesMap in the CircuitPropagator to // do the miplib trick. if( // check that option is on options::arithMLTrick() && // miplib rewrites aren't safe in incremental mode ! options::incrementalSolving() && // only useful in arith d_smt.d_logic.isTheoryEnabled(THEORY_ARITH) && // we add new assertions and need this (in practice, this // restriction only disables miplib processing during // re-simplification, which we don't expect to be useful anyway) d_assertions.getRealAssertionsEnd() == d_assertions.size() ) { PreprocessingPassRegistry::getInstance()->getPass("miplibTrick")->apply(&d_assertions); } else { Trace("simplify") << "SmtEnginePrivate::simplify(): " << "skipping miplib pseudobooleans pass (either incrementalSolving is on, or miplib pbs are turned off)..." << endl; } } Debug("smt") << " d_assertions : " << d_assertions.size() << endl; // before ppRewrite check if only core theory for BV theory d_smt.d_theoryEngine->staticInitializeBVOptions(d_assertions.ref()); // Theory preprocessing if (d_smt.d_earlyTheoryPP) { PreprocessingPassRegistry::getInstance()->getPass("earlyTheory")->apply(&d_assertions); } Debug("smt") << " d_assertions : " << d_assertions.size() << endl; // ITE simplification if(options::doITESimp() && (d_simplifyAssertionsDepth <= 1 || options::doITESimpOnRepeat())) { bool noConflict = (PreprocessingPassRegistry::getInstance()->getPass("simpITE")->apply(&d_assertions)).d_noConflict; if(!noConflict){ Chat() << "...ITE simplification found unsat..." << endl; return false; } } Debug("smt") << " d_assertions : " << d_assertions.size() << endl; // Unconstrained simplification if(options::unconstrainedSimp()) { PreprocessingPassRegistry::getInstance()->getPass("unconstrainedSimp")->apply(&d_assertions); } Debug("smt") << " d_assertions : " << d_assertions.size() << endl; if(options::repeatSimp() && options::simplificationMode() != SIMPLIFICATION_MODE_NONE) { bool noConflict = (PreprocessingPassRegistry::getInstance()->getPass("nonClausalSimplification")->apply(&d_assertions)).d_noConflict; if(!noConflict) { return false; } } Debug("smt") << " d_assertions : " << d_assertions.size() << endl; } catch(TypeCheckingExceptionPrivate& tcep) { // Calls to this function should have already weeded out any // typechecking exceptions via (e.g.) ensureBoolean(). But a // theory could still create a new expression that isn't // well-typed, and we don't want the C++ runtime to abort our // process without any error notice. stringstream ss; ss << "A bad expression was produced. Original exception follows:\n" << tcep; InternalError(ss.str().c_str()); } return true; } Result SmtEngine::check() { Assert(d_fullyInited); Assert(d_pendingPops == 0); Trace("smt") << "SmtEngine::check()" << endl; ResourceManager* resourceManager = d_private->getResourceManager(); resourceManager->beginCall(); // Only way we can be out of resource is if cumulative budget is on if (resourceManager->cumulativeLimitOn() && resourceManager->out()) { Result::UnknownExplanation why = resourceManager->outOfResources() ? Result::RESOURCEOUT : Result::TIMEOUT; return Result(Result::VALIDITY_UNKNOWN, why, d_filename); } // Make sure the prop layer has all of the assertions Trace("smt") << "SmtEngine::check(): processing assertions" << endl; d_private->processAssertions(); Trace("smt") << "SmtEngine::check(): done processing assertions" << endl; // Turn off stop only for QF_LRA // TODO: Bring up in a meeting where to put this if(options::decisionStopOnly() && !options::decisionMode.wasSetByUser() ){ if( // QF_LRA (not d_logic.isQuantified() && d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areIntegersUsed() )){ if(d_private->getAssertions()->getSkolemMap()->empty()){ options::decisionStopOnly.set(false); d_decisionEngine->clearStrategies(); Trace("smt") << "SmtEngine::check(): turning off stop only" << endl; } } } TimerStat::CodeTimer solveTimer(d_stats->d_solveTime); Chat() << "solving..." << endl; Trace("smt") << "SmtEngine::check(): running check" << endl; Result result = d_propEngine->checkSat(); resourceManager->endCall(); Trace("limit") << "SmtEngine::check(): cumulative millis " << resourceManager->getTimeUsage() << ", resources " << resourceManager->getResourceUsage() << endl; return Result(result, d_filename); } Result SmtEngine::quickCheck() { Assert(d_fullyInited); Trace("smt") << "SMT quickCheck()" << endl; return Result(Result::VALIDITY_UNKNOWN, Result::REQUIRES_FULL_CHECK, d_filename); } void SmtEnginePrivate::processAssertions() { PreprocessingPassRegistry::getInstance()->init(&d_smt, d_smt.d_theoryEngine, &d_topLevelSubstitutions, &d_pbsProcessor, &d_iteRemover, d_smt.d_decisionEngine, d_smt.d_propEngine, &d_propagatorNeedsFinish, &d_propagator, &d_boolVars, &d_substitutionsIndex, &d_nonClausalLearnedLiterals); TimerStat::CodeTimer paTimer(d_smt.d_stats->d_processAssertionsTime); spendResource(options::preprocessStep()); Assert(d_smt.d_fullyInited); Assert(d_smt.d_pendingPops == 0); // Dump the assertions dumpAssertions("pre-everything", d_assertions); Trace("smt-proc") << "SmtEnginePrivate::processAssertions() begin" << endl; Trace("smt") << "SmtEnginePrivate::processAssertions()" << endl; Debug("smt") << " d_assertions : " << d_assertions.size() << endl; if (d_assertions.size() == 0) { // nothing to do return; } if (d_assertionsProcessed && options::incrementalSolving()) { // Placeholder for storing substitutions d_substitutionsIndex = d_assertions.size(); d_assertions.push_back(NodeManager::currentNM()->mkConst(true)); } // Add dummy assertion in last position - to be used as a // placeholder for any new assertions to get added d_assertions.push_back(NodeManager::currentNM()->mkConst(true)); // any assertions added beyond realAssertionsEnd must NOT affect the // equisatisfiability d_assertions.setRealAssertionsEnd(d_assertions.size()); // Assertions are NOT guaranteed to be rewritten by this point { PreprocessingPassRegistry::getInstance()->getPass("expandingDefinitions")->apply(&d_assertions); } // save the assertions now THEORY_PROOF ( for (unsigned i = 0; i < d_assertions.size(); ++i) { ProofManager::currentPM()->addAssertion(d_assertions[i].toExpr()); } ); Debug("smt") << " d_assertions : " << d_assertions.size() << endl; if( options::nlExtPurify() ){ PreprocessingPassRegistry::getInstance()->getPass("nlExtPurify")->apply(&d_assertions); } if( options::ceGuidedInst() ){ //register sygus conjecture pre-rewrite (motivated by solution reconstruction) PreprocessingPassRegistry::getInstance()->getPass("ceGuidedInst")->apply(&d_assertions); } if (options::solveRealAsInt()) { PreprocessingPassRegistry::getInstance()->getPass("solveRealAsInt")->apply(&d_assertions); } if (options::solveIntAsBV() > 0) { PreprocessingPassRegistry::getInstance()->getPass("solveIntAsbv")->apply(&d_assertions); } if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && !d_smt.d_logic.isPure(THEORY_BV) && d_smt.d_logic.getLogicString() != "QF_UFBV" && d_smt.d_logic.getLogicString() != "QF_ABV") { throw ModalException("Eager bit-blasting does not currently support theory combination. " "Note that in a QF_BV problem UF symbols can be introduced for division. " "Try --bv-div-zero-const to interpret division by zero as a constant."); } if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { PreprocessingPassRegistry::getInstance()->getPass("bitBlastMode")->apply(&d_assertions); } if ( options::bvAbstraction() && !options::incrementalSolving()) { PreprocessingPassRegistry::getInstance()->getPass("bvAbstraction")->apply(&d_assertions); PreprocessingPassRegistry::getInstance()->getPass("rewrite")->apply(&d_assertions); } Debug("smt") << "d_assertions :" << d_assertions.size() << endl; bool noConflict = true; // Unconstrained simplification if(options::unconstrainedSimp()) { PreprocessingPassRegistry::getInstance()->getPass("rewrite")->apply(&d_assertions); PreprocessingPassRegistry::getInstance()->getPass("unconstrainedSimp")->apply(&d_assertions); } if(options::bvIntroducePow2()){ theory::bv::BVIntroducePow2::pow2Rewrite(d_assertions.ref()); } if(options::unsatCores()) { // special rewriting pass for unsat cores, since many of the passes below are skipped PreprocessingPassRegistry::getInstance()->getPass("rewrite")->apply(&d_assertions); } else { // Apply the substitutions we already have, and normalize //unsatCore check removed for redundancy PreprocessingPassRegistry::getInstance()->getPass("notUnsatCores")->apply(&d_assertions); } // Assertions ARE guaranteed to be rewritten by this point // Lift bit-vectors of size 1 to bool if(options::bitvectorToBool()) { PreprocessingPassRegistry::getInstance()->getPass("bvToBool")->apply(&d_assertions); PreprocessingPassRegistry::getInstance()->getPass("rewrite")->apply(&d_assertions); } // Convert non-top-level Booleans to bit-vectors of size 1 if(options::boolToBitvector()) { PreprocessingPassRegistry::getInstance()->getPass("boolTobv")->apply(&d_assertions); PreprocessingPassRegistry::getInstance()->getPass("rewrite")->apply(&d_assertions); } if(options::sepPreSkolemEmp()) { PreprocessingPassRegistry::getInstance()->getPass("sepPreSkolem")->apply(&d_assertions); } if( d_smt.d_logic.isQuantified() ){ PreprocessingPassRegistry::getInstance()->getPass("quantified")->apply(&d_assertions); } if( options::sortInference() || options::ufssFairnessMonotone() ){ //sort inference technique PreprocessingPassRegistry::getInstance()->getPass("inferenceOrFairness")->apply(&d_assertions); } if( options::pbRewrites() ){ PreprocessingPassRegistry::getInstance()->getPass("pbRewrite")->apply(&d_assertions); } noConflict = simplifyAssertions(); if(!noConflict){ ++(d_smt.d_stats->d_simplifiedToFalse); } if(options::doStaticLearning()) { PreprocessingPassRegistry::getInstance()->getPass("doStaticLearning")->apply(&d_assertions); } Debug("smt") << " d_assertions : " << d_assertions.size() << endl; { PreprocessingPassRegistry::getInstance()->getPass("removeITE")->apply(&d_assertions); } if(options::repeatSimp()) { ScopeCounter depth(d_simplifyAssertionsDepth); noConflict &= simplifyAssertions(); if (noConflict) { PreprocessingPassRegistry::getInstance()->getPass("repeatSimp")->apply(&d_assertions); // For some reason this is needed for some benchmarks, such as // http://cvc4.cs.nyu.edu/benchmarks/smtlib2/QF_AUFBV/dwp_formulas/try5_small_difret_functions_dwp_tac.re_node_set_remove_at.il.dwp.smt2 // Figure it out later PreprocessingPassRegistry::getInstance()->getPass("removeITE")->apply(&d_assertions); // Assert(iteRewriteAssertionsEnd == d_assertions.size()); } } if(options::rewriteApplyToConst()) { PreprocessingPassRegistry::getInstance()->getPass("rewriteApplyToConst")->apply(&d_assertions); } // begin: INVARIANT to maintain: no reordering of assertions or // introducing new ones #ifdef CVC4_ASSERTIONS unsigned iteRewriteAssertionsEnd = d_assertions.size(); #endif Debug("smt") << " d_assertions : " << d_assertions.size() << endl; Debug("smt") << "SmtEnginePrivate::processAssertions() POST SIMPLIFICATION" << endl; Debug("smt") << " d_assertions : " << d_assertions.size() << endl; { PreprocessingPassRegistry::getInstance()->getPass("theoryPreprocess")->apply(&d_assertions); } // If we are using eager bit-blasting wrap assertions in fake atom so that // everything gets bit-blasted to internal SAT solver if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { PreprocessingPassRegistry::getInstance()->getPass("bitBlastModeEager")->apply(&d_assertions); } //notify theory engine new preprocessed assertions d_smt.d_theoryEngine->notifyPreprocessedAssertions( d_assertions.ref() ); // Push the formula to decision engine if(noConflict) { Assert(iteRewriteAssertionsEnd == d_assertions.size()); PreprocessingPassRegistry::getInstance()->getPass("noConflict")->apply(&d_assertions); } // end: INVARIANT to maintain: no reordering of assertions or // introducing new ones Trace("smt-proc") << "SmtEnginePrivate::processAssertions() end" << endl; dumpAssertions("post-everything", d_assertions); // Push the formula to SAT { PreprocessingPassRegistry::getInstance()->getPass("cnf")->apply(&d_assertions); } d_assertionsProcessed = true; d_assertions.getSkolemMap()->clear(); d_assertions.clear(); } void SmtEnginePrivate::addFormula(TNode n, bool inUnsatCore, bool inInput) throw(TypeCheckingException, LogicException) { if (n == d_true) { // nothing to do return; } Trace("smt") << "SmtEnginePrivate::addFormula(" << n << "), inUnsatCore = " << inUnsatCore << ", inInput = " << inInput << endl; // Give it to proof manager PROOF( if( inInput ){ // n is an input assertion if (inUnsatCore || options::unsatCores() || options::dumpUnsatCores() || options::checkUnsatCores() || options::fewerPreprocessingHoles()) { ProofManager::currentPM()->addCoreAssertion(n.toExpr()); } }else{ // n is the result of an unknown preprocessing step, add it to dependency map to null ProofManager::currentPM()->addDependence(n, Node::null()); } // rewrite rules are by default in the unsat core because // they need to be applied until saturation if(options::unsatCores() && n.getKind() == kind::REWRITE_RULE ){ ProofManager::currentPM()->addUnsatCore(n.toExpr()); } ); // Add the normalized formula to the queue d_assertions.push_back(n); //d_assertions.push_back(Rewriter::rewrite(n)); } void SmtEngine::ensureBoolean(const Expr& e) throw(TypeCheckingException) { Type type = e.getType(options::typeChecking()); Type boolType = d_exprManager->booleanType(); if(type != boolType) { stringstream ss; ss << "Expected " << boolType << "\n" << "The assertion : " << e << "\n" << "Its type : " << type; throw TypeCheckingException(e, ss.str()); } } Result SmtEngine::checkSat(const Expr& ex, bool inUnsatCore) throw(TypeCheckingException, ModalException, LogicException) { return checkSatisfiability( ex, inUnsatCore, false ); }/* SmtEngine::checkSat() */ Result SmtEngine::query(const Expr& ex, bool inUnsatCore) throw(TypeCheckingException, ModalException, LogicException) { Assert(!ex.isNull()); return checkSatisfiability( ex, inUnsatCore, true ); }/* SmtEngine::query() */ Result SmtEngine::checkSatisfiability(const Expr& ex, bool inUnsatCore, bool isQuery) { try { Assert(ex.isNull() || ex.getExprManager() == d_exprManager); SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); Trace("smt") << "SmtEngine::" << (isQuery ? "query" : "checkSat") << "(" << ex << ")" << endl; if(d_queryMade && !options::incrementalSolving()) { throw ModalException("Cannot make multiple queries unless " "incremental solving is enabled " "(try --incremental)"); } Expr e; if(!ex.isNull()) { // Substitute out any abstract values in ex. e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); // Ensure expr is type-checked at this point. ensureBoolean(e); } // check to see if a postsolve() is pending if(d_needPostsolve) { d_theoryEngine->postsolve(); d_needPostsolve = false; } // Push the context internalPush(); // Note that a query has been made d_queryMade = true; // Add the formula if(!e.isNull()) { d_problemExtended = true; Expr ea = isQuery ? e.notExpr() : e; if(d_assertionList != NULL) { d_assertionList->push_back(ea); } d_private->addFormula(ea.getNode(), inUnsatCore); } Result r(Result::SAT_UNKNOWN, Result::UNKNOWN_REASON); r = isQuery ? check().asValidityResult() : check().asSatisfiabilityResult(); if ( ( options::solveRealAsInt() || options::solveIntAsBV() > 0 ) && r.asSatisfiabilityResult().isSat() == Result::UNSAT) { r = Result(Result::SAT_UNKNOWN, Result::UNKNOWN_REASON); } d_needPostsolve = true; // Dump the query if requested if(Dump.isOn("benchmark")) { // the expr already got dumped out if assertion-dumping is on if( isQuery ){ Dump("benchmark") << QueryCommand(ex); }else{ Dump("benchmark") << CheckSatCommand(ex); } } // Pop the context internalPop(); // Remember the status d_status = r; d_problemExtended = false; Trace("smt") << "SmtEngine::" << (isQuery ? "query" : "checkSat") << "(" << e << ") => " << r << endl; // Check that SAT results generate a model correctly. if(options::checkModels()) { if(r.asSatisfiabilityResult().isSat() == Result::SAT || (r.isUnknown() && r.whyUnknown() == Result::INCOMPLETE) ){ checkModel(/* hard failure iff */ ! r.isUnknown()); } } // Check that UNSAT results generate a proof correctly. if(options::checkProofs()) { if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { TimerStat::CodeTimer checkProofTimer(d_stats->d_checkProofTime); checkProof(); } } // Check that UNSAT results generate an unsat core correctly. if(options::checkUnsatCores()) { if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { TimerStat::CodeTimer checkUnsatCoreTimer(d_stats->d_checkUnsatCoreTime); checkUnsatCore(); } } return r; } catch (UnsafeInterruptException& e) { AlwaysAssert(d_private->getResourceManager()->out()); Result::UnknownExplanation why = d_private->getResourceManager()->outOfResources() ? Result::RESOURCEOUT : Result::TIMEOUT; return Result(Result::SAT_UNKNOWN, why, d_filename); } } Result SmtEngine::checkSynth(const Expr& e) throw(TypeCheckingException, ModalException, LogicException) { SmtScope smts(this); Trace("smt") << "Check synth: " << e << std::endl; Trace("smt-synth") << "Check synthesis conjecture: " << e << std::endl; Expr e_check = e; Node conj = Node::fromExpr( e ); if( conj.getKind()==kind::FORALL ){ //possibly run quantifier elimination to make formula into single invocation if( conj[1].getKind()==kind::EXISTS ){ Node conj_se = Node::fromExpr( expandDefinitions( conj[1][1].toExpr() ) ); Trace("smt-synth") << "Compute single invocation for " << conj_se << "..." << std::endl; quantifiers::SingleInvocationPartition sip; std::vector< Node > funcs; for( unsigned i=0; i qe_vars; std::vector< Node > nqe_vars; for( unsigned i=0; i orig; std::vector< Node > subs; //skolemize non-qe variables for( unsigned i=0; imkSkolem( "k", nqe_vars[i].getType(), "qe for non-ground single invocation" ); orig.push_back( nqe_vars[i] ); subs.push_back( k ); Trace("smt-synth") << " subs : " << nqe_vars[i] << " -> " << k << std::endl; } for( std::map< Node, bool >::iterator it = sip.d_funcs.begin(); it != sip.d_funcs.end(); ++it ){ orig.push_back( sip.d_func_inv[it->first] ); Node k = NodeManager::currentNM()->mkSkolem( "k", sip.d_func_fo_var[it->first].getType(), "qe for function in non-ground single invocation" ); subs.push_back( k ); Trace("smt-synth") << " subs : " << sip.d_func_inv[it->first] << " -> " << k << std::endl; } Node conj_se_ngsi = sip.getFullSpecification(); Node conj_se_ngsi_subs = conj_se_ngsi.substitute( orig.begin(), orig.end(), subs.begin(), subs.end() ); Assert( !qe_vars.empty() ); conj_se_ngsi_subs = NodeManager::currentNM()->mkNode( kind::EXISTS, NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, qe_vars ), conj_se_ngsi_subs ); Trace("smt-synth") << "Run quantifier elimination on " << conj_se_ngsi_subs << std::endl; Expr qe_res = smt_qe.doQuantifierElimination( conj_se_ngsi_subs.toExpr(), true, false ); Trace("smt-synth") << "Result : " << qe_res << std::endl; //create single invocation conjecture Node qe_res_n = Node::fromExpr( qe_res ); qe_res_n = qe_res_n.substitute( subs.begin(), subs.end(), orig.begin(), orig.end() ); if( !nqe_vars.empty() ){ qe_res_n = NodeManager::currentNM()->mkNode( kind::EXISTS, NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, nqe_vars ), qe_res_n ); } Assert( conj.getNumChildren()==3 ); qe_res_n = NodeManager::currentNM()->mkNode( kind::FORALL, conj[0], qe_res_n, conj[2] ); Trace("smt-synth") << "Converted conjecture after QE : " << qe_res_n << std::endl; e_check = qe_res_n.toExpr(); } } } return checkSatisfiability( e_check, true, false ); } Result SmtEngine::assertFormula(const Expr& ex, bool inUnsatCore) throw(TypeCheckingException, LogicException, UnsafeInterruptException) { Assert(ex.getExprManager() == d_exprManager); SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); Trace("smt") << "SmtEngine::assertFormula(" << ex << ")" << endl; if (Dump.isOn("raw-benchmark")) { Dump("raw-benchmark") << AssertCommand(ex); } // Substitute out any abstract values in ex Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); ensureBoolean(e); if(d_assertionList != NULL) { d_assertionList->push_back(e); } d_private->addFormula(e.getNode(), inUnsatCore); return quickCheck().asValidityResult(); }/* SmtEngine::assertFormula() */ Node SmtEngine::postprocess(TNode node, TypeNode expectedType) const { return node; } Expr SmtEngine::simplify(const Expr& ex) throw(TypeCheckingException, LogicException, UnsafeInterruptException) { Assert(ex.getExprManager() == d_exprManager); SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); Trace("smt") << "SMT simplify(" << ex << ")" << endl; if(Dump.isOn("benchmark")) { Dump("benchmark") << SimplifyCommand(ex); } Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); if( options::typeChecking() ) { e.getType(true); // ensure expr is type-checked at this point } // Make sure all preprocessing is done d_private->processAssertions(); Node n = d_private->simplify(Node::fromExpr(e)); n = postprocess(n, TypeNode::fromType(e.getType())); return n.toExpr(); } Expr SmtEngine::expandDefinitions(const Expr& ex) throw(TypeCheckingException, LogicException, UnsafeInterruptException) { d_private->spendResource(options::preprocessStep()); Assert(ex.getExprManager() == d_exprManager); SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); Trace("smt") << "SMT expandDefinitions(" << ex << ")" << endl; // Substitute out any abstract values in ex. Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); if(options::typeChecking()) { // Ensure expr is type-checked at this point. e.getType(true); } if(Dump.isOn("benchmark")) { Dump("benchmark") << ExpandDefinitionsCommand(e); } unordered_map cache; Node n = d_private->expandDefinitions(Node::fromExpr(e), cache, /* expandOnly = */ true); n = postprocess(n, TypeNode::fromType(e.getType())); return n.toExpr(); } Expr SmtEngine::getValue(const Expr& ex) const throw(ModalException, TypeCheckingException, LogicException, UnsafeInterruptException) { Assert(ex.getExprManager() == d_exprManager); SmtScope smts(this); Trace("smt") << "SMT getValue(" << ex << ")" << endl; if(Dump.isOn("benchmark")) { Dump("benchmark") << GetValueCommand(ex); } if(!options::produceModels()) { const char* msg = "Cannot get value when produce-models options is off."; throw ModalException(msg); } if(d_status.isNull() || d_status.asSatisfiabilityResult() == Result::UNSAT || d_problemExtended) { const char* msg = "Cannot get value unless immediately preceded by SAT/INVALID or UNKNOWN response."; //throw ModalException(msg); Warning() << CommandFailure(msg); return ex; } // Substitute out any abstract values in ex. Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); // Ensure expr is type-checked at this point. e.getType(options::typeChecking()); // do not need to apply preprocessing substitutions (should be recorded // in model already) Node n = Node::fromExpr(e); Trace("smt") << "--- getting value of " << n << endl; TypeNode expectedType = n.getType(); // Expand, then normalize unordered_map cache; n = d_private->expandDefinitions(n, cache); // There are two ways model values for terms are computed (for historical // reasons). One way is that used in check-model; the other is that // used by the Model classes. It's not clear to me exactly how these // two are different, but they need to be unified. This ugly hack here // is to fix bug 554 until we can revamp boolean-terms and models [MGD] //AJR : necessary? if(!n.getType().isFunction()) { n = Rewriter::rewrite(n); } Trace("smt") << "--- getting value of " << n << endl; TheoryModel* m = d_theoryEngine->getModel(); Node resultNode; if(m != NULL) { resultNode = m->getValue(n); } Trace("smt") << "--- got value " << n << " = " << resultNode << endl; resultNode = postprocess(resultNode, expectedType); Trace("smt") << "--- model-post returned " << resultNode << endl; Trace("smt") << "--- model-post returned " << resultNode.getType() << endl; Trace("smt") << "--- model-post expected " << expectedType << endl; // type-check the result we got Assert(resultNode.isNull() || resultNode.getType().isSubtypeOf(expectedType), "Run with -t smt for details."); // ensure it's a constant Assert(resultNode.getKind() == kind::LAMBDA || resultNode.isConst()); if(options::abstractValues() && resultNode.getType().isArray()) { resultNode = d_private->mkAbstractValue(resultNode); Trace("smt") << "--- abstract value >> " << resultNode << endl; } return resultNode.toExpr(); } bool SmtEngine::addToAssignment(const Expr& ex) { SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); // Substitute out any abstract values in ex Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); Type type = e.getType(options::typeChecking()); // must be Boolean PrettyCheckArgument( type.isBoolean(), e, "expected Boolean-typed variable or function application " "in addToAssignment()" ); Node n = e.getNode(); // must be an APPLY of a zero-ary defined function, or a variable PrettyCheckArgument( ( ( n.getKind() == kind::APPLY && ( d_definedFunctions->find(n.getOperator()) != d_definedFunctions->end() ) && n.getNumChildren() == 0 ) || n.isVar() ), e, "expected variable or defined-function application " "in addToAssignment(),\ngot %s", e.toString().c_str() ); if(!options::produceAssignments()) { return false; } if(d_assignments == NULL) { d_assignments = new(true) AssignmentSet(d_context); } d_assignments->insert(n); return true; } CVC4::SExpr SmtEngine::getAssignment() { Trace("smt") << "SMT getAssignment()" << endl; SmtScope smts(this); finalOptionsAreSet(); if(Dump.isOn("benchmark")) { Dump("benchmark") << GetAssignmentCommand(); } if(!options::produceAssignments()) { const char* msg = "Cannot get the current assignment when " "produce-assignments option is off."; throw ModalException(msg); } if(d_status.isNull() || d_status.asSatisfiabilityResult() == Result::UNSAT || d_problemExtended) { const char* msg = "Cannot get the current assignment unless immediately " "preceded by SAT/INVALID or UNKNOWN response."; //throw ModalException(msg); Warning() << CommandFailure(msg); return SExpr(vector()); } if(d_assignments == NULL) { return SExpr(vector()); } vector sexprs; TypeNode boolType = d_nodeManager->booleanType(); TheoryModel* m = d_theoryEngine->getModel(); for(AssignmentSet::key_iterator i = d_assignments->key_begin(), iend = d_assignments->key_end(); i != iend; ++i) { Assert((*i).getType() == boolType); Trace("smt") << "--- getting value of " << *i << endl; // Expand, then normalize unordered_map cache; Node n = d_private->expandDefinitions(*i, cache); n = Rewriter::rewrite(n); Trace("smt") << "--- getting value of " << n << endl; Node resultNode; if(m != NULL) { resultNode = m->getValue(n); } // type-check the result we got Assert(resultNode.isNull() || resultNode.getType() == boolType); // ensure it's a constant Assert(resultNode.isConst()); vector v; if((*i).getKind() == kind::APPLY) { Assert((*i).getNumChildren() == 0); v.push_back(SExpr(SExpr::Keyword((*i).getOperator().toString()))); } else { Assert((*i).isVar()); v.push_back(SExpr(SExpr::Keyword((*i).toString()))); } v.push_back(SExpr(SExpr::Keyword(resultNode.toString()))); sexprs.push_back(SExpr(v)); } return SExpr(sexprs); } void SmtEngine::addToModelCommandAndDump(const Command& c, uint32_t flags, bool userVisible, const char* dumpTag) { Trace("smt") << "SMT addToModelCommandAndDump(" << c << ")" << endl; SmtScope smts(this); // If we aren't yet fully inited, the user might still turn on // produce-models. So let's keep any commands around just in // case. This is useful in two cases: (1) SMT-LIBv1 auto-declares // sort "U" in QF_UF before setLogic() is run and we still want to // support finding card(U) with --finite-model-find, and (2) to // decouple SmtEngine and ExprManager if the user does a few // ExprManager::mkSort() before SmtEngine::setOption("produce-models") // and expects to find their cardinalities in the model. if(/* userVisible && */ (!d_fullyInited || options::produceModels()) && (flags & ExprManager::VAR_FLAG_DEFINED) == 0) { doPendingPops(); if(flags & ExprManager::VAR_FLAG_GLOBAL) { d_modelGlobalCommands.push_back(c.clone()); } else { d_modelCommands->push_back(c.clone()); } } if(Dump.isOn(dumpTag)) { if(d_fullyInited) { Dump(dumpTag) << c; } else { d_dumpCommands.push_back(c.clone()); } } } Model* SmtEngine::getModel() { Trace("smt") << "SMT getModel()" << endl; SmtScope smts(this); finalOptionsAreSet(); if(Dump.isOn("benchmark")) { Dump("benchmark") << GetModelCommand(); } if(d_status.isNull() || d_status.asSatisfiabilityResult() == Result::UNSAT || d_problemExtended) { const char* msg = "Cannot get the current model unless immediately " "preceded by SAT/INVALID or UNKNOWN response."; //throw ModalException(msg); Warning() << CommandFailure(msg); return NULL; } if(!options::produceModels()) { const char* msg = "Cannot get model when produce-models options is off."; throw ModalException(msg); } TheoryModel* m = d_theoryEngine->getModel(); m->d_inputName = d_filename; return m; } void SmtEngine::checkUnsatCore() { Assert(options::unsatCores(), "cannot check unsat core if unsat cores are turned off"); Notice() << "SmtEngine::checkUnsatCore(): generating unsat core" << endl; UnsatCore core = getUnsatCore(); SmtEngine coreChecker(d_exprManager); coreChecker.setLogic(getLogicInfo()); PROOF( std::vector::const_iterator itg = d_defineCommands.begin(); for (; itg != d_defineCommands.end(); ++itg) { (*itg)->invoke(&coreChecker); } ); Notice() << "SmtEngine::checkUnsatCore(): pushing core assertions (size == " << core.size() << ")" << endl; for(UnsatCore::iterator i = core.begin(); i != core.end(); ++i) { Notice() << "SmtEngine::checkUnsatCore(): pushing core member " << *i << endl; coreChecker.assertFormula(*i); } const bool checkUnsatCores = options::checkUnsatCores(); Result r; try { options::checkUnsatCores.set(false); options::checkProofs.set(false); r = coreChecker.checkSat(); } catch(...) { options::checkUnsatCores.set(checkUnsatCores); throw; } Notice() << "SmtEngine::checkUnsatCore(): result is " << r << endl; if(r.asSatisfiabilityResult().isUnknown()) { InternalError("SmtEngine::checkUnsatCore(): could not check core result unknown."); } if(r.asSatisfiabilityResult().isSat()) { InternalError("SmtEngine::checkUnsatCore(): produced core was satisfiable."); } } void SmtEngine::checkModel(bool hardFailure) { // --check-model implies --produce-assertions, which enables the // assertion list, so we should be ok. Assert(d_assertionList != NULL, "don't have an assertion list to check in SmtEngine::checkModel()"); TimerStat::CodeTimer checkModelTimer(d_stats->d_checkModelTime); // Throughout, we use Notice() to give diagnostic output. // // If this function is running, the user gave --check-model (or equivalent), // and if Notice() is on, the user gave --verbose (or equivalent). Notice() << "SmtEngine::checkModel(): generating model" << endl; TheoryModel* m = d_theoryEngine->getModel(); // Check individual theory assertions d_theoryEngine->checkTheoryAssertionsWithModel(); // Output the model Notice() << *m; // We have a "fake context" for the substitution map (we don't need it // to be context-dependent) context::Context fakeContext; SubstitutionMap substitutions(&fakeContext, /* substituteUnderQuantifiers = */ false); for(size_t k = 0; k < m->getNumCommands(); ++k) { const DeclareFunctionCommand* c = dynamic_cast(m->getCommand(k)); Notice() << "SmtEngine::checkModel(): model command " << k << " : " << m->getCommand(k) << endl; if(c == NULL) { // we don't care about DECLARE-DATATYPES, DECLARE-SORT, ... Notice() << "SmtEngine::checkModel(): skipping..." << endl; } else { // We have a DECLARE-FUN: // // We'll first do some checks, then add to our substitution map // the mapping: function symbol |-> value Expr func = c->getFunction(); Node val = m->getValue(func); Notice() << "SmtEngine::checkModel(): adding substitution: " << func << " |-> " << val << endl; // (1) if the value is a lambda, ensure the lambda doesn't contain the // function symbol (since then the definition is recursive) if (val.getKind() == kind::LAMBDA) { // first apply the model substitutions we have so far Debug("boolean-terms") << "applying subses to " << val[1] << endl; Node n = substitutions.apply(val[1]); Debug("boolean-terms") << "++ got " << n << endl; // now check if n contains func by doing a substitution // [func->func2] and checking equality of the Nodes. // (this just a way to check if func is in n.) SubstitutionMap subs(&fakeContext); Node func2 = NodeManager::currentNM()->mkSkolem("", TypeNode::fromType(func.getType()), "", NodeManager::SKOLEM_NO_NOTIFY); subs.addSubstitution(func, func2); if(subs.apply(n) != n) { Notice() << "SmtEngine::checkModel(): *** PROBLEM: MODEL VALUE DEFINED IN TERMS OF ITSELF ***" << endl; stringstream ss; ss << "SmtEngine::checkModel(): ERRORS SATISFYING ASSERTIONS WITH MODEL:" << endl << "considering model value for " << func << endl << "body of lambda is: " << val << endl; if(n != val[1]) { ss << "body substitutes to: " << n << endl; } ss << "so " << func << " is defined in terms of itself." << endl << "Run with `--check-models -v' for additional diagnostics."; InternalError(ss.str()); } } // (2) check that the value is actually a value else if (!val.isConst()) { Notice() << "SmtEngine::checkModel(): *** PROBLEM: MODEL VALUE NOT A CONSTANT ***" << endl; stringstream ss; ss << "SmtEngine::checkModel(): ERRORS SATISFYING ASSERTIONS WITH MODEL:" << endl << "model value for " << func << endl << " is " << val << endl << "and that is not a constant (.isConst() == false)." << endl << "Run with `--check-models -v' for additional diagnostics."; InternalError(ss.str()); } // (3) check that it's the correct (sub)type // This was intended to be a more general check, but for now we can't do that because // e.g. "1" is an INT, which isn't a subrange type [1..10] (etc.). else if(func.getType().isInteger() && !val.getType().isInteger()) { Notice() << "SmtEngine::checkModel(): *** PROBLEM: MODEL VALUE NOT CORRECT TYPE ***" << endl; stringstream ss; ss << "SmtEngine::checkModel(): ERRORS SATISFYING ASSERTIONS WITH MODEL:" << endl << "model value for " << func << endl << " is " << val << endl << "value type is " << val.getType() << endl << "should be of type " << func.getType() << endl << "Run with `--check-models -v' for additional diagnostics."; InternalError(ss.str()); } // (4) checks complete, add the substitution Debug("boolean-terms") << "cm: adding subs " << func << " :=> " << val << endl; substitutions.addSubstitution(func, val); } } // Now go through all our user assertions checking if they're satisfied. for(AssertionList::const_iterator i = d_assertionList->begin(); i != d_assertionList->end(); ++i) { Notice() << "SmtEngine::checkModel(): checking assertion " << *i << endl; Node n = Node::fromExpr(*i); // Apply any define-funs from the problem. { unordered_map cache; n = d_private->expandDefinitions(n, cache); } Notice() << "SmtEngine::checkModel(): -- expands to " << n << endl; // Apply our model value substitutions. Debug("boolean-terms") << "applying subses to " << n << endl; n = substitutions.apply(n); Debug("boolean-terms") << "++ got " << n << endl; Notice() << "SmtEngine::checkModel(): -- substitutes to " << n << endl; if( n.getKind() != kind::REWRITE_RULE ){ // In case it's a quantifier (or contains one), look up its value before // simplifying, or the quantifier might be irreparably altered. n = m->getValue(n); Notice() << "SmtEngine::checkModel(): -- get value : " << n << std::endl; } else { // Note this "skip" is done here, rather than above. This is // because (1) the quantifier could in principle simplify to false, // which should be reported, and (2) checking for the quantifier // above, before simplification, doesn't catch buried quantifiers // anyway (those not at the top-level). Notice() << "SmtEngine::checkModel(): -- skipping rewrite-rules assertion" << endl; continue; } // Simplify the result. n = d_private->simplify(n); Notice() << "SmtEngine::checkModel(): -- simplifies to " << n << endl; // Replace the already-known ITEs (this is important for ground ITEs under quantifiers). n = d_private->d_iteRemover.replace(n); Notice() << "SmtEngine::checkModel(): -- ite replacement gives " << n << endl; // Apply our model value substitutions (again), as things may have been simplified. Debug("boolean-terms") << "applying subses to " << n << endl; n = substitutions.apply(n); Debug("boolean-terms") << "++ got " << n << endl; Notice() << "SmtEngine::checkModel(): -- re-substitutes to " << n << endl; // As a last-ditch effort, ask model to simplify it. // Presently, this is only an issue for quantifiers, which can have a value // but don't show up in our substitution map above. n = m->getValue(n); Notice() << "SmtEngine::checkModel(): -- model-substitutes to " << n << endl; if( d_logic.isQuantified() ){ // AJR: since quantified formulas are not checkable, we assign them to true/false based on the satisfying assignment. // however, quantified formulas can be modified during preprocess, so they may not correspond to those in the satisfying assignment. // hence we use a relaxed version of check model here. // this is necessary until preprocessing passes explicitly record how they rewrite quantified formulas if( hardFailure && !n.isConst() && n.getKind() != kind::LAMBDA ){ Notice() << "SmtEngine::checkModel(): -- relax check model wrt quantified formulas..." << endl; AlwaysAssert( quantifiers::QuantifiersRewriter::containsQuantifiers( n ) ); Warning() << "Warning : SmtEngine::checkModel(): cannot check simplified assertion : " << n << endl; continue; } }else{ AlwaysAssert(!hardFailure || n.isConst() || n.getKind() == kind::LAMBDA); } // The result should be == true. if(n != NodeManager::currentNM()->mkConst(true)) { Notice() << "SmtEngine::checkModel(): *** PROBLEM: EXPECTED `TRUE' ***" << endl; stringstream ss; ss << "SmtEngine::checkModel(): " << "ERRORS SATISFYING ASSERTIONS WITH MODEL:" << endl << "assertion: " << *i << endl << "simplifies to: " << n << endl << "expected `true'." << endl << "Run with `--check-models -v' for additional diagnostics."; if(hardFailure) { InternalError(ss.str()); } else { Warning() << ss.str() << endl; } } } Notice() << "SmtEngine::checkModel(): all assertions checked out OK !" << endl; } UnsatCore SmtEngine::getUnsatCore() { Trace("smt") << "SMT getUnsatCore()" << endl; SmtScope smts(this); finalOptionsAreSet(); if(Dump.isOn("benchmark")) { Dump("benchmark") << GetUnsatCoreCommand(); } #if IS_PROOFS_BUILD if(!options::unsatCores()) { throw ModalException("Cannot get an unsat core when produce-unsat-cores option is off."); } if(d_status.isNull() || d_status.asSatisfiabilityResult() != Result::UNSAT || d_problemExtended) { //throw ModalException("Cannot get an unsat core unless immediately preceded by UNSAT/VALID response."); Warning() << CommandFailure("Cannot get an unsat core unless immediately preceded by UNSAT/VALID response."); return UnsatCore(); } d_proofManager->traceUnsatCore();// just to trigger core creation return UnsatCore(this, d_proofManager->extractUnsatCore()); #else /* IS_PROOFS_BUILD */ throw ModalException("This build of CVC4 doesn't have proof support (required for unsat cores)."); #endif /* IS_PROOFS_BUILD */ } Proof* SmtEngine::getProof() { Trace("smt") << "SMT getProof()" << endl; SmtScope smts(this); finalOptionsAreSet(); if(Dump.isOn("benchmark")) { Dump("benchmark") << GetProofCommand(); } #if IS_PROOFS_BUILD if(!options::proof()) { throw ModalException("Cannot get a proof when produce-proofs option is off."); } if(d_status.isNull() || d_status.asSatisfiabilityResult() != Result::UNSAT || d_problemExtended) { //throw ModalException("Cannot get a proof unless immediately preceded by UNSAT/VALID response."); Warning() << CommandFailure("Cannot get a proof unless immediately preceded by UNSAT/VALID response."); return NULL; } return ProofManager::getProof(this); #else /* IS_PROOFS_BUILD */ throw ModalException("This build of CVC4 doesn't have proof support."); #endif /* IS_PROOFS_BUILD */ } void SmtEngine::printInstantiations( std::ostream& out ) { SmtScope smts(this); if( options::instFormatMode()==INST_FORMAT_MODE_SZS ){ out << "% SZS output start Proof for " << d_filename.c_str() << std::endl; } if( d_theoryEngine ){ d_theoryEngine->printInstantiations( out ); }else{ Assert( false ); } if( options::instFormatMode()==INST_FORMAT_MODE_SZS ){ out << "% SZS output end Proof for " << d_filename.c_str() << std::endl; } } void SmtEngine::printSynthSolution( std::ostream& out ) { SmtScope smts(this); if( d_theoryEngine ){ d_theoryEngine->printSynthSolution( out ); }else{ Assert( false ); } } Expr SmtEngine::doQuantifierElimination(const Expr& e, bool doFull, bool strict) throw(TypeCheckingException, ModalException, LogicException) { SmtScope smts(this); if(!d_logic.isPure(THEORY_ARITH) && strict){ Warning() << "Unexpected logic for quantifier elimination " << d_logic << endl; } Trace("smt-qe") << "Do quantifier elimination " << e << std::endl; Node n_e = Node::fromExpr( e ); if( n_e.getKind()!=kind::EXISTS ){ throw ModalException("Expecting an existentially quantified formula as argument to get-qe."); } //tag the quantified formula with the quant-elim attribute TypeNode t = NodeManager::currentNM()->booleanType(); Node n_attr = NodeManager::currentNM()->mkSkolem("qe", t, "Auxiliary variable for qe attr."); std::vector< Node > node_values; d_theoryEngine->setUserAttribute( doFull ? "quant-elim" : "quant-elim-partial", n_attr, node_values, ""); n_attr = NodeManager::currentNM()->mkNode(kind::INST_ATTRIBUTE, n_attr); n_attr = NodeManager::currentNM()->mkNode(kind::INST_PATTERN_LIST, n_attr); std::vector< Node > e_children; e_children.push_back( n_e[0] ); e_children.push_back( n_e[1] ); e_children.push_back( n_attr ); Node nn_e = NodeManager::currentNM()->mkNode( kind::EXISTS, e_children ); Trace("smt-qe-debug") << "Query for quantifier elimination : " << nn_e << std::endl; Assert( nn_e.getNumChildren()==3 ); Result r = checkSatisfiability(nn_e.toExpr(), true, true); Trace("smt-qe") << "Query returned " << r << std::endl; if(r.asSatisfiabilityResult().isSat() != Result::UNSAT ) { if( r.asSatisfiabilityResult().isSat() != Result::SAT && doFull ){ stringstream ss; ss << "While performing quantifier elimination, unexpected result : " << r << " for query."; InternalError(ss.str().c_str()); } Node top_q = Rewriter::rewrite( nn_e ).negate(); Assert( top_q.getKind()==kind::FORALL ); Trace("smt-qe") << "Get qe for " << top_q << std::endl; Node ret_n = d_theoryEngine->getInstantiatedConjunction( top_q ); Trace("smt-qe") << "Returned : " << ret_n << std::endl; ret_n = Rewriter::rewrite( ret_n.negate() ); return ret_n.toExpr(); }else { return NodeManager::currentNM()->mkConst(true).toExpr(); } } void SmtEngine::getInstantiatedQuantifiedFormulas( std::vector< Expr >& qs ) { SmtScope smts(this); if( d_theoryEngine ){ std::vector< Node > qs_n; d_theoryEngine->getInstantiatedQuantifiedFormulas( qs_n ); for( unsigned i=0; i& insts ) { SmtScope smts(this); if( d_theoryEngine ){ std::vector< Node > insts_n; d_theoryEngine->getInstantiations( Node::fromExpr( q ), insts_n ); for( unsigned i=0; i >& tvecs ) { SmtScope smts(this); Assert(options::trackInstLemmas()); if( d_theoryEngine ){ std::vector< std::vector< Node > > tvecs_n; d_theoryEngine->getInstantiationTermVectors( Node::fromExpr( q ), tvecs_n ); for( unsigned i=0; i tvec; for( unsigned j=0; j SmtEngine::getAssertions() { SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); if(Dump.isOn("benchmark")) { Dump("benchmark") << GetAssertionsCommand(); } Trace("smt") << "SMT getAssertions()" << endl; if(!options::produceAssertions()) { const char* msg = "Cannot query the current assertion list when not in produce-assertions mode."; throw ModalException(msg); } Assert(d_assertionList != NULL); // copy the result out return vector(d_assertionList->begin(), d_assertionList->end()); } void SmtEngine::push() throw(ModalException, LogicException, UnsafeInterruptException) { SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); Trace("smt") << "SMT push()" << endl; d_private->processAssertions(); if(Dump.isOn("benchmark")) { Dump("benchmark") << PushCommand(); } if(!options::incrementalSolving()) { throw ModalException("Cannot push when not solving incrementally (use --incremental)"); } // check to see if a postsolve() is pending if(d_needPostsolve) { d_theoryEngine->postsolve(); d_needPostsolve = false; } // The problem isn't really "extended" yet, but this disallows // get-model after a push, simplifying our lives somewhat and // staying symmtric with pop. d_problemExtended = true; d_userLevels.push_back(d_userContext->getLevel()); internalPush(); Trace("userpushpop") << "SmtEngine: pushed to level " << d_userContext->getLevel() << endl; } void SmtEngine::pop() { SmtScope smts(this); finalOptionsAreSet(); Trace("smt") << "SMT pop()" << endl; if(Dump.isOn("benchmark")) { Dump("benchmark") << PopCommand(); } if(!options::incrementalSolving()) { throw ModalException("Cannot pop when not solving incrementally (use --incremental)"); } if(d_userLevels.size() == 0) { throw ModalException("Cannot pop beyond the first user frame"); } // check to see if a postsolve() is pending if(d_needPostsolve) { d_theoryEngine->postsolve(); d_needPostsolve = false; } // The problem isn't really "extended" yet, but this disallows // get-model after a pop, simplifying our lives somewhat. It might // not be strictly necessary to do so, since the pops occur lazily, // but also it would be weird to have a legally-executed (get-model) // that only returns a subset of the assignment (because the rest // is no longer in scope!). d_problemExtended = true; AlwaysAssert(d_userContext->getLevel() > 0); AlwaysAssert(d_userLevels.back() < d_userContext->getLevel()); while (d_userLevels.back() < d_userContext->getLevel()) { internalPop(true); } d_userLevels.pop_back(); // Clear out assertion queues etc., in case anything is still in there d_private->notifyPop(); Trace("userpushpop") << "SmtEngine: popped to level " << d_userContext->getLevel() << endl; // FIXME: should we reset d_status here? // SMT-LIBv2 spec seems to imply no, but it would make sense to.. } void SmtEngine::internalPush() { Assert(d_fullyInited); Trace("smt") << "SmtEngine::internalPush()" << endl; doPendingPops(); if(options::incrementalSolving()) { d_private->processAssertions(); TimerStat::CodeTimer pushPopTimer(d_stats->d_pushPopTime); d_userContext->push(); // the d_context push is done inside of the SAT solver d_propEngine->push(); } } void SmtEngine::internalPop(bool immediate) { Assert(d_fullyInited); Trace("smt") << "SmtEngine::internalPop()" << endl; if(options::incrementalSolving()) { ++d_pendingPops; } if(immediate) { doPendingPops(); } } void SmtEngine::doPendingPops() { Assert(d_pendingPops == 0 || options::incrementalSolving()); while(d_pendingPops > 0) { TimerStat::CodeTimer pushPopTimer(d_stats->d_pushPopTime); d_propEngine->pop(); // the d_context pop is done inside of the SAT solver d_userContext->pop(); --d_pendingPops; } } void SmtEngine::reset() throw() { SmtScope smts(this); ExprManager *em = d_exprManager; Trace("smt") << "SMT reset()" << endl; if(Dump.isOn("benchmark")) { Dump("benchmark") << ResetCommand(); } Options opts; opts.copyValues(d_originalOptions); this->~SmtEngine(); NodeManager::fromExprManager(em)->getOptions().copyValues(opts); new(this) SmtEngine(em); } void SmtEngine::resetAssertions() throw() { SmtScope smts(this); Trace("smt") << "SMT resetAssertions()" << endl; if(Dump.isOn("benchmark")) { Dump("benchmark") << ResetAssertionsCommand(); } while(!d_userLevels.empty()) { pop(); } // Also remember the global push/pop around everything. Assert(d_userLevels.size() == 0 && d_userContext->getLevel() == 1); d_context->popto(0); d_userContext->popto(0); DeleteAndClearCommandVector(d_modelGlobalCommands); d_userContext->push(); d_context->push(); } void SmtEngine::interrupt() throw(ModalException) { if(!d_fullyInited) { return; } d_propEngine->interrupt(); d_theoryEngine->interrupt(); } void SmtEngine::setResourceLimit(unsigned long units, bool cumulative) { d_private->getResourceManager()->setResourceLimit(units, cumulative); } void SmtEngine::setTimeLimit(unsigned long milis, bool cumulative) { d_private->getResourceManager()->setTimeLimit(milis, cumulative); } unsigned long SmtEngine::getResourceUsage() const { return d_private->getResourceManager()->getResourceUsage(); } unsigned long SmtEngine::getTimeUsage() const { return d_private->getResourceManager()->getTimeUsage(); } unsigned long SmtEngine::getResourceRemaining() const throw(ModalException) { return d_private->getResourceManager()->getResourceRemaining(); } unsigned long SmtEngine::getTimeRemaining() const throw(ModalException) { return d_private->getResourceManager()->getTimeRemaining(); } Statistics SmtEngine::getStatistics() const throw() { return Statistics(*d_statisticsRegistry); } SExpr SmtEngine::getStatistic(std::string name) const throw() { return d_statisticsRegistry->getStatistic(name); } void SmtEngine::safeFlushStatistics(int fd) const { d_statisticsRegistry->safeFlushInformation(fd); } void SmtEngine::setUserAttribute(const std::string& attr, Expr expr, std::vector expr_values, std::string str_value) { SmtScope smts(this); std::vector node_values; for( unsigned i=0; isetUserAttribute(attr, expr.getNode(), node_values, str_value); } void SmtEngine::setPrintFuncInModel(Expr f, bool p) { Trace("setp-model") << "Set printInModel " << f << " to " << p << std::endl; for( unsigned i=0; i(c); if(dfc != NULL) { if( dfc->getFunction()==f ){ dfc->setPrintInModel( p ); } } } for( unsigned i=0; isize(); i++ ){ Command * c = (*d_modelCommands)[i]; DeclareFunctionCommand* dfc = dynamic_cast(c); if(dfc != NULL) { if( dfc->getFunction()==f ){ dfc->setPrintInModel( p ); } } } } void SmtEngine::beforeSearch() throw(ModalException) { if(d_fullyInited) { throw ModalException( "SmtEngine::beforeSearch called after initialization."); } } void SmtEngine::setOption(const std::string& key, const CVC4::SExpr& value) throw(OptionException, ModalException) { NodeManagerScope nms(d_nodeManager); Trace("smt") << "SMT setOption(" << key << ", " << value << ")" << endl; if(Dump.isOn("benchmark")) { Dump("benchmark") << SetOptionCommand(key, value); } if(key == "command-verbosity") { if(!value.isAtom()) { const vector& cs = value.getChildren(); if(cs.size() == 2 && (cs[0].isKeyword() || cs[0].isString()) && cs[1].isInteger()) { string c = cs[0].getValue(); const Integer& v = cs[1].getIntegerValue(); if(v < 0 || v > 2) { throw OptionException("command-verbosity must be 0, 1, or 2"); } d_commandVerbosity[c] = v; return; } } throw OptionException("command-verbosity value must be a tuple (command-name, integer)"); } if(!value.isAtom()) { throw OptionException("bad value for :" + key); } string optionarg = value.getValue(); Options& nodeManagerOptions = NodeManager::currentNM()->getOptions(); nodeManagerOptions.setOption(key, optionarg); } CVC4::SExpr SmtEngine::getOption(const std::string& key) const throw(OptionException) { NodeManagerScope nms(d_nodeManager); Trace("smt") << "SMT getOption(" << key << ")" << endl; if(key.length() >= 18 && key.compare(0, 18, "command-verbosity:") == 0) { map::const_iterator i = d_commandVerbosity.find(key.c_str() + 18); if(i != d_commandVerbosity.end()) { return SExpr((*i).second); } i = d_commandVerbosity.find("*"); if(i != d_commandVerbosity.end()) { return SExpr((*i).second); } return SExpr(Integer(2)); } if(Dump.isOn("benchmark")) { Dump("benchmark") << GetOptionCommand(key); } if(key == "command-verbosity") { vector result; SExpr defaultVerbosity; for(map::const_iterator i = d_commandVerbosity.begin(); i != d_commandVerbosity.end(); ++i) { vector v; v.push_back(SExpr((*i).first)); v.push_back(SExpr((*i).second)); if((*i).first == "*") { // put the default at the end of the SExpr defaultVerbosity = SExpr(v); } else { result.push_back(SExpr(v)); } } // put the default at the end of the SExpr if(!defaultVerbosity.isAtom()) { result.push_back(defaultVerbosity); } else { // ensure the default is always listed vector v; v.push_back(SExpr("*")); v.push_back(SExpr(Integer(2))); result.push_back(SExpr(v)); } return SExpr(result); } Options& nodeManagerOptions = NodeManager::currentNM()->getOptions(); return SExpr::parseAtom(nodeManagerOptions.getOption(key)); } void SmtEngine::setReplayStream(ExprStream* replayStream) { AlwaysAssert(!d_fullyInited, "Cannot set replay stream once fully initialized"); d_replayStream = replayStream; } }/* CVC4 namespace */