/********************* */ /*! \file theory_arith.cpp ** \verbatim ** Top contributors (to current version): ** Andrew Reynolds, Tim King, Alex Ozdemir ** This file is part of the CVC4 project. ** Copyright (c) 2009-2021 by the authors listed in the file AUTHORS ** in the top-level source directory and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief [[ Add one-line brief description here ]] ** ** [[ Add lengthier description here ]] ** \todo document this file **/ #include "theory/arith/theory_arith.h" #include "expr/proof_rule.h" #include "options/smt_options.h" #include "smt/smt_statistics_registry.h" #include "theory/arith/arith_rewriter.h" #include "theory/arith/infer_bounds.h" #include "theory/arith/nl/nonlinear_extension.h" #include "theory/arith/theory_arith_private.h" #include "theory/ext_theory.h" #include "theory/rewriter.h" #include "theory/theory_model.h" using namespace std; using namespace CVC5::kind; namespace CVC5 { namespace theory { namespace arith { TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, ProofNodeManager* pnm) : Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, pnm), d_internal( new TheoryArithPrivate(*this, c, u, out, valuation, logicInfo, pnm)), d_ppRewriteTimer("theory::arith::ppRewriteTimer"), d_ppPfGen(pnm, c, "Arith::ppRewrite"), d_astate(*d_internal, c, u, valuation), d_im(*this, d_astate, pnm), d_nonlinearExtension(nullptr), d_arithPreproc(d_astate, d_im, pnm, logicInfo) { smtStatisticsRegistry()->registerStat(&d_ppRewriteTimer); // indicate we are using the theory state object and inference manager d_theoryState = &d_astate; d_inferManager = &d_im; } TheoryArith::~TheoryArith(){ smtStatisticsRegistry()->unregisterStat(&d_ppRewriteTimer); delete d_internal; } TheoryRewriter* TheoryArith::getTheoryRewriter() { return &d_rewriter; } bool TheoryArith::needsEqualityEngine(EeSetupInfo& esi) { return d_internal->needsEqualityEngine(esi); } void TheoryArith::finishInit() { if (getLogicInfo().isTheoryEnabled(THEORY_ARITH) && getLogicInfo().areTranscendentalsUsed()) { // witness is used to eliminate square root d_valuation.setUnevaluatedKind(kind::WITNESS); // we only need to add the operators that are not syntax sugar d_valuation.setUnevaluatedKind(kind::EXPONENTIAL); d_valuation.setUnevaluatedKind(kind::SINE); d_valuation.setUnevaluatedKind(kind::PI); } // only need to create nonlinear extension if non-linear logic const LogicInfo& logicInfo = getLogicInfo(); if (logicInfo.isTheoryEnabled(THEORY_ARITH) && !logicInfo.isLinear()) { d_nonlinearExtension.reset( new nl::NonlinearExtension(*this, d_astate, d_equalityEngine, d_pnm)); } // finish initialize internally d_internal->finishInit(); } void TheoryArith::preRegisterTerm(TNode n) { if (d_nonlinearExtension != nullptr) { d_nonlinearExtension->preRegisterTerm(n); } d_internal->preRegisterTerm(n); } TrustNode TheoryArith::expandDefinition(Node node) { // call eliminate operators, to eliminate partial operators only std::vector lems; TrustNode ret = d_arithPreproc.eliminate(node, lems, true); Assert(lems.empty()); return ret; } void TheoryArith::notifySharedTerm(TNode n) { d_internal->notifySharedTerm(n); } TrustNode TheoryArith::ppRewrite(TNode atom, std::vector& lems) { CodeTimer timer(d_ppRewriteTimer, /* allow_reentrant = */ true); Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl; if (atom.getKind() == kind::EQUAL) { return ppRewriteEq(atom); } Assert(Theory::theoryOf(atom) == THEORY_ARITH); // Eliminate operators. Notice we must do this here since other // theories may generate lemmas that involve non-standard operators. For // example, quantifier instantiation may use TO_INTEGER terms; SyGuS may // introduce non-standard arithmetic terms appearing in grammars. // call eliminate operators. In contrast to expandDefinitions, we eliminate // *all* extended arithmetic operators here, including total ones. return d_arithPreproc.eliminate(atom, lems, false); } TrustNode TheoryArith::ppRewriteEq(TNode atom) { Assert(atom.getKind() == kind::EQUAL); if (!options::arithRewriteEq()) { return TrustNode::null(); } Assert(atom[0].getType().isReal()); Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1]; Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1]; Node rewritten = Rewriter::rewrite(leq.andNode(geq)); Debug("arith::preprocess") << "arith::preprocess() : returning " << rewritten << endl; // don't need to rewrite terms since rewritten is not a non-standard op if (proofsEnabled()) { return d_ppPfGen.mkTrustedRewrite( atom, rewritten, d_pnm->mkNode(PfRule::INT_TRUST, {}, {atom.eqNode(rewritten)})); } return TrustNode::mkTrustRewrite(atom, rewritten, nullptr); } Theory::PPAssertStatus TheoryArith::ppAssert( TrustNode tin, TrustSubstitutionMap& outSubstitutions) { return d_internal->ppAssert(tin, outSubstitutions); } void TheoryArith::ppStaticLearn(TNode n, NodeBuilder<>& learned) { d_internal->ppStaticLearn(n, learned); } bool TheoryArith::preCheck(Effort level) { Trace("arith-check") << "TheoryArith::preCheck " << level << std::endl; return d_internal->preCheck(level); } void TheoryArith::postCheck(Effort level) { Trace("arith-check") << "TheoryArith::postCheck " << level << std::endl; // check with the non-linear solver at last call if (level == Theory::EFFORT_LAST_CALL) { if (d_nonlinearExtension != nullptr) { d_nonlinearExtension->check(level); } return; } // otherwise, check with the linear solver if (d_internal->postCheck(level)) { // linear solver emitted a conflict or lemma, return return; } if (Theory::fullEffort(level)) { if (d_nonlinearExtension != nullptr) { d_nonlinearExtension->check(level); } else if (d_internal->foundNonlinear()) { // set incomplete d_im.setIncomplete(); } } } bool TheoryArith::preNotifyFact( TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) { Trace("arith-check") << "TheoryArith::preNotifyFact: " << fact << ", isPrereg=" << isPrereg << ", isInternal=" << isInternal << std::endl; d_internal->preNotifyFact(atom, pol, fact); // We do not assert to the equality engine of arithmetic in the standard way, // hence we return "true" to indicate we are finished with this fact. return true; } bool TheoryArith::needsCheckLastEffort() { if (d_nonlinearExtension != nullptr) { return d_nonlinearExtension->needsCheckLastEffort(); } return false; } TrustNode TheoryArith::explain(TNode n) { return d_internal->explain(n); } void TheoryArith::propagate(Effort e) { d_internal->propagate(e); } bool TheoryArith::collectModelInfo(TheoryModel* m, const std::set& termSet) { // this overrides behavior to not assert equality engine return collectModelValues(m, termSet); } bool TheoryArith::collectModelValues(TheoryModel* m, const std::set& termSet) { // get the model from the linear solver std::map arithModel; d_internal->collectModelValues(termSet, arithModel); // if non-linear is enabled, intercept the model, which may repair its values if (d_nonlinearExtension != nullptr) { // Non-linear may repair values to satisfy non-linear constraints (see // documentation for NonlinearExtension::interceptModel). d_nonlinearExtension->interceptModel(arithModel, termSet); } // We are now ready to assert the model. for (const std::pair& p : arithModel) { // maps to constant of comparable type Assert(p.first.getType().isComparableTo(p.second.getType())); if (m->assertEquality(p.first, p.second, true)) { continue; } // If we failed to assert an equality, it is likely due to theory // combination, namely the repaired model for non-linear changed // an equality status that was agreed upon by both (linear) arithmetic // and another theory. In this case, we must add a lemma, or otherwise // we would terminate with an invalid model. Thus, we add a splitting // lemma of the form ( x = v V x != v ) where v is the model value // assigned by the non-linear solver to x. if (d_nonlinearExtension != nullptr) { Node eq = p.first.eqNode(p.second); Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, eq.negate()); bool added = d_im.lemma(lem, InferenceId::ARITH_SPLIT_FOR_NL_MODEL); AlwaysAssert(added) << "The lemma was already in cache. Probably there is something wrong with theory combination..."; } return false; } return true; } void TheoryArith::notifyRestart(){ d_internal->notifyRestart(); } void TheoryArith::presolve(){ d_internal->presolve(); if (d_nonlinearExtension != nullptr) { d_nonlinearExtension->presolve(); } } EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) { return d_internal->getEqualityStatus(a,b); } Node TheoryArith::getModelValue(TNode var) { return d_internal->getModelValue( var ); } std::pair TheoryArith::entailmentCheck(TNode lit) { ArithEntailmentCheckParameters def; def.addLookupRowSumAlgorithms(); ArithEntailmentCheckSideEffects ase; std::pair res = d_internal->entailmentCheck(lit, def, ase); return res; } eq::ProofEqEngine* TheoryArith::getProofEqEngine() { return d_im.getProofEqEngine(); } } // namespace arith } // namespace theory } // namespace CVC5