diff options
Diffstat (limited to 'src/smt/smt_engine.cpp')
-rw-r--r-- | src/smt/smt_engine.cpp | 3077 |
1 files changed, 650 insertions, 2427 deletions
diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index ff5cff5b6..2a0cde015 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -5,7 +5,7 @@ ** Andrew Reynolds, Morgan Deters, Aina Niemetz ** This file is part of the CVC4 project. ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. + ** 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 ** @@ -16,113 +16,55 @@ #include "smt/smt_engine.h" -#include <algorithm> -#include <cctype> -#include <iterator> -#include <memory> -#include <sstream> -#include <stack> -#include <string> -#include <tuple> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - #include "api/cvc4cpp.h" #include "base/check.h" -#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/cdhashmap.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_algorithm.h" -#include "expr/node_builder.h" -#include "expr/node_self_iterator.h" -#include "expr/node_visitor.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_options.h" #include "options/language.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 "preprocessing/preprocessing_pass.h" -#include "preprocessing/preprocessing_pass_context.h" -#include "preprocessing/preprocessing_pass_registry.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/abduction_solver.h" -#include "smt/command.h" -#include "smt/command_list.h" +#include "smt/abstract_values.h" +#include "smt/assertions.h" +#include "smt/check_models.h" #include "smt/defined_function.h" +#include "smt/dump_manager.h" +#include "smt/interpolation_solver.h" +#include "smt/listeners.h" #include "smt/logic_request.h" -#include "smt/managed_ostreams.h" #include "smt/model_blocker.h" #include "smt/model_core_builder.h" -#include "smt/process_assertions.h" -#include "smt/set_defaults.h" +#include "smt/node_command.h" +#include "smt/options_manager.h" +#include "smt/preprocessor.h" +#include "smt/proof_manager.h" +#include "smt/quant_elim_solver.h" #include "smt/smt_engine_scope.h" +#include "smt/smt_engine_state.h" #include "smt/smt_engine_stats.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 "theory/booleans/circuit_propagator.h" -#include "theory/bv/theory_bv_rewriter.h" -#include "theory/logic_info.h" -#include "theory/quantifiers/fun_def_process.h" -#include "theory/quantifiers/single_inv_partition.h" -#include "theory/quantifiers/sygus/synth_engine.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers_engine.h" +#include "smt/smt_solver.h" +#include "smt/sygus_solver.h" +#include "theory/quantifiers/quantifiers_attributes.h" #include "theory/rewriter.h" -#include "theory/sort_inference.h" -#include "theory/strings/theory_strings.h" -#include "theory/substitutions.h" +#include "theory/smt_engine_subsolver.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/random.h" #include "util/resource_manager.h" -#if (IS_LFSC_BUILD && IS_PROOFS_BUILD) -#include "lfscc.h" -#endif +// required for hacks related to old proofs for unsat cores +#include "base/configuration.h" +#include "base/configuration_private.h" using namespace std; -using namespace CVC4; using namespace CVC4::smt; using namespace CVC4::preprocessing; using namespace CVC4::prop; @@ -131,539 +73,35 @@ using namespace CVC4::theory; namespace CVC4 { -namespace proof { -extern const char* const plf_signatures; -} // namespace proof - -namespace smt { - -struct DeleteCommandFunction : public std::unary_function<const Command*, void> -{ - void operator()(const Command* command) { delete command; } -}; - -void DeleteAndClearCommandVector(std::vector<Command*>& commands) { - std::for_each(commands.begin(), commands.end(), DeleteCommandFunction()); - commands.clear(); -} - -class SoftResourceOutListener : public Listener { - public: - SoftResourceOutListener(SmtEngine& smt) : d_smt(&smt) {} - void notify() override - { - 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) {} - void notify() override - { - SmtScope scope(d_smt); - theory::Rewriter::clearCaches(); - } - private: - SmtEngine* d_smt; -}; /* class HardResourceOutListener */ - -class BeforeSearchListener : public Listener { - public: - BeforeSearchListener(SmtEngine& smt) : d_smt(&smt) {} - void notify() override { d_smt->beforeSearch(); } - - private: - SmtEngine* d_smt; -}; /* class BeforeSearchListener */ - -class SetDefaultExprDepthListener : public Listener { - public: - void notify() override - { - 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: - void notify() override - { - 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: - void notify() override - { - 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: - void notify() override - { - const std::string& value = options::dumpModeString(); - Dump.setDumpFromString(value); - } -}; - -class PrintSuccessListener : public Listener { - public: - void notify() override - { - 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<Node, Node, NodeHashFunction> NodeToNodeHashMap; - typedef unordered_map<Node, bool, NodeHashFunction> NodeToBoolHashMap; - - /** 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; - - /** - * This list contains: - * softResourceOut - * hardResourceOut - * beforeSearchListener - * - * This needs to be deleted before both NodeManager's Options, - * SmtEngine, d_resourceManager, and TheoryEngine. - */ - ListenerRegistrationList* d_listenerRegistrations; - - /** A circuit propagator for non-clausal propositional deduction */ - booleans::CircuitPropagator d_propagator; - - /** Assertions in the preprocessing pipeline */ - AssertionPipeline d_assertions; - - /** Whether any assertions have been processed */ - CDO<bool> d_assertionsProcessed; - - // 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; - - /** TODO: whether certain preprocess steps are necessary */ - //bool d_needsExpandDefs; - - /** The preprocessing pass context */ - std::unique_ptr<PreprocessingPassContext> d_preprocessingPassContext; - - /** Process assertions module */ - ProcessAssertions d_processor; - - //------------------------------- expression names - /** mapping from expressions to name */ - context::CDHashMap< Node, std::string, NodeHashFunction > d_exprNames; - //------------------------------- end expression names - public: - IteSkolemMap& getIteSkolemMap() { return d_assertions.getIteSkolemMap(); } - - /** Instance of the ITE remover */ - RemoveTermFormulas d_iteRemover; - - /* Finishes the initialization of the private portion of SMTEngine. */ - void finishInit(); - - /*------------------- sygus utils ------------------*/ - /** - * sygus variables declared (from "declare-var" and "declare-fun" commands) - * - * The SyGuS semantics for declared variables is that they are implicitly - * universally quantified in the constraints. - */ - std::vector<Node> d_sygusVars; - /** sygus constraints */ - std::vector<Node> d_sygusConstraints; - /** functions-to-synthesize */ - std::vector<Node> d_sygusFunSymbols; - /** - * Whether we need to reconstruct the sygus conjecture. - */ - CDO<bool> d_sygusConjectureStale; - /*------------------- end of sygus utils ------------------*/ - - public: - SmtEnginePrivate(SmtEngine& smt) - : d_smt(smt), - d_managedRegularChannel(), - d_managedDiagnosticChannel(), - d_managedDumpChannel(), - d_listenerRegistrations(new ListenerRegistrationList()), - d_propagator(true, true), - d_assertions(), - d_assertionsProcessed(smt.getUserContext(), false), - d_fakeContext(), - d_abstractValueMap(&d_fakeContext), - d_abstractValues(), - d_processor(smt, *smt.getResourceManager()), - // d_needsExpandDefs(true), //TODO? - d_exprNames(smt.getUserContext()), - d_iteRemover(smt.getUserContext()), - d_sygusConjectureStale(smt.getUserContext(), true) - { - d_smt.d_nodeManager->subscribeEvents(this); - d_true = NodeManager::currentNM()->mkConst(true); - ResourceManager* rm = d_smt.getResourceManager(); - - d_listenerRegistrations->add( - rm->registerSoftListener(new SoftResourceOutListener(d_smt))); - - d_listenerRegistrations->add( - rm->registerHardListener(new HardResourceOutListener(d_smt))); - - try - { - Options& opts = d_smt.getOptions(); - // 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( - opts.registerBeforeSearchListener(new BeforeSearchListener(d_smt))); - - // These do need registration calls. - d_listenerRegistrations->add(opts.registerSetDefaultExprDepthListener( - new SetDefaultExprDepthListener(), true)); - d_listenerRegistrations->add(opts.registerSetDefaultExprDagListener( - new SetDefaultExprDagListener(), true)); - d_listenerRegistrations->add(opts.registerSetPrintExprTypesListener( - new SetPrintExprTypesListener(), true)); - d_listenerRegistrations->add( - opts.registerSetDumpModeListener(new DumpModeListener(), true)); - d_listenerRegistrations->add(opts.registerSetPrintSuccessListener( - new PrintSuccessListener(), true)); - d_listenerRegistrations->add(opts.registerSetRegularOutputChannelListener( - new SetToDefaultSourceListener(&d_managedRegularChannel), true)); - d_listenerRegistrations->add( - opts.registerSetDiagnosticOutputChannelListener( - new SetToDefaultSourceListener(&d_managedDiagnosticChannel), - true)); - d_listenerRegistrations->add(opts.registerDumpToFileNameListener( - new SetToDefaultSourceListener(&d_managedDumpChannel), true)); - } - catch (OptionException& e) - { - // Registering the option listeners can lead to OptionExceptions, e.g. - // when the user chooses a dump tag that does not exist. In that case, we - // have to make sure that we delete existing listener registrations and - // that we unsubscribe from NodeManager events. Otherwise we will have - // errors in the deconstructors of the NodeManager (because the - // NodeManager tries to notify an SmtEnginePrivate that does not exist) - // and the ListenerCollection (because not all registrations have been - // removed before calling the deconstructor). - delete d_listenerRegistrations; - d_smt.d_nodeManager->unsubscribeEvents(this); - throw OptionException(e.getRawMessage()); - } - } - - ~SmtEnginePrivate() - { - delete d_listenerRegistrations; - - if(d_propagator.getNeedsFinish()) { - d_propagator.finish(); - d_propagator.setNeedsFinish(false); - } - d_smt.d_nodeManager->unsubscribeEvents(this); - } - - void spendResource(ResourceManager::Resource r) - { - d_smt.getResourceManager()->spendResource(r); - } - - ProcessAssertions* getProcessAssertions() { return &d_processor; } - - void nmNotifyNewSort(TypeNode tn, uint32_t flags) override - { - 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, uint32_t flags) override - { - DeclareTypeCommand c(tn.getAttribute(expr::VarNameAttr()), - tn.getAttribute(expr::SortArityAttr()), - tn.toType()); - if ((flags & ExprManager::SORT_FLAG_PLACEHOLDER) == 0) - { - d_smt.addToModelCommandAndDump(c); - } - } - - void nmNotifyNewDatatypes(const std::vector<DatatypeType>& dtts, - uint32_t flags) override - { - if ((flags & ExprManager::DATATYPE_FLAG_PLACEHOLDER) == 0) - { - std::vector<Type> types(dtts.begin(), dtts.end()); - DatatypeDeclarationCommand c(types); - d_smt.addToModelCommandAndDump(c); - } - } - - void nmNotifyNewVar(TNode n, uint32_t flags) override - { - DeclareFunctionCommand c(n.getAttribute(expr::VarNameAttr()), - n.toExpr(), - n.getType().toType()); - if((flags & ExprManager::VAR_FLAG_DEFINED) == 0) { - d_smt.addToModelCommandAndDump(c, flags); - } - } - - void nmNotifyNewSkolem(TNode n, - const std::string& comment, - uint32_t flags) override - { - 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"); - } - } - - void nmNotifyDeleteNode(TNode n) override {} - - Node applySubstitutions(TNode node) - { - return Rewriter::rewrite( - d_preprocessingPassContext->getTopLevelSubstitutions().apply(node)); - } - - /** - * Process the assertions that have been asserted. - */ - void processAssertions(); - - /** Process a user push. - */ - void notifyPush() { - - } - - /** - * 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. It also pops the - * last map of expression names from notifyPush. - */ - void notifyPop() { - d_assertions.clear(); - d_propagator.getLearnedLiterals().clear(); - getIteSkolemMap().clear(); - } - - /** - * 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 arguments isInput and isAssumption are used for bookkeeping for proofs. - * The argument maybeHasFv should be set to true if the assertion may have - * free variables. By construction, assertions from the smt2 parser are - * guaranteed not to have free variables. However, other cases such as - * assertions from the SyGuS parser may have free variables (say if the - * input contains an assert or define-fun-rec command). - * - * @param isAssumption If true, the formula is considered to be an assumption - * (this is used to distinguish assertions and assumptions) - */ - void addFormula(TNode n, - bool inUnsatCore, - bool inInput = true, - bool isAssumption = false, - bool maybeHasFv = false); - /** - * 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 = d_processor.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; - } - - //------------------------------- expression names - // implements setExpressionName, as described in smt_engine.h - void setExpressionName(Expr e, std::string name) { - d_exprNames[Node::fromExpr(e)] = name; - } - - // implements getExpressionName, as described in smt_engine.h - bool getExpressionName(Expr e, std::string& name) const { - context::CDHashMap< Node, std::string, NodeHashFunction >::const_iterator it = d_exprNames.find(e); - if(it!=d_exprNames.end()) { - name = (*it).second; - return true; - }else{ - return false; - } - } - //------------------------------- end expression names -};/* class SmtEnginePrivate */ - -}/* namespace CVC4::smt */ - SmtEngine::SmtEngine(ExprManager* em, Options* optr) - : d_context(new Context()), - d_userContext(new UserContext()), - d_userLevels(), + : d_state(new SmtEngineState(*this)), d_exprManager(em), d_nodeManager(d_exprManager->getNodeManager()), - d_theoryEngine(nullptr), - d_propEngine(nullptr), + d_absValues(new AbstractValues(d_nodeManager)), + d_asserts(new Assertions(getUserContext(), *d_absValues.get())), + d_dumpm(new DumpManager(getUserContext())), + d_routListener(new ResourceOutListener(*this)), + d_snmListener(new SmtNodeManagerListener(*d_dumpm.get(), d_outMgr)), + d_smtSolver(nullptr), d_proofManager(nullptr), + d_model(nullptr), + d_checkModels(nullptr), + d_pfManager(nullptr), d_rewriter(new theory::Rewriter()), d_definedFunctions(nullptr), + d_sygusSolver(nullptr), d_abductSolver(nullptr), - d_assertionList(nullptr), - d_assignments(nullptr), - d_modelGlobalCommands(), - d_modelCommands(nullptr), - d_dumpCommands(), - d_defineCommands(), + d_interpolSolver(nullptr), + d_quantElimSolver(nullptr), d_logic(), d_originalOptions(), d_isInternalSubsolver(false), - d_pendingPops(0), - d_fullyInited(false), - d_queryMade(false), - d_needPostsolve(false), - d_globalNegation(false), - d_status(), - d_expectedStatus(), - d_smtMode(SMT_MODE_START), - d_private(nullptr), d_statisticsRegistry(nullptr), d_stats(nullptr), + d_outMgr(this), d_resourceManager(nullptr), + d_optm(nullptr), + d_pp(nullptr), d_scope(nullptr) { // !!!!!!!!!!!!!!!!!!!!!! temporary hack: this makes the current SmtEngine @@ -688,9 +126,24 @@ SmtEngine::SmtEngine(ExprManager* em, Options* optr) d_statisticsRegistry.reset(new StatisticsRegistry()); d_resourceManager.reset( new ResourceManager(*d_statisticsRegistry.get(), d_options)); - d_private.reset(new smt::SmtEnginePrivate(*this)); + d_optm.reset(new smt::OptionsManager(&d_options, d_resourceManager.get())); + // listen to node manager events + d_nodeManager->subscribeEvents(d_snmListener.get()); + // listen to resource out + d_resourceManager->registerListener(d_routListener.get()); + // make statistics d_stats.reset(new SmtEngineStatistics()); - d_stats->d_resourceUnitsUsed.setData(d_resourceManager->getResourceUsage()); + // reset the preprocessor + d_pp.reset(new smt::Preprocessor( + *this, getUserContext(), *d_absValues.get(), *d_stats)); + // make the SMT solver + d_smtSolver.reset( + new SmtSolver(*this, *d_state, d_resourceManager.get(), *d_pp, *d_stats)); + // make the SyGuS solver + d_sygusSolver.reset( + new SygusSolver(*d_smtSolver, *d_pp, getUserContext(), d_outMgr)); + // make the quantifier elimination solver + d_quantElimSolver.reset(new QuantElimSolver(*d_smtSolver)); // The ProofManager is constructed before any other proof objects such as // SatProof and TheoryProofs. The TheoryProofEngine and the SatProof are @@ -705,96 +158,103 @@ SmtEngine::SmtEngine(ExprManager* em, Options* optr) #endif d_definedFunctions = new (true) DefinedFunctionMap(getUserContext()); - d_modelCommands = new (true) smt::CommandList(getUserContext()); } -void SmtEngine::finishInit() +bool SmtEngine::isFullyInited() const { return d_state->isFullyInited(); } +bool SmtEngine::isQueryMade() const { return d_state->isQueryMade(); } +size_t SmtEngine::getNumUserLevels() const { - // Notice that finishInit is called when options are finalized. If we are - // parsing smt2, this occurs at the moment we enter "Assert mode", page 52 - // of SMT-LIB 2.6 standard. + return d_state->getNumUserLevels(); +} +SmtMode SmtEngine::getSmtMode() const { return d_state->getMode(); } +bool SmtEngine::isSmtModeSat() const +{ + SmtMode mode = getSmtMode(); + return mode == SmtMode::SAT || mode == SmtMode::SAT_UNKNOWN; +} +Result SmtEngine::getStatusOfLastCommand() const +{ + return d_state->getStatus(); +} +context::UserContext* SmtEngine::getUserContext() +{ + return d_state->getUserContext(); +} +context::Context* SmtEngine::getContext() { return d_state->getContext(); } - // Inialize the resource manager based on the options. - d_resourceManager->setHardLimit(options::hardLimit()); - if (options::perCallResourceLimit() != 0) - { - d_resourceManager->setResourceLimit(options::perCallResourceLimit(), false); - } - if (options::cumulativeResourceLimit() != 0) - { - d_resourceManager->setResourceLimit(options::cumulativeResourceLimit(), - true); - } - if (options::perCallMillisecondLimit() != 0) - { - d_resourceManager->setTimeLimit(options::perCallMillisecondLimit(), false); - } - if (options::cumulativeMillisecondLimit() != 0) +TheoryEngine* SmtEngine::getTheoryEngine() +{ + return d_smtSolver->getTheoryEngine(); +} + +prop::PropEngine* SmtEngine::getPropEngine() +{ + return d_smtSolver->getPropEngine(); +} + +void SmtEngine::finishInit() +{ + if (d_state->isFullyInited()) { - d_resourceManager->setTimeLimit(options::cumulativeMillisecondLimit(), - true); + // already initialized, return + return; } - if (options::cpuTime()) + + // Notice that finishInitInternal is called when options are finalized. If we + // are parsing smt2, this occurs at the moment we enter "Assert mode", page 52 + // of SMT-LIB 2.6 standard. + + // set the logic + if (!d_logic.isLocked()) { - d_resourceManager->useCPUTime(true); + setLogicInternal(); } // set the random seed Random::getRandom().setSeed(options::seed()); - // ensure that our heuristics are properly set up - setDefaults(d_logic, d_isInternalSubsolver); - - Trace("smt-debug") << "SmtEngine::finishInit" << std::endl; - // We have mutual dependency here, so we add the prop engine to the theory - // engine later (it is non-essential there) - d_theoryEngine.reset(new TheoryEngine(getContext(), - getUserContext(), - getResourceManager(), - d_private->d_iteRemover, - const_cast<const LogicInfo&>(d_logic))); - - // Add the theories - for(TheoryId id = theory::THEORY_FIRST; id < theory::THEORY_LAST; ++id) { - TheoryConstructor::addTheory(getTheoryEngine(), id); - //register with proof engine if applicable -#ifdef CVC4_PROOF - ProofManager::currentPM()->getTheoryProofEngine()->registerTheory(d_theoryEngine->theoryOf(id)); -#endif - } + // Call finish init on the options manager. This inializes the resource + // manager based on the options, and sets up the best default options + // based on our heuristics. + d_optm->finishInit(d_logic, d_isInternalSubsolver); - Trace("smt-debug") << "Making decision engine..." << std::endl; + ProofNodeManager* pnm = nullptr; + if (options::proofNew()) + { + d_pfManager.reset(new PfManager(getUserContext(), this)); + PreprocessProofGenerator* pppg = d_pfManager->getPreprocessProofGenerator(); + // use this proof node manager + pnm = d_pfManager->getProofNodeManager(); + // enable proof support in the rewriter + d_rewriter->setProofNodeManager(pnm); + // enable it in the assertions pipeline + d_asserts->setProofGenerator(pppg); + // enable it in the SmtSolver + d_smtSolver->setProofNodeManager(pnm); + // enabled proofs in the preprocessor + d_pp->setProofGenerator(pppg); + } - Trace("smt-debug") << "Making prop engine..." << std::endl; - /* force destruction of referenced PropEngine to enforce that statistics - * are unregistered by the obsolete PropEngine object before registered - * again by the new PropEngine object */ - d_propEngine.reset(nullptr); - d_propEngine.reset(new PropEngine( - getTheoryEngine(), getContext(), getUserContext(), getResourceManager())); + Trace("smt-debug") << "SmtEngine::finishInit" << std::endl; + d_smtSolver->finishInit(const_cast<const LogicInfo&>(d_logic)); - Trace("smt-debug") << "Setting up theory engine..." << std::endl; - d_theoryEngine->setPropEngine(getPropEngine()); - Trace("smt-debug") << "Finishing init for theory engine..." << std::endl; - d_theoryEngine->finishInit(); + // now can construct the SMT-level model object + TheoryEngine* te = d_smtSolver->getTheoryEngine(); + Assert(te != nullptr); + TheoryModel* tm = te->getModel(); + if (tm != nullptr) + { + d_model.reset(new Model(*this, tm)); + // make the check models utility + d_checkModels.reset(new CheckModels(*d_smtSolver.get())); + } // global push/pop around everything, to ensure proper destruction // of context-dependent data structures - d_userContext->push(); - d_context->push(); - - 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(getUserContext()); - d_globalDefineFunRecLemmas.reset(new std::vector<Node>()); - } + d_state->setup(); + + Trace("smt-debug") << "Set up assertions..." << std::endl; + d_asserts->finishInit(); // dump out a set-logic command only when raw-benchmark is disabled to avoid // dumping the command twice. @@ -802,74 +262,45 @@ void SmtEngine::finishInit() { LogicInfo everything; everything.lock(); - Dump("benchmark") << CommentCommand( + getOutputManager().getPrinter().toStreamCmdComment( + getOutputManager().getDumpOut(), "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()); + "the input."); + getOutputManager().getPrinter().toStreamCmdSetBenchmarkLogic( + getOutputManager().getDumpOut(), 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(); + // initialize the dump manager + d_dumpm->finishInit(); // subsolvers if (options::produceAbducts()) { d_abductSolver.reset(new AbductionSolver(this)); } - - 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)); - } - }); - d_private->finishInit(); - Trace("smt-debug") << "SmtEngine::finishInit done" << std::endl; -} - -void SmtEngine::finalOptionsAreSet() { - if(d_fullyInited) { - return; - } - - if(! d_logic.isLocked()) { - setLogicInternal(); + if (options::produceInterpols() != options::ProduceInterpols::NONE) + { + d_interpolSolver.reset(new InterpolationSolver(this)); } - // finish initialization, create the prop engine, etc. - finishInit(); + d_pp->finishInit(); - AlwaysAssert(d_propEngine->getAssertionLevel() == 0) + AlwaysAssert(getPropEngine()->getAssertionLevel() == 0) << "The PropEngine has pushed but the SmtEngine " "hasn't finished initializing!"; - d_fullyInited = true; Assert(d_logic.isLocked()); + + // store that we are finished initializing + d_state->finishInit(); + Trace("smt-debug") << "SmtEngine::finishInit done" << std::endl; } void SmtEngine::shutdown() { - doPendingPops(); + d_state->shutdown(); - while(options::incrementalSolving() && d_userContext->getLevel() > 1) { - internalPop(true); - } - - if (d_propEngine != nullptr) - { - d_propEngine->shutdown(); - } - if (d_theoryEngine != nullptr) - { - d_theoryEngine->shutdown(); - } + d_smtSolver->shutdown(); } SmtEngine::~SmtEngine() @@ -881,35 +312,12 @@ SmtEngine::~SmtEngine() // 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(); - } - - d_globalDefineFunRecLemmas.reset(); - - 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_state->cleanup(); d_definedFunctions->deleteSelf(); //destroy all passes before destroying things that they refer to - d_private->getProcessAssertions()->cleanup(); + d_pp->cleanup(); // d_proofManager is always created when proofs are enabled at configure // time. Because of this, this code should not be wrapped in PROOF() which @@ -922,18 +330,25 @@ SmtEngine::~SmtEngine() d_proofManager.reset(nullptr); #endif - d_theoryEngine.reset(nullptr); - d_propEngine.reset(nullptr); + d_absValues.reset(nullptr); + d_asserts.reset(nullptr); + d_dumpm.reset(nullptr); + + d_sygusSolver.reset(nullptr); + + d_smtSolver.reset(nullptr); d_stats.reset(nullptr); - d_private.reset(nullptr); + d_nodeManager->unsubscribeEvents(d_snmListener.get()); + d_snmListener.reset(nullptr); + d_routListener.reset(nullptr); + d_optm.reset(nullptr); + d_pp.reset(nullptr); // d_resourceManager must be destroyed before d_statisticsRegistry d_resourceManager.reset(nullptr); d_statisticsRegistry.reset(nullptr); - - - d_userContext.reset(nullptr); - d_context.reset(nullptr); + // destroy the state + d_state.reset(nullptr); } catch(Exception& e) { Warning() << "CVC4 threw an exception during cleanup." << endl << e << endl; @@ -943,7 +358,8 @@ SmtEngine::~SmtEngine() void SmtEngine::setLogic(const LogicInfo& logic) { SmtScope smts(this); - if(d_fullyInited) { + if (d_state->isFullyInited()) + { throw ModalException("Cannot set logic in SmtEngine after the engine has " "finished initializing."); } @@ -961,8 +377,8 @@ void SmtEngine::setLogic(const std::string& s) // dump out a set-logic command if (Dump.isOn("raw-benchmark")) { - Dump("raw-benchmark") - << SetBenchmarkLogicCommand(d_logic.getLogicString()); + getOutputManager().getPrinter().toStreamCmdSetBenchmarkLogic( + getOutputManager().getDumpOut(), d_logic.getLogicString()); } } catch (IllegalArgumentException& e) @@ -972,9 +388,8 @@ void SmtEngine::setLogic(const std::string& s) } void SmtEngine::setLogic(const char* logic) { setLogic(string(logic)); } -LogicInfo SmtEngine::getLogicInfo() const { - return d_logic; -} + +const LogicInfo& SmtEngine::getLogicInfo() const { return d_logic; } LogicInfo SmtEngine::getUserLogicInfo() const { @@ -987,30 +402,26 @@ LogicInfo SmtEngine::getUserLogicInfo() const void SmtEngine::notifyStartParsing(std::string filename) { - d_filename = filename; - + d_state->setFilename(filename); // Copy the original options. This is called prior to beginning parsing. // Hence reset should revert to these options, which note is after reading // the command line. d_originalOptions.copyValues(d_options); } -std::string SmtEngine::getFilename() const { return d_filename; } +const std::string& SmtEngine::getFilename() const +{ + return d_state->getFilename(); +} void SmtEngine::setLogicInternal() { - Assert(!d_fullyInited) + Assert(!d_state->isFullyInited()) << "setting logic in SmtEngine but the engine has already" " finished initializing for this run"; d_logic.lock(); d_userLogic.lock(); } -void SmtEngine::setProblemExtended() -{ - d_smtMode = SMT_MODE_ASSERT; - d_assumptions.clear(); -} - void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) { SmtScope smts(this); @@ -1020,12 +431,14 @@ void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) 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); + Result::Sat status = + (s == "sat") ? Result::SAT + : ((s == "unsat") ? Result::UNSAT : Result::SAT_UNKNOWN); + getOutputManager().getPrinter().toStreamCmdSetBenchmarkStatus( + getOutputManager().getDumpOut(), status); } else { - Dump("benchmark") << SetInfoCommand(key, value); + getOutputManager().getPrinter().toStreamCmdSetInfo( + getOutputManager().getDumpOut(), key, value); } } @@ -1038,7 +451,7 @@ void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) } else if (key == "filename") { - d_filename = value.getValue(); + d_state->setFilename(value.getValue()); return; } else if (key == "smt-lib-version" && !options::inputLanguage.wasSetByUser()) @@ -1082,7 +495,7 @@ void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) throw OptionException("argument to (set-info :status ..) must be " "`sat' or `unsat' or `unknown'"); } - d_expectedStatus = Result(s, d_filename); + d_state->notifyExpectedStatus(s); return; } throw UnrecognizedOptionException(); @@ -1157,7 +570,8 @@ CVC4::SExpr SmtEngine::getInfo(const std::string& key) const if (key == "status") { // sat | unsat | unknown - switch (d_status.asSatisfiabilityResult().isSat()) + Result status = d_state->getStatus(); + switch (status.asSatisfiabilityResult().isSat()) { case Result::SAT: return SExpr(SExpr::Keyword("sat")); case Result::UNSAT: return SExpr(SExpr::Keyword("unsat")); @@ -1166,10 +580,11 @@ CVC4::SExpr SmtEngine::getInfo(const std::string& key) const } if (key == "reason-unknown") { - if (!d_status.isNull() && d_status.isUnknown()) + Result status = d_state->getStatus(); + if (!status.isNull() && status.isUnknown()) { - stringstream ss; - ss << d_status.whyUnknown(); + std::stringstream ss; + ss << status.whyUnknown(); string s = ss.str(); transform(s.begin(), s.end(), s.begin(), ::tolower); return SExpr(SExpr::Keyword(s)); @@ -1183,9 +598,9 @@ CVC4::SExpr SmtEngine::getInfo(const std::string& key) const } if (key == "assertion-stack-levels") { - AlwaysAssert(d_userLevels.size() - <= std::numeric_limits<unsigned long int>::max()); - return SExpr(static_cast<unsigned long int>(d_userLevels.size())); + size_t ulevel = d_state->getNumUserLevels(); + AlwaysAssert(ulevel <= std::numeric_limits<unsigned long int>::max()); + return SExpr(static_cast<unsigned long int>(ulevel)); } Assert(key == "all-options"); // get the options, like all-statistics @@ -1194,33 +609,36 @@ CVC4::SExpr SmtEngine::getInfo(const std::string& key) const return SExpr::parseListOfListOfAtoms(current_options); } -void SmtEngine::debugCheckFormals(const std::vector<Expr>& formals, Expr func) +void SmtEngine::debugCheckFormals(const std::vector<Node>& formals, Node func) { - for(std::vector<Expr>::const_iterator i = formals.begin(); i != formals.end(); ++i) { + for (std::vector<Node>::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()); + throw TypeCheckingException(func.toExpr(), ss.str()); } } } -void SmtEngine::debugCheckFunctionBody(Expr formula, - const std::vector<Expr>& formals, - Expr func) +void SmtEngine::debugCheckFunctionBody(Node formula, + const std::vector<Node>& formals, + Node func) { - Type formulaType = formula.getType(options::typeChecking()); - Type funcType = func.getType(); + TypeNode formulaType = formula.getType(options::typeChecking()); + TypeNode 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(); + TypeNode rangeType = funcType.getRangeType(); if(! formulaType.isComparableTo(rangeType)) { stringstream ss; ss << "Type of defined function does not match its declaration\n" @@ -1228,29 +646,29 @@ void SmtEngine::debugCheckFunctionBody(Expr formula, << "Declared type : " << rangeType << "\n" << "The body : " << formula << "\n" << "Body type : " << formulaType; - throw TypeCheckingException(func, ss.str()); + throw TypeCheckingException(func.toExpr(), 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" + << "Declared type : " << funcType << "\n" << "The definition : " << formula << "\n" - << "Definition type: " << formulaType << " " << Type::getTypeNode(formulaType)->getId(); - throw TypeCheckingException(func, ss.str()); + << "Definition type: " << formulaType; + throw TypeCheckingException(func.toExpr(), ss.str()); } } } -void SmtEngine::defineFunction(Expr func, - const std::vector<Expr>& formals, - Expr formula, +void SmtEngine::defineFunction(Node func, + const std::vector<Node>& formals, + Node formula, bool global) { SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); + finishInit(); + d_state->doPendingPops(); Trace("smt") << "SMT defineFunction(" << func << ")" << endl; debugCheckFormals(formals, func); @@ -1258,53 +676,48 @@ void SmtEngine::defineFunction(Expr func, ss << language::SetLanguage( language::SetLanguage::getLanguage(Dump.getStream())) << func; - DefineFunctionCommand c(ss.str(), func, formals, formula, global); - addToModelCommandAndDump( - c, ExprManager::VAR_FLAG_DEFINED, true, "declarations"); + std::vector<Node> nFormals; + nFormals.reserve(formals.size()); + + for (const Node& formal : formals) + { + nFormals.push_back(formal); + } - PROOF(if (options::checkUnsatCores()) { - d_defineCommands.push_back(c.clone()); - }); + DefineFunctionNodeCommand nc(ss.str(), func, nFormals, formula); + d_dumpm->addToModelCommandAndDump( + nc, ExprManager::VAR_FLAG_DEFINED, true, "declarations"); // type check body debugCheckFunctionBody(formula, formals, func); // Substitute out any abstract values in formula - Node formNode = d_private->substituteAbstractValues(Node::fromExpr(formula)); - - TNode funcNode = func.getTNode(); - vector<Node> formalsNodes; - for(vector<Expr>::const_iterator i = formals.begin(), - iend = formals.end(); - i != iend; - ++i) { - formalsNodes.push_back((*i).getNode()); - } - DefinedFunction def(funcNode, formalsNodes, formNode); + Node formNode = d_absValues->substituteAbstractValues(formula); + DefinedFunction def(func, formals, 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; + Debug("smt") << "definedFunctions insert " << func << " " << formNode << endl; if (global) { - d_definedFunctions->insertAtContextLevelZero(funcNode, def); + d_definedFunctions->insertAtContextLevelZero(func, def); } else { - d_definedFunctions->insert(funcNode, def); + d_definedFunctions->insert(func, def); } } void SmtEngine::defineFunctionsRec( - const std::vector<Expr>& funcs, - const std::vector<std::vector<Expr>>& formals, - const std::vector<Expr>& formulas, + const std::vector<Node>& funcs, + const std::vector<std::vector<Node>>& formals, + const std::vector<Node>& formulas, bool global) { SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); + finishInit(); + d_state->doPendingPops(); Trace("smt") << "SMT defineFunctionsRec(...)" << endl; if (funcs.size() != formals.size() && funcs.size() != formulas.size()) @@ -1328,24 +741,15 @@ void SmtEngine::defineFunctionsRec( if (Dump.isOn("raw-benchmark")) { - std::vector<api::Term> tFuncs = api::exprVectorToTerms(d_solver, funcs); - std::vector<std::vector<api::Term>> tFormals; - for (const std::vector<Expr>& formal : formals) - { - tFormals.emplace_back(api::exprVectorToTerms(d_solver, formal)); - } - std::vector<api::Term> tFormulas = - api::exprVectorToTerms(d_solver, formulas); - Dump("raw-benchmark") << DefineFunctionRecCommand( - d_solver, tFuncs, tFormals, tFormulas, global); + getOutputManager().getPrinter().toStreamCmdDefineFunctionRec( + getOutputManager().getDumpOut(), funcs, formals, formulas); } - ExprManager* em = getExprManager(); - bool maybeHasFv = language::isInputLangSygus(options::inputLanguage()); + NodeManager* nm = getNodeManager(); for (unsigned i = 0, size = funcs.size(); i < size; i++) { // we assert a quantified formula - Expr func_app; + Node func_app; // make the function application if (formals[i].empty()) { @@ -1354,120 +758,60 @@ void SmtEngine::defineFunctionsRec( } else { - std::vector<Expr> children; + std::vector<Node> children; children.push_back(funcs[i]); children.insert(children.end(), formals[i].begin(), formals[i].end()); - func_app = em->mkExpr(kind::APPLY_UF, children); + func_app = nm->mkNode(kind::APPLY_UF, children); } - Expr lem = em->mkExpr(kind::EQUAL, func_app, formulas[i]); + Node lem = nm->mkNode(kind::EQUAL, func_app, formulas[i]); if (!formals[i].empty()) { // set the attribute to denote this is a function definition - std::string attr_name("fun-def"); - Expr aexpr = em->mkExpr(kind::INST_ATTRIBUTE, func_app); - aexpr = em->mkExpr(kind::INST_PATTERN_LIST, aexpr); - std::vector<Expr> expr_values; - std::string str_value; - setUserAttribute(attr_name, func_app, expr_values, str_value); + Node aexpr = nm->mkNode(kind::INST_ATTRIBUTE, func_app); + aexpr = nm->mkNode(kind::INST_PATTERN_LIST, aexpr); + FunDefAttribute fda; + func_app.setAttribute(fda, true); // make the quantified formula - Expr boundVars = em->mkExpr(kind::BOUND_VAR_LIST, formals[i]); - lem = em->mkExpr(kind::FORALL, boundVars, lem, aexpr); + Node boundVars = nm->mkNode(kind::BOUND_VAR_LIST, formals[i]); + lem = nm->mkNode(kind::FORALL, boundVars, lem, aexpr); } // assert the quantified formula // notice we don't call assertFormula directly, since this would // duplicate the output on raw-benchmark. - Node n = d_private->substituteAbstractValues(Node::fromExpr(lem)); - if (d_assertionList != nullptr) - { - d_assertionList->push_back(n); - } - if (global && d_globalDefineFunRecLemmas != nullptr) - { - // Global definitions are asserted at check-sat-time because we have to - // make sure that they are always present - Assert(!language::isInputLangSygus(options::inputLanguage())); - d_globalDefineFunRecLemmas->emplace_back(n); - } - else - { - d_private->addFormula(n, false, true, false, maybeHasFv); - } + // add define recursive definition to the assertions + d_asserts->addDefineFunRecDefinition(lem, global); } } -void SmtEngine::defineFunctionRec(Expr func, - const std::vector<Expr>& formals, - Expr formula, +void SmtEngine::defineFunctionRec(Node func, + const std::vector<Node>& formals, + Node formula, bool global) { - std::vector<Expr> funcs; + std::vector<Node> funcs; funcs.push_back(func); - std::vector<std::vector<Expr> > formals_multi; + std::vector<std::vector<Node>> formals_multi; formals_multi.push_back(formals); - std::vector<Expr> formulas; + std::vector<Node> formulas; formulas.push_back(formula); defineFunctionsRec(funcs, formals_multi, formulas, global); } -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(); -} - -void SmtEnginePrivate::finishInit() +bool SmtEngine::isDefinedFunction(Node func) { - d_preprocessingPassContext.reset( - new PreprocessingPassContext(&d_smt, &d_iteRemover, &d_propagator)); - - // initialize the preprocessing passes - d_processor.finishInit(d_preprocessingPassContext.get()); -} - -Result SmtEngine::check() { - Assert(d_fullyInited); - Assert(d_pendingPops == 0); - - Trace("smt") << "SmtEngine::check()" << endl; - - d_resourceManager->beginCall(); - - // Only way we can be out of resource is if cumulative budget is on - if (d_resourceManager->cumulativeLimitOn() && d_resourceManager->out()) - { - Result::UnknownExplanation why = d_resourceManager->outOfResources() - ? Result::RESOURCEOUT - : Result::TIMEOUT; - return Result(Result::ENTAILMENT_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; - - TimerStat::CodeTimer solveTimer(d_stats->d_solveTime); - - Chat() << "solving..." << endl; - Trace("smt") << "SmtEngine::check(): running check" << endl; - Result result = d_propEngine->checkSat(); - - d_resourceManager->endCall(); - Trace("limit") << "SmtEngine::check(): cumulative millis " - << d_resourceManager->getTimeUsage() << ", resources " - << d_resourceManager->getResourceUsage() << endl; - - return Result(result, d_filename); + Debug("smt") << "isDefined function " << func << "?" << std::endl; + return d_definedFunctions->find(func) != d_definedFunctions->end(); } Result SmtEngine::quickCheck() { - Assert(d_fullyInited); + Assert(d_state->isFullyInited()); Trace("smt") << "SMT quickCheck()" << endl; + const std::string& filename = d_state->getFilename(); return Result( - Result::ENTAILMENT_UNKNOWN, Result::REQUIRES_FULL_CHECK, d_filename); + Result::ENTAILMENT_UNKNOWN, Result::REQUIRES_FULL_CHECK, filename); } -theory::TheoryModel* SmtEngine::getAvailableModel(const char* c) const +Model* SmtEngine::getAvailableModel(const char* c) const { if (!options::assignFunctionValues()) { @@ -1476,7 +820,8 @@ theory::TheoryModel* SmtEngine::getAvailableModel(const char* c) const throw RecoverableModalException(ss.str().c_str()); } - if (d_smtMode != SMT_MODE_SAT && d_smtMode != SMT_MODE_SAT_UNKNOWN) + if (d_state->getMode() != SmtMode::SAT + && d_state->getMode() != SmtMode::SAT_UNKNOWN) { std::stringstream ss; ss << "Cannot " << c @@ -1492,330 +837,128 @@ theory::TheoryModel* SmtEngine::getAvailableModel(const char* c) const throw ModalException(ss.str().c_str()); } - TheoryModel* m = d_theoryEngine->getBuiltModel(); + TheoryEngine* te = d_smtSolver->getTheoryEngine(); + Assert(te != nullptr); + TheoryModel* m = te->getBuiltModel(); if (m == nullptr) { std::stringstream ss; ss << "Cannot " << c << " since model is not available. Perhaps the most recent call to " - "check-sat was interupted?"; + "check-sat was interrupted?"; throw RecoverableModalException(ss.str().c_str()); } - return m; + return d_model.get(); } -void SmtEnginePrivate::processAssertions() { - TimerStat::CodeTimer paTimer(d_smt.d_stats->d_processAssertionsTime); - spendResource(ResourceManager::Resource::PreprocessStep); - Assert(d_smt.d_fullyInited); - Assert(d_smt.d_pendingPops == 0); - - if (d_assertions.size() == 0) { - // nothing to do - return; - } - if (d_assertionsProcessed && options::incrementalSolving()) { - // TODO(b/1255): Substitutions in incremental mode should be managed with a - // proper data structure. +void SmtEngine::notifyPushPre() { d_smtSolver->processAssertions(*d_asserts); } - d_assertions.enableStoreSubstsInAsserts(); - } - else - { - d_assertions.disableStoreSubstsInAsserts(); - } +void SmtEngine::notifyPushPost() +{ + TimerStat::CodeTimer pushPopTimer(d_stats->d_pushPopTime); + Assert(getPropEngine() != nullptr); + getPropEngine()->push(); +} - // process the assertions - bool noConflict = d_processor.apply(d_assertions); +void SmtEngine::notifyPopPre() +{ + TimerStat::CodeTimer pushPopTimer(d_stats->d_pushPopTime); + PropEngine* pe = getPropEngine(); + Assert(pe != nullptr); + pe->pop(); +} - //notify theory engine new preprocessed assertions - d_smt.d_theoryEngine->notifyPreprocessedAssertions( d_assertions.ref() ); +void SmtEngine::notifyPostSolvePre() +{ + PropEngine* pe = getPropEngine(); + Assert(pe != nullptr); + pe->resetTrail(); +} - // Push the formula to decision engine - if (noConflict) - { - Chat() << "pushing to decision engine..." << endl; - d_smt.d_propEngine->addAssertionsToDecisionEngine(d_assertions); - } +void SmtEngine::notifyPostSolvePost() +{ + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->postsolve(); +} - // end: INVARIANT to maintain: no reordering of assertions or - // introducing new ones +Result SmtEngine::checkSat() +{ + Node nullNode; + return checkSat(nullNode); +} - // if incremental, compute which variables are assigned - if (options::incrementalSolving()) +Result SmtEngine::checkSat(const Node& assumption, bool inUnsatCore) +{ + if (Dump.isOn("benchmark")) { - d_preprocessingPassContext->recordSymbolsInAssertions(d_assertions.ref()); + getOutputManager().getPrinter().toStreamCmdCheckSat( + getOutputManager().getDumpOut(), assumption); } - - // Push the formula to SAT + std::vector<Node> assump; + if (!assumption.isNull()) { - Chat() << "converting to CNF..." << endl; - TimerStat::CodeTimer codeTimer(d_smt.d_stats->d_cnfConversionTime); - for (unsigned i = 0; i < d_assertions.size(); ++ i) { - Chat() << "+ " << d_assertions[i] << std::endl; - d_smt.d_propEngine->assertFormula(d_assertions[i]); - } + assump.push_back(assumption); } - - d_assertionsProcessed = true; - - d_assertions.clear(); - getIteSkolemMap().clear(); + return checkSatInternal(assump, inUnsatCore, false); } -void SmtEnginePrivate::addFormula( - TNode n, bool inUnsatCore, bool inInput, bool isAssumption, bool maybeHasFv) +Result SmtEngine::checkSat(const std::vector<Node>& assumptions, + bool inUnsatCore) { - if (n == d_true) { - // nothing to do - return; - } - - Trace("smt") << "SmtEnginePrivate::addFormula(" << n - << "), inUnsatCore = " << inUnsatCore - << ", inInput = " << inInput - << ", isAssumption = " << isAssumption << endl; - - // Ensure that it does not contain free variables - if (maybeHasFv) + if (Dump.isOn("benchmark")) { - if (expr::hasFreeVar(n)) + if (assumptions.empty()) { - std::stringstream se; - se << "Cannot process assertion with free variable."; - if (language::isInputLangSygus(options::inputLanguage())) - { - // Common misuse of SyGuS is to use top-level assert instead of - // constraint when defining the synthesis conjecture. - se << " Perhaps you meant `constraint` instead of `assert`?"; - } - throw ModalException(se.str().c_str()); + getOutputManager().getPrinter().toStreamCmdCheckSat( + getOutputManager().getDumpOut()); } - } - - // 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()); + else + { + getOutputManager().getPrinter().toStreamCmdCheckSatAssuming( + getOutputManager().getDumpOut(), assumptions); } - ); - - // Add the normalized formula to the queue - d_assertions.push_back(n, isAssumption); - //d_assertions.push_back(Rewriter::rewrite(n)); -} - -void SmtEngine::ensureBoolean(const Node& n) -{ - TypeNode type = n.getType(options::typeChecking()); - TypeNode boolType = NodeManager::currentNM()->booleanType(); - if(type != boolType) { - stringstream ss; - ss << "Expected " << boolType << "\n" - << "The assertion : " << n << "\n" - << "Its type : " << type; - throw TypeCheckingException(n.toExpr(), ss.str()); } + return checkSatInternal(assumptions, inUnsatCore, false); } -Result SmtEngine::checkSat(const Expr& assumption, bool inUnsatCore) +Result SmtEngine::checkEntailed(const Node& node, bool inUnsatCore) { - Dump("benchmark") << CheckSatCommand(assumption); - return checkSatisfiability(assumption, inUnsatCore, false); -} - -Result SmtEngine::checkSat(const vector<Expr>& assumptions, bool inUnsatCore) -{ - if (assumptions.empty()) - { - Dump("benchmark") << CheckSatCommand(); - } - else + if (Dump.isOn("benchmark")) { - Dump("benchmark") << CheckSatAssumingCommand(assumptions); + getOutputManager().getPrinter().toStreamCmdQuery( + getOutputManager().getDumpOut(), node); } - - return checkSatisfiability(assumptions, inUnsatCore, false); -} - -Result SmtEngine::checkEntailed(const Expr& expr, bool inUnsatCore) -{ - Dump("benchmark") << QueryCommand(expr, inUnsatCore); - return checkSatisfiability( - expr.isNull() ? std::vector<Expr>() : std::vector<Expr>{expr}, + return checkSatInternal( + node.isNull() ? std::vector<Node>() : std::vector<Node>{node}, inUnsatCore, true) .asEntailmentResult(); } -Result SmtEngine::checkEntailed(const vector<Expr>& exprs, bool inUnsatCore) +Result SmtEngine::checkEntailed(const std::vector<Node>& nodes, + bool inUnsatCore) { - return checkSatisfiability(exprs, inUnsatCore, true).asEntailmentResult(); + return checkSatInternal(nodes, inUnsatCore, true).asEntailmentResult(); } -Result SmtEngine::checkSatisfiability(const Expr& expr, - bool inUnsatCore, - bool isEntailmentCheck) -{ - return checkSatisfiability( - expr.isNull() ? std::vector<Expr>() : std::vector<Expr>{expr}, - inUnsatCore, - isEntailmentCheck); -} - -Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, - bool inUnsatCore, - bool isEntailmentCheck) +Result SmtEngine::checkSatInternal(const std::vector<Node>& assumptions, + bool inUnsatCore, + bool isEntailmentCheck) { try { SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); + finishInit(); Trace("smt") << "SmtEngine::" << (isEntailmentCheck ? "checkEntailed" : "checkSat") << "(" << assumptions << ")" << endl; - - if(d_queryMade && !options::incrementalSolving()) { - throw ModalException("Cannot make multiple queries unless " - "incremental solving is enabled " - "(try --incremental)"); - } - - // Note that a query has been made - d_queryMade = true; - // reset global negation - d_globalNegation = false; - - bool didInternalPush = false; - - setProblemExtended(); - - if (isEntailmentCheck) - { - size_t size = assumptions.size(); - if (size > 1) - { - /* Assume: not (BIGAND assumptions) */ - d_assumptions.push_back( - d_exprManager->mkExpr(kind::AND, assumptions).notExpr()); - } - else if (size == 1) - { - /* Assume: not expr */ - d_assumptions.push_back(assumptions[0].notExpr()); - } - } - else - { - /* Assume: BIGAND assumptions */ - d_assumptions = assumptions; - } - - if (!d_assumptions.empty()) - { - internalPush(); - didInternalPush = true; - } - - Result r(Result::SAT_UNKNOWN, Result::UNKNOWN_REASON); - for (Expr e : d_assumptions) - { - // Substitute out any abstract values in ex. - Node n = d_private->substituteAbstractValues(Node::fromExpr(e)); - // Ensure expr is type-checked at this point. - ensureBoolean(n); - - /* Add assumption */ - if (d_assertionList != NULL) - { - d_assertionList->push_back(n); - } - d_private->addFormula(n, inUnsatCore, true, true); - } - - if (d_globalDefineFunRecLemmas != nullptr) - { - // Global definitions are asserted at check-sat-time because we have to - // make sure that they are always present (they are essentially level - // zero assertions) - for (const Node& lemma : *d_globalDefineFunRecLemmas) - { - d_private->addFormula(lemma, false, true, false, false); - } - } - - r = check(); - - if ((options::solveRealAsInt() || options::solveIntAsBV() > 0) - && r.asSatisfiabilityResult().isSat() == Result::UNSAT) - { - r = Result(Result::SAT_UNKNOWN, Result::UNKNOWN_REASON); - } - // flipped if we did a global negation - if (d_globalNegation) - { - Trace("smt") << "SmtEngine::process global negate " << r << std::endl; - if (r.asSatisfiabilityResult().isSat() == Result::UNSAT) - { - r = Result(Result::SAT); - } - else if (r.asSatisfiabilityResult().isSat() == Result::SAT) - { - // only if satisfaction complete - if (d_logic.isPure(THEORY_ARITH) || d_logic.isPure(THEORY_BV)) - { - r = Result(Result::UNSAT); - } - else - { - r = Result(Result::SAT_UNKNOWN, Result::UNKNOWN_REASON); - } - } - Trace("smt") << "SmtEngine::global negate returned " << r << std::endl; - } - - d_needPostsolve = true; - - // Pop the context - if (didInternalPush) - { - internalPop(); - } - - // Remember the status - d_status = r; - // Check against expected status - if (!d_expectedStatus.isUnknown() && !d_status.isUnknown() - && d_status != d_expectedStatus) - { - CVC4_FATAL() << "Expected result " << d_expectedStatus << " but got " - << d_status; - } - d_expectedStatus = Result(); - // Update the SMT mode - if (d_status.asSatisfiabilityResult().isSat() == Result::UNSAT) - { - d_smtMode = SMT_MODE_UNSAT; - } - else if (d_status.asSatisfiabilityResult().isSat() == Result::SAT) - { - d_smtMode = SMT_MODE_SAT; - } - else - { - d_smtMode = SMT_MODE_SAT_UNKNOWN; - } + // check the satisfiability with the solver object + Result r = d_smtSolver->checkSatisfiability( + *d_asserts.get(), assumptions, inUnsatCore, isEntailmentCheck); Trace("smt") << "SmtEngine::" << (isEntailmentCheck ? "query" : "checkSat") << "(" << assumptions << ") => " << r << endl; @@ -1827,12 +970,6 @@ Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, checkModel(); } } - // Check that UNSAT results generate a proof correctly. - if(options::checkProofs()) { - if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { - checkProof(); - } - } // Check that UNSAT results generate an unsat core correctly. if(options::checkUnsatCores()) { if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { @@ -1844,14 +981,18 @@ Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, return r; } catch (UnsafeInterruptException& e) { AlwaysAssert(d_resourceManager->out()); + // Notice that we do not notify the state of this result. If we wanted to + // make the solver resume a working state after an interupt, then we would + // implement a different callback and use it here, e.g. + // d_state.notifyCheckSatInterupt. Result::UnknownExplanation why = d_resourceManager->outOfResources() ? Result::RESOURCEOUT : Result::TIMEOUT; - return Result(Result::SAT_UNKNOWN, why, d_filename); + return Result(Result::SAT_UNKNOWN, why, d_state->getFilename()); } } -vector<Expr> SmtEngine::getUnsatAssumptions(void) +std::vector<Node> SmtEngine::getUnsatAssumptions(void) { Trace("smt") << "SMT getUnsatAssumptions()" << endl; SmtScope smts(this); @@ -1861,22 +1002,27 @@ vector<Expr> SmtEngine::getUnsatAssumptions(void) "Cannot get unsat assumptions when produce-unsat-assumptions option " "is off."); } - if (d_smtMode != SMT_MODE_UNSAT) + if (d_state->getMode() != SmtMode::UNSAT) { throw RecoverableModalException( "Cannot get unsat assumptions unless immediately preceded by " "UNSAT/ENTAILED."); } - finalOptionsAreSet(); + finishInit(); if (Dump.isOn("benchmark")) { - Dump("benchmark") << GetUnsatAssumptionsCommand(); + getOutputManager().getPrinter().toStreamCmdGetUnsatAssumptions( + getOutputManager().getDumpOut()); } UnsatCore core = getUnsatCoreInternal(); - vector<Expr> res; - for (const Expr& e : d_assumptions) + std::vector<Node> res; + std::vector<Node>& assumps = d_asserts->getAssumptions(); + for (const Node& e : assumps) { - if (find(core.begin(), core.end(), e) != core.end()) { res.push_back(e); } + if (std::find(core.begin(), core.end(), e) != core.end()) + { + res.push_back(e); + } } return res; } @@ -1884,24 +1030,20 @@ vector<Expr> SmtEngine::getUnsatAssumptions(void) Result SmtEngine::assertFormula(const Node& formula, bool inUnsatCore) { SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); + finishInit(); + d_state->doPendingPops(); Trace("smt") << "SmtEngine::assertFormula(" << formula << ")" << endl; if (Dump.isOn("raw-benchmark")) { - Dump("raw-benchmark") << AssertCommand(formula.toExpr()); + getOutputManager().getPrinter().toStreamCmdAssert( + getOutputManager().getDumpOut(), formula); } // Substitute out any abstract values in ex - Node n = d_private->substituteAbstractValues(formula); + Node n = d_absValues->substituteAbstractValues(formula); - ensureBoolean(n); - if(d_assertionList != NULL) { - d_assertionList->push_back(n); - } - bool maybeHasFv = language::isInputLangSygus(options::inputLanguage()); - d_private->addFormula(n, inUnsatCore, true, false, maybeHasFv); + d_asserts->assertFormula(n, inUnsatCore); return quickCheck().asEntailmentResult(); }/* SmtEngine::assertFormula() */ @@ -1911,223 +1053,77 @@ Result SmtEngine::assertFormula(const Node& formula, bool inUnsatCore) -------------------------------------------------------------------------- */ -void SmtEngine::declareSygusVar(const std::string& id, Expr var, Type type) -{ - SmtScope smts(this); - finalOptionsAreSet(); - d_private->d_sygusVars.push_back(Node::fromExpr(var)); - Trace("smt") << "SmtEngine::declareSygusVar: " << var << "\n"; - Dump("raw-benchmark") << DeclareSygusVarCommand(id, var, type); - // don't need to set that the conjecture is stale -} - -void SmtEngine::declareSygusFunctionVar(const std::string& id, - Expr var, - Type type) +void SmtEngine::declareSygusVar(const std::string& id, Node var, TypeNode type) { SmtScope smts(this); - finalOptionsAreSet(); - d_private->d_sygusVars.push_back(Node::fromExpr(var)); - Trace("smt") << "SmtEngine::declareSygusFunctionVar: " << var << "\n"; - Dump("raw-benchmark") << DeclareSygusVarCommand(id, var, type); - + d_sygusSolver->declareSygusVar(id, var, type); + if (Dump.isOn("raw-benchmark")) + { + getOutputManager().getPrinter().toStreamCmdDeclareVar( + getOutputManager().getDumpOut(), var, type); + } // don't need to set that the conjecture is stale } void SmtEngine::declareSynthFun(const std::string& id, - Expr func, - Type sygusType, + Node func, + TypeNode sygusType, bool isInv, - const std::vector<Expr>& vars) + const std::vector<Node>& vars) { SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); - Node fn = Node::fromExpr(func); - d_private->d_sygusFunSymbols.push_back(fn); - if (!vars.empty()) - { - Expr bvl = d_exprManager->mkExpr(kind::BOUND_VAR_LIST, vars); - std::vector<Expr> attr_val_bvl; - attr_val_bvl.push_back(bvl); - setUserAttribute("sygus-synth-fun-var-list", func, attr_val_bvl, ""); - } - // whether sygus type encodes syntax restrictions - if (sygusType.isDatatype() - && static_cast<DatatypeType>(sygusType).getDatatype().isSygus()) + d_state->doPendingPops(); + d_sygusSolver->declareSynthFun(id, func, sygusType, isInv, vars); + + // !!! TEMPORARY: We cannot construct a SynthFunCommand since we cannot + // construct a Term-level Grammar from a Node-level sygus TypeNode. Thus we + // must print the command using the Node-level utility method for now. + + if (Dump.isOn("raw-benchmark")) { - TypeNode stn = TypeNode::fromType(sygusType); - Node sym = d_nodeManager->mkBoundVar("sfproxy", stn); - std::vector<Expr> attr_value; - attr_value.push_back(sym.toExpr()); - setUserAttribute("sygus-synth-grammar", func, attr_value, ""); + getOutputManager().getPrinter().toStreamCmdSynthFun( + getOutputManager().getDumpOut(), + id, + vars, + func.getType().isFunction() ? func.getType().getRangeType() + : func.getType(), + isInv, + sygusType); } - Trace("smt") << "SmtEngine::declareSynthFun: " << func << "\n"; - Dump("raw-benchmark") << SynthFunCommand(id, func, sygusType, isInv, vars); - // sygus conjecture is now stale - setSygusConjectureStale(); } -void SmtEngine::assertSygusConstraint(Expr constraint) +void SmtEngine::assertSygusConstraint(Node constraint) { SmtScope smts(this); - finalOptionsAreSet(); - d_private->d_sygusConstraints.push_back(constraint); - - Trace("smt") << "SmtEngine::assertSygusConstrant: " << constraint << "\n"; - Dump("raw-benchmark") << SygusConstraintCommand(constraint); - // sygus conjecture is now stale - setSygusConjectureStale(); + finishInit(); + d_sygusSolver->assertSygusConstraint(constraint); + if (Dump.isOn("raw-benchmark")) + { + getOutputManager().getPrinter().toStreamCmdConstraint( + getOutputManager().getDumpOut(), constraint); + } } -void SmtEngine::assertSygusInvConstraint(const Expr& inv, - const Expr& pre, - const Expr& trans, - const Expr& post) +void SmtEngine::assertSygusInvConstraint(Node inv, + Node pre, + Node trans, + Node post) { SmtScope smts(this); - finalOptionsAreSet(); - // build invariant constraint - - // get variables (regular and their respective primed versions) - std::vector<Node> terms, vars, primed_vars; - terms.push_back(Node::fromExpr(inv)); - terms.push_back(Node::fromExpr(pre)); - terms.push_back(Node::fromExpr(trans)); - terms.push_back(Node::fromExpr(post)); - // variables are built based on the invariant type - FunctionType t = static_cast<FunctionType>(inv.getType()); - std::vector<Type> argTypes = t.getArgTypes(); - for (const Type& ti : argTypes) - { - TypeNode tn = TypeNode::fromType(ti); - vars.push_back(d_nodeManager->mkBoundVar(tn)); - d_private->d_sygusVars.push_back(vars.back()); - std::stringstream ss; - ss << vars.back() << "'"; - primed_vars.push_back(d_nodeManager->mkBoundVar(ss.str(), tn)); - d_private->d_sygusVars.push_back(primed_vars.back()); - } - - // make relevant terms; 0 -> Inv, 1 -> Pre, 2 -> Trans, 3 -> Post - for (unsigned i = 0; i < 4; ++i) + finishInit(); + d_sygusSolver->assertSygusInvConstraint(inv, pre, trans, post); + if (Dump.isOn("raw-benchmark")) { - Node op = terms[i]; - Trace("smt-debug") << "Make inv-constraint term #" << i << " : " << op - << " with type " << op.getType() << "...\n"; - std::vector<Node> children; - children.push_back(op); - // transition relation applied over both variable lists - if (i == 2) - { - children.insert(children.end(), vars.begin(), vars.end()); - children.insert(children.end(), primed_vars.begin(), primed_vars.end()); - } - else - { - children.insert(children.end(), vars.begin(), vars.end()); - } - terms[i] = d_nodeManager->mkNode(kind::APPLY_UF, children); - // make application of Inv on primed variables - if (i == 0) - { - children.clear(); - children.push_back(op); - children.insert(children.end(), primed_vars.begin(), primed_vars.end()); - terms.push_back(d_nodeManager->mkNode(kind::APPLY_UF, children)); - } + getOutputManager().getPrinter().toStreamCmdInvConstraint( + getOutputManager().getDumpOut(), inv, pre, trans, post); } - // make constraints - std::vector<Node> conj; - conj.push_back(d_nodeManager->mkNode(kind::IMPLIES, terms[1], terms[0])); - Node term0_and_2 = d_nodeManager->mkNode(kind::AND, terms[0], terms[2]); - conj.push_back(d_nodeManager->mkNode(kind::IMPLIES, term0_and_2, terms[4])); - conj.push_back(d_nodeManager->mkNode(kind::IMPLIES, terms[0], terms[3])); - Node constraint = d_nodeManager->mkNode(kind::AND, conj); - - d_private->d_sygusConstraints.push_back(constraint); - - Trace("smt") << "SmtEngine::assertSygusInvConstrant: " << constraint << "\n"; - Dump("raw-benchmark") << SygusInvConstraintCommand(inv, pre, trans, post); - // sygus conjecture is now stale - setSygusConjectureStale(); } Result SmtEngine::checkSynth() { SmtScope smts(this); - - if (options::incrementalSolving()) - { - // TODO (project #7) - throw ModalException( - "Cannot make check-synth commands when incremental solving is enabled"); - } - Expr query; - if (d_private->d_sygusConjectureStale) - { - // build synthesis conjecture from asserted constraints and declared - // variables/functions - Node sygusVar = - d_nodeManager->mkSkolem("sygus", d_nodeManager->booleanType()); - Node inst_attr = d_nodeManager->mkNode(kind::INST_ATTRIBUTE, sygusVar); - Node sygusAttr = d_nodeManager->mkNode(kind::INST_PATTERN_LIST, inst_attr); - std::vector<Node> bodyv; - Trace("smt") << "Sygus : Constructing sygus constraint...\n"; - unsigned n_constraints = d_private->d_sygusConstraints.size(); - Node body = n_constraints == 0 - ? d_nodeManager->mkConst(true) - : (n_constraints == 1 - ? d_private->d_sygusConstraints[0] - : d_nodeManager->mkNode( - kind::AND, d_private->d_sygusConstraints)); - body = body.notNode(); - Trace("smt") << "...constructed sygus constraint " << body << std::endl; - if (!d_private->d_sygusVars.empty()) - { - Node boundVars = - d_nodeManager->mkNode(kind::BOUND_VAR_LIST, d_private->d_sygusVars); - body = d_nodeManager->mkNode(kind::EXISTS, boundVars, body); - Trace("smt") << "...constructed exists " << body << std::endl; - } - if (!d_private->d_sygusFunSymbols.empty()) - { - Node boundVars = d_nodeManager->mkNode(kind::BOUND_VAR_LIST, - d_private->d_sygusFunSymbols); - body = d_nodeManager->mkNode(kind::FORALL, boundVars, body, sygusAttr); - } - Trace("smt") << "...constructed forall " << body << std::endl; - - // set attribute for synthesis conjecture - setUserAttribute("sygus", sygusVar.toExpr(), {}, ""); - - Trace("smt") << "Check synthesis conjecture: " << body << std::endl; - Dump("raw-benchmark") << CheckSynthCommand(); - - d_private->d_sygusConjectureStale = false; - - if (options::incrementalSolving()) - { - // we push a context so that this conjecture is removed if we modify it - // later - internalPush(); - assertFormula(body, true); - } - else - { - query = body.toExpr(); - } - } - - Result r = checkSatisfiability(query, true, false); - - // Check that synthesis solutions satisfy the conjecture - if (options::checkSynthSol() - && r.asSatisfiabilityResult().isSat() == Result::UNSAT) - { - checkSynthSolution(); - } - return r; + finishInit(); + return d_sygusSolver->checkSynth(*d_asserts); } /* @@ -2136,85 +1132,42 @@ Result SmtEngine::checkSynth() -------------------------------------------------------------------------- */ -Node SmtEngine::postprocess(TNode node, TypeNode expectedType) const { - return node; -} - -Expr SmtEngine::simplify(const Expr& ex) +Node SmtEngine::simplify(const Node& ex) { - 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(); + finishInit(); + d_state->doPendingPops(); + // ensure we've processed assertions + d_smtSolver->processAssertions(*d_asserts); + return d_pp->simplify(ex); } Node SmtEngine::expandDefinitions(const Node& ex) { - d_private->spendResource(ResourceManager::Resource::PreprocessStep); + d_resourceManager->spendResource(ResourceManager::Resource::PreprocessStep); SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); - Trace("smt") << "SMT expandDefinitions(" << ex << ")" << endl; - - // Substitute out any abstract values in ex. - Node e = d_private->substituteAbstractValues(ex); - if(options::typeChecking()) { - // Ensure expr is type-checked at this point. - e.getType(true); - } - - unordered_map<Node, Node, NodeHashFunction> cache; - Node n = d_private->getProcessAssertions()->expandDefinitions( - e, cache, /* expandOnly = */ true); - n = postprocess(n, e.getType()); - - return n; + finishInit(); + d_state->doPendingPops(); + // set expandOnly flag to true + return d_pp->expandDefinitions(ex, true); } // TODO(#1108): Simplify the error reporting of this method. -Expr SmtEngine::getValue(const Expr& ex) const +Node SmtEngine::getValue(const Node& ex) const { - Assert(ex.getExprManager() == d_exprManager); SmtScope smts(this); Trace("smt") << "SMT getValue(" << ex << ")" << endl; if(Dump.isOn("benchmark")) { - Dump("benchmark") << GetValueCommand(ex); + d_outMgr.getPrinter().toStreamCmdGetValue(d_outMgr.getDumpOut(), {ex}); } + TypeNode expectedType = ex.getType(); - // 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()); + // Substitute out any abstract values in ex and expand + Node n = d_pp->expandDefinitions(ex); - // 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<Node, Node, NodeHashFunction> cache; - n = d_private->getProcessAssertions()->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 @@ -2227,16 +1180,12 @@ Expr SmtEngine::getValue(const Expr& ex) const } Trace("smt") << "--- getting value of " << n << endl; - TheoryModel* m = getAvailableModel("get-value"); - Node resultNode; - if(m != NULL) { - resultNode = m->getValue(n); - } + Model* m = getAvailableModel("get-value"); + Assert(m != nullptr); + Node 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; + Trace("smt") << "--- type " << resultNode.getType() << endl; + Trace("smt") << "--- expected type " << expectedType << endl; // type-check the result we got // Notice that lambdas have function type, which does not respect the subtype @@ -2251,171 +1200,56 @@ Expr SmtEngine::getValue(const Expr& ex) const || resultNode.isConst()); if(options::abstractValues() && resultNode.getType().isArray()) { - resultNode = d_private->mkAbstractValue(resultNode); + resultNode = d_absValues->mkAbstractValue(resultNode); Trace("smt") << "--- abstract value >> " << resultNode << endl; } - return resultNode.toExpr(); + return resultNode; } -vector<Expr> SmtEngine::getValues(const vector<Expr>& exprs) +std::vector<Node> SmtEngine::getValues(const std::vector<Node>& exprs) { - vector<Expr> result; - for (const Expr& e : exprs) + std::vector<Node> result; + for (const Node& e : exprs) { result.push_back(getValue(e)); } return result; } -bool SmtEngine::addToAssignment(const Expr& ex) { - SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); - // Substitute out any abstract values in ex - Node n = d_private->substituteAbstractValues(Node::fromExpr(ex)); - TypeNode type = n.getType(options::typeChecking()); - // must be Boolean - PrettyCheckArgument(type.isBoolean(), - n, - "expected Boolean-typed variable or function application " - "in addToAssignment()"); - // must be a defined constant, or a variable - PrettyCheckArgument( - (((d_definedFunctions->find(n) != d_definedFunctions->end()) - && n.getNumChildren() == 0) - || n.isVar()), - n, - "expected variable or defined-function application " - "in addToAssignment(),\ngot %s", - n.toString().c_str()); - if(!options::produceAssignments()) { - return false; - } - if(d_assignments == NULL) { - d_assignments = new (true) AssignmentSet(getContext()); - } - d_assignments->insert(n); - - return true; -} - -// TODO(#1108): Simplify the error reporting of this method. -vector<pair<Expr, Expr>> 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); - } - - // Get the model here, regardless of whether d_assignments is null, since - // we should throw errors related to model availability whether or not - // assignments is null. - TheoryModel* m = getAvailableModel("get assignment"); - - vector<pair<Expr,Expr>> res; - if (d_assignments != nullptr) - { - TypeNode boolType = d_nodeManager->booleanType(); - for (AssignmentSet::key_iterator i = d_assignments->key_begin(), - iend = d_assignments->key_end(); - i != iend; - ++i) - { - Node as = *i; - Assert(as.getType() == boolType); - - Trace("smt") << "--- getting value of " << as << endl; - - // Expand, then normalize - unordered_map<Node, Node, NodeHashFunction> cache; - Node n = d_private->getProcessAssertions()->expandDefinitions(as, cache); - n = Rewriter::rewrite(n); - - Trace("smt") << "--- getting value of " << n << endl; - Node resultNode; - if (m != nullptr) - { - resultNode = m->getValue(n); - } - - // type-check the result we got - Assert(resultNode.isNull() || resultNode.getType() == boolType); - - // ensure it's a constant - Assert(resultNode.isConst()); - - Assert(as.isVar()); - res.emplace_back(as.toExpr(), resultNode.toExpr()); - } - } - return res; -} - -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) { - 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()); - } - } -} - // TODO(#1108): Simplify the error reporting of this method. Model* SmtEngine::getModel() { Trace("smt") << "SMT getModel()" << endl; SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); if(Dump.isOn("benchmark")) { - Dump("benchmark") << GetModelCommand(); + getOutputManager().getPrinter().toStreamCmdGetModel( + getOutputManager().getDumpOut()); } - TheoryModel* m = getAvailableModel("get model"); + Model* m = getAvailableModel("get model"); // Since model m is being returned to the user, we must ensure that this // model object remains valid with future check-sat calls. Hence, we set // the theory engine into "eager model building" mode. TODO #2648: revisit. - d_theoryEngine->setEagerModelBuilding(); + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->setEagerModelBuilding(); if (options::modelCoresMode() != options::ModelCoresMode::NONE) { // If we enabled model cores, we compute a model core for m based on our // (expanded) assertions using the model core builder utility - std::vector<Expr> eassertsProc = getExpandedAssertions(); - ModelCoreBuilder::setModelCore(eassertsProc, m, options::modelCoresMode()); - } - m->d_inputName = d_filename; - m->d_isKnownSat = (d_smtMode == SMT_MODE_SAT); + std::vector<Node> eassertsProc = getExpandedAssertions(); + ModelCoreBuilder::setModelCore( + eassertsProc, m->getTheoryModel(), options::modelCoresMode()); + } + // set the information on the SMT-level model + Assert(m != nullptr); + m->d_inputName = d_state->getFilename(); + m->d_isKnownSat = (d_state->getMode() == SmtMode::SAT); return m; } @@ -2424,55 +1258,57 @@ Result SmtEngine::blockModel() Trace("smt") << "SMT blockModel()" << endl; SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); if (Dump.isOn("benchmark")) { - Dump("benchmark") << BlockModelCommand(); + getOutputManager().getPrinter().toStreamCmdBlockModel( + getOutputManager().getDumpOut()); } - TheoryModel* m = getAvailableModel("block model"); + Model* m = getAvailableModel("block model"); if (options::blockModelsMode() == options::BlockModelsMode::NONE) { std::stringstream ss; ss << "Cannot block model when block-models is set to none."; - throw ModalException(ss.str().c_str()); + throw RecoverableModalException(ss.str().c_str()); } // get expanded assertions - std::vector<Expr> eassertsProc = getExpandedAssertions(); - Expr eblocker = ModelBlocker::getModelBlocker( - eassertsProc, m, options::blockModelsMode()); - return assertFormula(Node::fromExpr(eblocker)); + std::vector<Node> eassertsProc = getExpandedAssertions(); + Node eblocker = ModelBlocker::getModelBlocker( + eassertsProc, m->getTheoryModel(), options::blockModelsMode()); + return assertFormula(eblocker); } -Result SmtEngine::blockModelValues(const std::vector<Expr>& exprs) +Result SmtEngine::blockModelValues(const std::vector<Node>& exprs) { Trace("smt") << "SMT blockModelValues()" << endl; SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); - PrettyCheckArgument( - !exprs.empty(), - "block model values must be called on non-empty set of terms"); if (Dump.isOn("benchmark")) { - Dump("benchmark") << BlockModelValuesCommand(exprs); + getOutputManager().getPrinter().toStreamCmdBlockModelValues( + getOutputManager().getDumpOut(), exprs); } - TheoryModel* m = getAvailableModel("block model values"); + Model* m = getAvailableModel("block model values"); // get expanded assertions - std::vector<Expr> eassertsProc = getExpandedAssertions(); + std::vector<Node> eassertsProc = getExpandedAssertions(); // we always do block model values mode here - Expr eblocker = ModelBlocker::getModelBlocker( - eassertsProc, m, options::BlockModelsMode::VALUES, exprs); - return assertFormula(Node::fromExpr(eblocker)); + Node eblocker = + ModelBlocker::getModelBlocker(eassertsProc, + m->getTheoryModel(), + options::BlockModelsMode::VALUES, + exprs); + return assertFormula(eblocker); } -std::pair<Expr, Expr> SmtEngine::getSepHeapAndNilExpr(void) +std::pair<Node, Node> SmtEngine::getSepHeapAndNilExpr(void) { if (!d_logic.isTheoryEnabled(THEORY_SEP)) { @@ -2482,74 +1318,60 @@ std::pair<Expr, Expr> SmtEngine::getSepHeapAndNilExpr(void) throw RecoverableModalException(msg); } NodeManagerScope nms(d_nodeManager); - Expr heap; - Expr nil; + Node heap; + Node nil; Model* m = getAvailableModel("get separation logic heap and nil"); - if (!m->getHeapModel(heap, nil)) + TheoryModel* tm = m->getTheoryModel(); + if (!tm->getHeapModel(heap, nil)) { - InternalError() - << "SmtEngine::getSepHeapAndNilExpr(): failed to obtain heap/nil " - "expressions from theory model."; + const char* msg = + "Failed to obtain heap/nil " + "expressions from theory model."; + throw RecoverableModalException(msg); } return std::make_pair(heap, nil); } -std::vector<Expr> SmtEngine::getExpandedAssertions() +std::vector<Node> SmtEngine::getExpandedAssertions() { - std::vector<Expr> easserts = getAssertions(); + std::vector<Node> easserts = getAssertions(); // must expand definitions - std::vector<Expr> eassertsProc; + std::vector<Node> eassertsProc; std::unordered_map<Node, Node, NodeHashFunction> cache; - for (const Expr& e : easserts) + for (const Node& e : easserts) { - Node ea = Node::fromExpr(e); - Node eae = d_private->getProcessAssertions()->expandDefinitions(ea, cache); - eassertsProc.push_back(eae.toExpr()); + Node eae = d_pp->expandDefinitions(e, cache); + eassertsProc.push_back(eae); } return eassertsProc; } -Expr SmtEngine::getSepHeapExpr() { return getSepHeapAndNilExpr().first; } - -Expr SmtEngine::getSepNilExpr() { return getSepHeapAndNilExpr().second; } - -void SmtEngine::checkProof() +void SmtEngine::declareSepHeap(TypeNode locT, TypeNode dataT) { -#if (IS_LFSC_BUILD && IS_PROOFS_BUILD) - - Chat() << "generating proof..." << endl; - - const Proof& pf = getProof(); - - Chat() << "checking proof..." << endl; - - std::string logicString = d_logic.getLogicString(); - - std::stringstream pfStream; - - pfStream << proof::plf_signatures << endl; - int64_t sizeBeforeProof = static_cast<int64_t>(pfStream.tellp()); - - pf.toStream(pfStream); - d_stats->d_proofsSize += - static_cast<int64_t>(pfStream.tellp()) - sizeBeforeProof; - + if (!d_logic.isTheoryEnabled(THEORY_SEP)) { - TimerStat::CodeTimer checkProofTimer(d_stats->d_lfscCheckProofTime); - lfscc_init(); - lfscc_check_file(pfStream, false, false, false, false, false, false, false); + const char* msg = + "Cannot declare heap if not using the separation logic theory."; + throw RecoverableModalException(msg); } - // FIXME: we should actually call lfscc_cleanup here, but lfscc_cleanup - // segfaults on regress0/bv/core/bitvec7.smt - // lfscc_cleanup(); + SmtScope smts(this); + finishInit(); + TheoryEngine* te = getTheoryEngine(); + te->declareSepHeap(locT, dataT); +} -#else /* (IS_LFSC_BUILD && IS_PROOFS_BUILD) */ - Unreachable() - << "This version of CVC4 was built without proof support; cannot check " - "proofs."; -#endif /* (IS_LFSC_BUILD && IS_PROOFS_BUILD) */ +bool SmtEngine::getSepHeapTypes(TypeNode& locT, TypeNode& dataT) +{ + SmtScope smts(this); + finishInit(); + TheoryEngine* te = getTheoryEngine(); + return te->getSepHeapTypes(locT, dataT); } +Node SmtEngine::getSepHeapExpr() { return getSepHeapAndNilExpr().first; } + +Node SmtEngine::getSepNilExpr() { return getSepHeapAndNilExpr().second; } + UnsatCore SmtEngine::getUnsatCoreInternal() { #if IS_PROOFS_BUILD @@ -2558,7 +1380,7 @@ UnsatCore SmtEngine::getUnsatCoreInternal() throw ModalException( "Cannot get an unsat core when produce-unsat-cores option is off."); } - if (d_smtMode != SMT_MODE_UNSAT) + if (d_state->getMode() != SmtMode::UNSAT) { throw RecoverableModalException( "Cannot get an unsat core unless immediately preceded by " @@ -2566,7 +1388,7 @@ UnsatCore SmtEngine::getUnsatCoreInternal() } d_proofManager->traceUnsatCore(); // just to trigger core creation - return UnsatCore(this, d_proofManager->extractUnsatCore()); + return UnsatCore(d_proofManager->extractUnsatCore()); #else /* IS_PROOFS_BUILD */ throw ModalException( "This build of CVC4 doesn't have proof support (required for unsat " @@ -2581,27 +1403,29 @@ void SmtEngine::checkUnsatCore() { Notice() << "SmtEngine::checkUnsatCore(): generating unsat core" << endl; UnsatCore core = getUnsatCore(); - SmtEngine coreChecker(d_exprManager, &d_options); - coreChecker.setIsInternalSubsolver(); - coreChecker.setLogic(getLogicInfo()); - coreChecker.getOptions().set(options::checkUnsatCores, false); - coreChecker.getOptions().set(options::checkProofs, false); + // initialize the core checker + std::unique_ptr<SmtEngine> coreChecker; + initializeSubsolver(coreChecker); + coreChecker->getOptions().set(options::checkUnsatCores, false); - PROOF( - std::vector<Command*>::const_iterator itg = d_defineCommands.begin(); - for (; itg != d_defineCommands.end(); ++itg) { - (*itg)->invoke(&coreChecker); + // set up separation logic heap if necessary + TypeNode sepLocType, sepDataType; + if (getSepHeapTypes(sepLocType, sepDataType)) + { + coreChecker->declareSepHeap(sepLocType, sepDataType); } - ); - Notice() << "SmtEngine::checkUnsatCore(): pushing core assertions (size == " << core.size() << ")" << endl; + Notice() << "SmtEngine::checkUnsatCore(): pushing core assertions" + << std::endl; for(UnsatCore::iterator i = core.begin(); i != core.end(); ++i) { - Notice() << "SmtEngine::checkUnsatCore(): pushing core member " << *i << endl; - coreChecker.assertFormula(Node::fromExpr(*i)); + Node assertionAfterExpansion = expandDefinitions(*i); + Notice() << "SmtEngine::checkUnsatCore(): pushing core member " << *i + << ", expanded to " << assertionAfterExpansion << "\n"; + coreChecker->assertFormula(assertionAfterExpansion); } Result r; try { - r = coreChecker.checkSat(); + r = coreChecker->checkSat(); } catch(...) { throw; } @@ -2619,543 +1443,93 @@ void SmtEngine::checkUnsatCore() { } void SmtEngine::checkModel(bool hardFailure) { + context::CDList<Node>* al = d_asserts->getAssertionList(); // --check-model implies --produce-assertions, which enables the // assertion list, so we should be ok. - Assert(d_assertionList != NULL) + Assert(al != nullptr) << "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 = getAvailableModel("check model"); - - // check-model is not guaranteed to succeed if approximate values were used. - // Thus, we intentionally abort here. - if (m->hasApproximations()) - { - throw RecoverableModalException( - "Cannot run check-model on a model with approximate values."); - } - - // Check individual theory assertions - if (options::debugCheckModels()) - { - d_theoryEngine->checkTheoryAssertionsWithModel(hardFailure); - } - - // 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<const DeclareFunctionCommand*>(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()) - { - // This is only a warning since it could have been assigned an - // unevaluable term (e.g. an application of a transcendental function). - // This parallels the behavior (warnings for non-constant expressions) - // when checking whether assertions are satisfied below. - Warning() << "Warning : SmtEngine::checkModel(): " - << "model value for " << func << endl - << " is " << val << endl - << "and that is not a constant (.isConst() == false)." - << std::endl - << "Run with `--check-models -v' for additional diagnostics." - << std::endl; - } - - // (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; - InternalError() - << "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."; - } - - // (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 (const Node& assertion : *d_assertionList) - { - Notice() << "SmtEngine::checkModel(): checking assertion " << assertion - << endl; - Node n = assertion; - - // Apply any define-funs from the problem. - { - unordered_map<Node, Node, NodeHashFunction> cache; - n = d_private->getProcessAssertions()->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; - - // We look up the value before simplifying. If n contains quantifiers, - // this may increases the chance of finding its value before the node is - // altered by simplification below. - n = m->getValue(n); - Notice() << "SmtEngine::checkModel(): -- get value : " << n << std::endl; - - // 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 (n.isConst()) - { - if (n.getConst<bool>()) - { - // assertion is true, everything is fine - continue; - } - } - - // Otherwise, we did not succeed in showing the current assertion to be - // true. This may either indicate that our model is wrong, or that we cannot - // check it. The latter may be the case for several reasons. - // For example, quantified formulas are not checkable, although 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 throw - // warnings for assertions that do not simplify to either true or false. - // Other theories such as non-linear arithmetic (in particular, - // transcendental functions) also have the property of not being able to - // be checked precisely here. - // Note that warnings like these can be avoided for quantified formulas - // by making preprocessing passes explicitly record how they - // rewrite quantified formulas (see cvc4-wishues#43). - if (!n.isConst()) - { - // Not constant, print a less severe warning message here. - Warning() << "Warning : SmtEngine::checkModel(): cannot check simplified " - "assertion : " - << n << endl; - continue; - } - // Assertions that simplify to false result in an InternalError or - // Warning being thrown below (when hardFailure is false). - Notice() << "SmtEngine::checkModel(): *** PROBLEM: EXPECTED `TRUE' ***" - << endl; - stringstream ss; - ss << "SmtEngine::checkModel(): " - << "ERRORS SATISFYING ASSERTIONS WITH MODEL:" << endl - << "assertion: " << assertion << endl - << "simplifies to: " << n << endl - << "expected `true'." << endl - << "Run with `--check-models -v' for additional diagnostics."; - if (hardFailure) - { - // internal error if hardFailure is true - InternalError() << ss.str(); - } - else - { - Warning() << ss.str() << endl; - } - } - Notice() << "SmtEngine::checkModel(): all assertions checked out OK !" << endl; -} - -void SmtEngine::checkSynthSolution() -{ - NodeManager* nm = NodeManager::currentNM(); - Notice() << "SmtEngine::checkSynthSolution(): checking synthesis solution" << endl; - std::map<Node, std::map<Node, Node>> sol_map; - /* Get solutions and build auxiliary vectors for substituting */ - if (!d_theoryEngine->getSynthSolutions(sol_map)) - { - InternalError() << "SmtEngine::checkSynthSolution(): No solution to check!"; - return; - } - if (sol_map.empty()) - { - InternalError() << "SmtEngine::checkSynthSolution(): Got empty solution!"; - return; - } - Trace("check-synth-sol") << "Got solution map:\n"; - // the set of synthesis conjectures in our assertions - std::unordered_set<Node, NodeHashFunction> conjs; - // For each of the above conjectures, the functions-to-synthesis and their - // solutions. This is used as a substitution below. - std::map<Node, std::vector<Node>> fvarMap; - std::map<Node, std::vector<Node>> fsolMap; - for (const std::pair<const Node, std::map<Node, Node>>& cmap : sol_map) - { - Trace("check-synth-sol") << "For conjecture " << cmap.first << ":\n"; - conjs.insert(cmap.first); - std::vector<Node>& fvars = fvarMap[cmap.first]; - std::vector<Node>& fsols = fsolMap[cmap.first]; - for (const std::pair<const Node, Node>& pair : cmap.second) - { - Trace("check-synth-sol") - << " " << pair.first << " --> " << pair.second << "\n"; - fvars.push_back(pair.first); - fsols.push_back(pair.second); - } - } - Trace("check-synth-sol") << "Starting new SMT Engine\n"; - /* Start new SMT engine to check solutions */ - SmtEngine solChecker(d_exprManager, &d_options); - solChecker.setIsInternalSubsolver(); - solChecker.setLogic(getLogicInfo()); - solChecker.getOptions().set(options::checkSynthSol, false); - solChecker.getOptions().set(options::sygusRecFun, false); + Model* m = getAvailableModel("check model"); + Assert(m != nullptr); - Trace("check-synth-sol") << "Retrieving assertions\n"; - // Build conjecture from original assertions - if (d_assertionList == NULL) - { - Trace("check-synth-sol") << "No assertions to check\n"; - return; - } - // auxiliary assertions - std::vector<Node> auxAssertions; - // expand definitions cache - std::unordered_map<Node, Node, NodeHashFunction> cache; - for (const Node& assertion : *d_assertionList) - { - Notice() << "SmtEngine::checkSynthSolution(): checking assertion " - << assertion << endl; - Trace("check-synth-sol") << "Retrieving assertion " << assertion << "\n"; - // Apply any define-funs from the problem. - Node n = - d_private->getProcessAssertions()->expandDefinitions(assertion, cache); - Notice() << "SmtEngine::checkSynthSolution(): -- expands to " << n << endl; - Trace("check-synth-sol") << "Expanded assertion " << n << "\n"; - if (conjs.find(n) == conjs.end()) - { - Trace("check-synth-sol") << "It is an auxiliary assertion\n"; - auxAssertions.push_back(n); - } - else - { - Trace("check-synth-sol") << "It is a synthesis conjecture\n"; - } - } - // check all conjectures - for (const Node& conj : conjs) - { - // get the solution for this conjecture - std::vector<Node>& fvars = fvarMap[conj]; - std::vector<Node>& fsols = fsolMap[conj]; - // Apply solution map to conjecture body - Node conjBody; - /* Whether property is quantifier free */ - if (conj[1].getKind() != kind::EXISTS) - { - conjBody = conj[1].substitute( - fvars.begin(), fvars.end(), fsols.begin(), fsols.end()); - } - else - { - conjBody = conj[1][1].substitute( - fvars.begin(), fvars.end(), fsols.begin(), fsols.end()); - - /* Skolemize property */ - std::vector<Node> vars, skos; - for (unsigned j = 0, size = conj[1][0].getNumChildren(); j < size; ++j) - { - vars.push_back(conj[1][0][j]); - std::stringstream ss; - ss << "sk_" << j; - skos.push_back(nm->mkSkolem(ss.str(), conj[1][0][j].getType())); - Trace("check-synth-sol") << "\tSkolemizing " << conj[1][0][j] << " to " - << skos.back() << "\n"; - } - conjBody = conjBody.substitute( - vars.begin(), vars.end(), skos.begin(), skos.end()); - } - Notice() << "SmtEngine::checkSynthSolution(): -- body substitutes to " - << conjBody << endl; - Trace("check-synth-sol") << "Substituted body of assertion to " << conjBody - << "\n"; - solChecker.assertFormula(conjBody); - // Assert all auxiliary assertions. This may include recursive function - // definitions that were added as assertions to the sygus problem. - for (const Node& a : auxAssertions) - { - solChecker.assertFormula(a); - } - Result r = solChecker.checkSat(); - Notice() << "SmtEngine::checkSynthSolution(): result is " << r << endl; - Trace("check-synth-sol") << "Satsifiability check: " << r << "\n"; - if (r.asSatisfiabilityResult().isUnknown()) - { - InternalError() << "SmtEngine::checkSynthSolution(): could not check " - "solution, result " - "unknown."; - } - else if (r.asSatisfiabilityResult().isSat()) - { - InternalError() - << "SmtEngine::checkSynthSolution(): produced solution leads to " - "satisfiable negated conjecture."; - } - solChecker.resetAssertions(); - } -} - -void SmtEngine::checkInterpol(Expr interpol, - const std::vector<Expr>& easserts, - const Node& conj) -{ -} - -void SmtEngine::checkAbduct(Node a) -{ - Assert(a.getType().isBoolean()); - // check it with the abduction solver - return d_abductSolver->checkAbduct(a); + // check the model with the check models utility + Assert(d_checkModels != nullptr); + d_checkModels->checkModel(m, al, hardFailure); } // TODO(#1108): Simplify the error reporting of this method. UnsatCore SmtEngine::getUnsatCore() { Trace("smt") << "SMT getUnsatCore()" << endl; SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); if(Dump.isOn("benchmark")) { - Dump("benchmark") << GetUnsatCoreCommand(); + getOutputManager().getPrinter().toStreamCmdGetUnsatCore( + getOutputManager().getDumpOut()); } return getUnsatCoreInternal(); } -// TODO(#1108): Simplify the error reporting of this method. -const 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_smtMode != SMT_MODE_UNSAT) - { - throw RecoverableModalException( - "Cannot get a proof unless immediately preceded by UNSAT/ENTAILED " - "response."); - } - - 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); - finalOptionsAreSet(); + finishInit(); if (options::instFormatMode() == options::InstFormatMode::SZS) { - out << "% SZS output start Proof for " << d_filename.c_str() << std::endl; - } - if( d_theoryEngine ){ - d_theoryEngine->printInstantiations( out ); - }else{ - Assert(false); + out << "% SZS output start Proof for " << d_state->getFilename() + << std::endl; } + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->printInstantiations(out); if (options::instFormatMode() == options::InstFormatMode::SZS) { - out << "% SZS output end Proof for " << d_filename.c_str() << std::endl; + out << "% SZS output end Proof for " << d_state->getFilename() << std::endl; } } void SmtEngine::printSynthSolution( std::ostream& out ) { SmtScope smts(this); - finalOptionsAreSet(); - if( d_theoryEngine ){ - d_theoryEngine->printSynthSolution( out ); - }else{ - Assert(false); - } + finishInit(); + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->printSynthSolution(out); } -bool SmtEngine::getSynthSolutions(std::map<Expr, Expr>& sol_map) +bool SmtEngine::getSynthSolutions(std::map<Node, Node>& solMap) { SmtScope smts(this); - finalOptionsAreSet(); - std::map<Node, std::map<Node, Node>> sol_mapn; - Assert(d_theoryEngine != nullptr); - // fail if the theory engine does not have synthesis solutions - if (!d_theoryEngine->getSynthSolutions(sol_mapn)) - { - return false; - } - for (std::pair<const Node, std::map<Node, Node>>& cs : sol_mapn) - { - for (std::pair<const Node, Node>& s : cs.second) - { - sol_map[s.first.toExpr()] = s.second.toExpr(); - } - } - return true; + finishInit(); + return d_sygusSolver->getSynthSolutions(solMap); } -Expr SmtEngine::doQuantifierElimination(const Expr& e, bool doFull, bool strict) +Node SmtEngine::getQuantifierElimination(Node q, bool doFull, bool strict) { SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); 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 && n_e.getKind() != kind::FORALL) - { - throw ModalException( - "Expecting a 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.getKind() == kind::EXISTS ? n_e[1] - : n_e[1].negate()); - 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 ){ - Notice() - << "While performing quantifier elimination, unexpected result : " - << r << " for query."; - // failed, return original - return e; - } - std::vector< Node > inst_qs; - d_theoryEngine->getInstantiatedQuantifiedFormulas( inst_qs ); - Assert(inst_qs.size() <= 1); - Node ret_n; - if( inst_qs.size()==1 ){ - Node top_q = inst_qs[0]; - //Node top_q = Rewriter::rewrite( nn_e ).negate(); - Assert(top_q.getKind() == kind::FORALL); - Trace("smt-qe") << "Get qe for " << top_q << std::endl; - ret_n = d_theoryEngine->getInstantiatedConjunction( top_q ); - Trace("smt-qe") << "Returned : " << ret_n << std::endl; - if (n_e.getKind() == kind::EXISTS) - { - ret_n = Rewriter::rewrite(ret_n.negate()); - } - }else{ - ret_n = NodeManager::currentNM()->mkConst(n_e.getKind() != kind::EXISTS); - } - // do extended rewrite to minimize the size of the formula aggressively - theory::quantifiers::ExtendedRewriter extr(true); - ret_n = extr.extendedRewrite(ret_n); - return ret_n.toExpr(); - }else { - return NodeManager::currentNM() - ->mkConst(n_e.getKind() == kind::EXISTS) - .toExpr(); - } + return d_quantElimSolver->getQuantifierElimination(*d_asserts, q, doFull); } -bool SmtEngine::getInterpol(const Expr& conj, - const Type& grammarType, - Expr& interpol) +bool SmtEngine::getInterpol(const Node& conj, + const TypeNode& grammarType, + Node& interpol) { - return false; + SmtScope smts(this); + finishInit(); + bool success = d_interpolSolver->getInterpol(conj, grammarType, interpol); + // notify the state of whether the get-interpol call was successfuly, which + // impacts the SMT mode. + d_state->notifyGetInterpol(success); + return success; } -bool SmtEngine::getInterpol(const Expr& conj, Expr& interpol) +bool SmtEngine::getInterpol(const Node& conj, Node& interpol) { - Type grammarType; + TypeNode grammarType; return getInterpol(conj, grammarType, interpol); } @@ -3163,15 +1537,13 @@ bool SmtEngine::getAbduct(const Node& conj, const TypeNode& grammarType, Node& abd) { - if (d_abductSolver->getAbduct(conj, grammarType, abd)) - { - // successfully generated an abduct, update to abduct state - d_smtMode = SMT_MODE_ABDUCT; - return true; - } - // failed, we revert to the assert state - d_smtMode = SMT_MODE_ASSERT; - return false; + SmtScope smts(this); + finishInit(); + bool success = d_abductSolver->getAbduct(conj, grammarType, abd); + // notify the state of whether the get-abduct call was successfuly, which + // impacts the SMT mode. + d_state->notifyGetAbduct(success); + return success; } bool SmtEngine::getAbduct(const Node& conj, Node& abd) @@ -3180,56 +1552,31 @@ bool SmtEngine::getAbduct(const Node& conj, Node& abd) return getAbduct(conj, grammarType, abd); } -void SmtEngine::getInstantiatedQuantifiedFormulas( std::vector< Expr >& qs ) { +void SmtEngine::getInstantiatedQuantifiedFormulas(std::vector<Node>& qs) +{ SmtScope smts(this); - if( d_theoryEngine ){ - std::vector< Node > qs_n; - d_theoryEngine->getInstantiatedQuantifiedFormulas( qs_n ); - for( unsigned i=0; i<qs_n.size(); i++ ){ - qs.push_back( qs_n[i].toExpr() ); - } - }else{ - Assert(false); - } + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->getInstantiatedQuantifiedFormulas(qs); } -void SmtEngine::getInstantiations( Expr q, std::vector< Expr >& 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<insts_n.size(); i++ ){ - insts.push_back( insts_n[i].toExpr() ); - } - }else{ - Assert(false); - } -} - -void SmtEngine::getInstantiationTermVectors( Expr q, std::vector< std::vector< Expr > >& tvecs ) { +void SmtEngine::getInstantiationTermVectors( + Node q, std::vector<std::vector<Node>>& 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<tvecs_n.size(); i++ ){ - std::vector< Expr > tvec; - for( unsigned j=0; j<tvecs_n[i].size(); j++ ){ - tvec.push_back( tvecs_n[i][j].toExpr() ); - } - tvecs.push_back( tvec ); - } - }else{ - Assert(false); - } + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->getInstantiationTermVectors(q, tvecs); } -vector<Expr> SmtEngine::getAssertions() { +std::vector<Node> SmtEngine::getAssertions() +{ SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); + finishInit(); + d_state->doPendingPops(); if(Dump.isOn("benchmark")) { - Dump("benchmark") << GetAssertionsCommand(); + getOutputManager().getPrinter().toStreamCmdGetAssertions( + getOutputManager().getDumpOut()); } Trace("smt") << "SMT getAssertions()" << endl; if(!options::produceAssertions()) { @@ -3237,11 +1584,12 @@ vector<Expr> SmtEngine::getAssertions() { "Cannot query the current assertion list when not in produce-assertions mode."; throw ModalException(msg); } - Assert(d_assertionList != NULL); - std::vector<Expr> res; - for (const Node& n : *d_assertionList) + context::CDList<Node>* al = d_asserts->getAssertionList(); + Assert(al != nullptr); + std::vector<Node> res; + for (const Node& n : *al) { - res.emplace_back(n.toExpr()); + res.emplace_back(n); } // copy the result out return res; @@ -3250,188 +1598,102 @@ vector<Expr> SmtEngine::getAssertions() { void SmtEngine::push() { SmtScope smts(this); - finalOptionsAreSet(); - doPendingPops(); + finishInit(); + d_state->doPendingPops(); Trace("smt") << "SMT push()" << endl; - d_private->notifyPush(); - d_private->processAssertions(); + d_smtSolver->processAssertions(*d_asserts); if(Dump.isOn("benchmark")) { - Dump("benchmark") << PushCommand(); - } - if(!options::incrementalSolving()) { - throw ModalException("Cannot push when not solving incrementally (use --incremental)"); + getOutputManager().getPrinter().toStreamCmdPush( + getOutputManager().getDumpOut()); } - - - // The problem isn't really "extended" yet, but this disallows - // get-model after a push, simplifying our lives somewhat and - // staying symmetric with pop. - setProblemExtended(); - - d_userLevels.push_back(d_userContext->getLevel()); - internalPush(); - Trace("userpushpop") << "SmtEngine: pushed to level " - << d_userContext->getLevel() << endl; + d_state->userPush(); } void SmtEngine::pop() { SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); 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)"); + getOutputManager().getPrinter().toStreamCmdPop( + getOutputManager().getDumpOut()); } - if(d_userLevels.size() == 0) { - throw ModalException("Cannot pop beyond the first user frame"); - } - - // 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!). - setProblemExtended(); - - 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(); + d_state->userPop(); // Clear out assertion queues etc., in case anything is still in there - d_private->notifyPop(); + d_asserts->clearCurrent(); + // clear the learned literals from the preprocessor + d_pp->clearLearnedLiterals(); Trace("userpushpop") << "SmtEngine: popped to level " - << d_userContext->getLevel() << endl; - // FIXME: should we reset d_status here? + << getUserContext()->getLevel() << endl; + // 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() { - Trace("smt") << "SmtEngine::doPendingPops()" << endl; - Assert(d_pendingPops == 0 || options::incrementalSolving()); - // check to see if a postsolve() is pending - if (d_needPostsolve) - { - d_propEngine->resetTrail(); - } - 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; - } - if (d_needPostsolve) - { - d_theoryEngine->postsolve(); - d_needPostsolve = false; - } -} - void SmtEngine::reset() { SmtScope smts(this); ExprManager *em = d_exprManager; Trace("smt") << "SMT reset()" << endl; if(Dump.isOn("benchmark")) { - Dump("benchmark") << ResetCommand(); + getOutputManager().getPrinter().toStreamCmdReset( + getOutputManager().getDumpOut()); } + std::string filename = d_state->getFilename(); Options opts; opts.copyValues(d_originalOptions); this->~SmtEngine(); new (this) SmtEngine(em, &opts); + // Restore data set after creation + notifyStartParsing(filename); } void SmtEngine::resetAssertions() { SmtScope smts(this); - if (!d_fullyInited) + if (!d_state->isFullyInited()) { // We're still in Start Mode, nothing asserted yet, do nothing. // (see solver execution modes in the SMT-LIB standard) - Assert(d_context->getLevel() == 0); - Assert(d_userContext->getLevel() == 0); - DeleteAndClearCommandVector(d_modelGlobalCommands); + Assert(getContext()->getLevel() == 0); + Assert(getUserContext()->getLevel() == 0); + d_dumpm->resetAssertions(); return; } - doPendingPops(); Trace("smt") << "SMT resetAssertions()" << endl; if (Dump.isOn("benchmark")) { - Dump("benchmark") << ResetAssertionsCommand(); - } - - while (!d_userLevels.empty()) - { - pop(); + getOutputManager().getPrinter().toStreamCmdResetAssertions( + getOutputManager().getDumpOut()); } - // Remember the global push/pop around everything when beyond Start mode - // (see solver execution modes in the SMT-LIB standard) - 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(); + d_asserts->clearCurrent(); + d_state->notifyResetAssertions(); + d_dumpm->resetAssertions(); + // push the state to maintain global context around everything + d_state->setup(); - /* Create new PropEngine. - * First force destruction of referenced PropEngine to enforce that - * statistics are unregistered by the obsolete PropEngine object before - * registered again by the new PropEngine object */ - d_propEngine.reset(nullptr); - d_propEngine.reset(new PropEngine( - getTheoryEngine(), getContext(), getUserContext(), getResourceManager())); - d_theoryEngine->setPropEngine(getPropEngine()); + d_smtSolver->resetAssertions(); } void SmtEngine::interrupt() { - if(!d_fullyInited) { + if (!d_state->isFullyInited()) + { return; } - d_propEngine->interrupt(); - d_theoryEngine->interrupt(); + d_smtSolver->interrupt(); } void SmtEngine::setResourceLimit(unsigned long units, bool cumulative) { d_resourceManager->setResourceLimit(units, cumulative); } -void SmtEngine::setTimeLimit(unsigned long milis, bool cumulative) { - d_resourceManager->setTimeLimit(milis, cumulative); +void SmtEngine::setTimeLimit(unsigned long milis) +{ + d_resourceManager->setTimeLimit(milis); } unsigned long SmtEngine::getResourceUsage() const { @@ -3447,11 +1709,6 @@ unsigned long SmtEngine::getResourceRemaining() const return d_resourceManager->getResourceRemaining(); } -unsigned long SmtEngine::getTimeRemaining() const -{ - return d_resourceManager->getTimeRemaining(); -} - NodeManager* SmtEngine::getNodeManager() const { return d_exprManager->getNodeManager(); @@ -3477,52 +1734,32 @@ void SmtEngine::setUserAttribute(const std::string& attr, const std::string& str_value) { SmtScope smts(this); - finalOptionsAreSet(); + finishInit(); std::vector<Node> node_values; - for( unsigned i=0; i<expr_values.size(); i++ ){ + for (std::size_t i = 0, n = expr_values.size(); i < n; i++) + { node_values.push_back( expr_values[i].getNode() ); } - d_theoryEngine->setUserAttribute(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<d_modelGlobalCommands.size(); i++ ){ - Command * c = d_modelGlobalCommands[i]; - DeclareFunctionCommand* dfc = dynamic_cast<DeclareFunctionCommand*>(c); - if(dfc != NULL) { - if( dfc->getFunction()==f ){ - dfc->setPrintInModel( p ); - } - } - } - for( unsigned i=0; i<d_modelCommands->size(); i++ ){ - Command * c = (*d_modelCommands)[i]; - DeclareFunctionCommand* dfc = dynamic_cast<DeclareFunctionCommand*>(c); - if(dfc != NULL) { - if( dfc->getFunction()==f ){ - dfc->setPrintInModel( p ); - } - } - } -} - -void SmtEngine::beforeSearch() -{ - if(d_fullyInited) { - throw ModalException( - "SmtEngine::beforeSearch called after initialization."); - } + TheoryEngine* te = getTheoryEngine(); + Assert(te != nullptr); + te->setUserAttribute(attr, expr.getNode(), node_values, str_value); } - void SmtEngine::setOption(const std::string& key, const CVC4::SExpr& value) { + // Always check whether the SmtEngine has been initialized (which is done + // upon entering Assert mode the first time). No option can be set after + // initialized. + if (d_state->isFullyInited()) + { + throw ModalException("SmtEngine::setOption called after initialization."); + } NodeManagerScope nms(d_nodeManager); Trace("smt") << "SMT setOption(" << key << ", " << value << ")" << endl; if(Dump.isOn("benchmark")) { - Dump("benchmark") << SetOptionCommand(key, value); + getOutputManager().getPrinter().toStreamCmdSetOption( + getOutputManager().getDumpOut(), key, value); } if(key == "command-verbosity") { @@ -3575,7 +1812,7 @@ CVC4::SExpr SmtEngine::getOption(const std::string& key) const } if(Dump.isOn("benchmark")) { - Dump("benchmark") << GetOptionCommand(key); + d_outMgr.getPrinter().toStreamCmdGetOption(d_outMgr.getDumpOut(), key); } if(key == "command-verbosity") { @@ -3610,15 +1847,6 @@ CVC4::SExpr SmtEngine::getOption(const std::string& key) const return SExpr::parseAtom(d_options.getOption(key)); } -bool SmtEngine::getExpressionName(Expr e, std::string& name) const { - return d_private->getExpressionName(e, name); -} - -void SmtEngine::setExpressionName(Expr e, const std::string& name) { - Trace("smt-debug") << "Set expression name " << e << " to " << name << std::endl; - d_private->setExpressionName(e,name); -} - Options& SmtEngine::getOptions() { return d_options; } const Options& SmtEngine::getOptions() const { return d_options; } @@ -3628,18 +1856,13 @@ ResourceManager* SmtEngine::getResourceManager() return d_resourceManager.get(); } -void SmtEngine::setSygusConjectureStale() +DumpManager* SmtEngine::getDumpManager() { return d_dumpm.get(); } + +const Printer* SmtEngine::getPrinter() const { - if (d_private->d_sygusConjectureStale) - { - // already stale - return; - } - d_private->d_sygusConjectureStale = true; - if (options::incrementalSolving()) - { - internalPop(); - } + return Printer::getPrinter(d_options[options::outputLanguage]); } +OutputManager& SmtEngine::getOutputManager() { return d_outMgr; } + }/* CVC4 namespace */ |