From: Andrew Reynolds Date: Mon, 24 May 2021 18:51:09 +0000 (-0500) Subject: Move proof utilities to src/proof/ (#6611) X-Git-Tag: cvc5-1.0.0~1702 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=bd33d20609999f6f847aeb63a42350aeb3041406;p=cvc5.git Move proof utilities to src/proof/ (#6611) This moves all generic proof utilites from src/expr/ and src/theory/ to src/proof/. It also changes the include for term conversion proof generator to conv_proof_generator in preparation to rename this utility on a followup PR (to avoid confusion with the use of "Term"). --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5362baad8..025f10117 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -136,9 +136,50 @@ libcvc5_add_sources( printer/smt2/smt2_printer.h printer/tptp/tptp_printer.cpp printer/tptp/tptp_printer.h + proof/buffered_proof_generator.cpp + proof/buffered_proof_generator.h + proof/conv_proof_generator.cpp + proof/conv_proof_generator.h + proof/conv_seq_proof_generator.cpp + proof/conv_seq_proof_generator.h proof/clause_id.h proof/dot/dot_printer.cpp proof/dot/dot_printer.h + proof/eager_proof_generator.cpp + proof/eager_proof_generator.h + proof/lazy_proof.cpp + proof/lazy_proof.h + proof/lazy_proof_chain.cpp + proof/lazy_proof_chain.h + proof/lazy_tree_proof_generator.cpp + proof/lazy_tree_proof_generator.h + proof/proof.cpp + proof/proof.h + proof/proof_checker.cpp + proof/proof_checker.h + proof/proof_ensure_closed.cpp + proof/proof_ensure_closed.h + proof/proof_generator.cpp + proof/proof_generator.h + proof/proof_node.cpp + proof/proof_node.h + proof/proof_node_algorithm.cpp + proof/proof_node_algorithm.h + proof/proof_node_to_sexpr.cpp + proof/proof_node_to_sexpr.h + proof/proof_node_manager.cpp + proof/proof_node_manager.h + proof/proof_node_updater.cpp + proof/proof_node_updater.h + proof/proof_rule.cpp + proof/proof_rule.h + proof/proof_set.h + proof/proof_step_buffer.cpp + proof/proof_step_buffer.h + proof/trust_node.cpp + proof/trust_node.h + proof/theory_proof_step_buffer.cpp + proof/theory_proof_step_buffer.h proof/unsat_core.cpp proof/unsat_core.h prop/bv_sat_solver_notify.h @@ -600,8 +641,6 @@ libcvc5_add_sources( theory/decision_manager.h theory/decision_strategy.cpp theory/decision_strategy.h - theory/eager_proof_generator.cpp - theory/eager_proof_generator.h theory/ee_manager.cpp theory/ee_manager.h theory/ee_manager_distributed.cpp @@ -631,8 +670,6 @@ libcvc5_add_sources( theory/inference_id.h theory/inference_manager_buffered.cpp theory/inference_manager_buffered.h - theory/lazy_tree_proof_generator.cpp - theory/lazy_tree_proof_generator.h theory/logic_info.cpp theory/logic_info.h theory/model_manager.cpp @@ -1029,14 +1066,10 @@ libcvc5_add_sources( theory/theory_model_builder.h theory/theory_preprocessor.cpp theory/theory_preprocessor.h - theory/theory_proof_step_buffer.cpp - theory/theory_proof_step_buffer.h theory/theory_rewriter.cpp theory/theory_rewriter.h theory/theory_state.cpp theory/theory_state.h - theory/trust_node.cpp - theory/trust_node.h theory/trust_substitutions.cpp theory/trust_substitutions.h theory/type_enumerator.h diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt index 9bf63dcfc..094c7bcbd 100644 --- a/src/expr/CMakeLists.txt +++ b/src/expr/CMakeLists.txt @@ -24,8 +24,6 @@ libcvc5_add_sources( attribute_unique_id.h bound_var_manager.cpp bound_var_manager.h - buffered_proof_generator.cpp - buffered_proof_generator.h emptyset.cpp emptyset.h emptybag.cpp @@ -33,10 +31,6 @@ libcvc5_add_sources( expr_iomanip.cpp expr_iomanip.h kind_map.h - lazy_proof.cpp - lazy_proof.h - lazy_proof_chain.cpp - lazy_proof_chain.h match_trie.cpp match_trie.h node.cpp @@ -58,37 +52,12 @@ libcvc5_add_sources( sequence.cpp sequence.h node_visitor.h - proof.cpp - proof.h - proof_checker.cpp - proof_checker.h - proof_ensure_closed.cpp - proof_ensure_closed.h - proof_generator.cpp - proof_generator.h - proof_node.cpp - proof_node.h - proof_node_algorithm.cpp - proof_node_algorithm.h - proof_node_to_sexpr.cpp - proof_node_to_sexpr.h - proof_node_manager.cpp - proof_node_manager.h - proof_node_updater.cpp - proof_node_updater.h - proof_rule.cpp - proof_rule.h - proof_set.h - proof_step_buffer.cpp - proof_step_buffer.h skolem_manager.cpp skolem_manager.h symbol_manager.cpp symbol_manager.h symbol_table.cpp symbol_table.h - tconv_seq_proof_generator.cpp - tconv_seq_proof_generator.h term_canonize.cpp term_canonize.h term_context.cpp @@ -97,8 +66,6 @@ libcvc5_add_sources( term_context_node.h term_context_stack.cpp term_context_stack.h - term_conversion_proof_generator.cpp - term_conversion_proof_generator.h type_checker.h type_matcher.cpp type_matcher.h diff --git a/src/expr/buffered_proof_generator.cpp b/src/expr/buffered_proof_generator.cpp deleted file mode 100644 index 2cbbd7e91..000000000 --- a/src/expr/buffered_proof_generator.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of a proof generator for buffered proof steps. - */ - -#include "expr/buffered_proof_generator.h" - -#include "expr/proof.h" -#include "expr/proof_node_manager.h" - -namespace cvc5 { - -BufferedProofGenerator::BufferedProofGenerator(context::Context* c, - ProofNodeManager* pnm) - : ProofGenerator(), d_facts(c), d_pnm(pnm) -{ -} - -bool BufferedProofGenerator::addStep(Node fact, - ProofStep ps, - CDPOverwrite opolicy) -{ - // check duplicates if we are not always overwriting - if (opolicy != CDPOverwrite::ALWAYS) - { - if (d_facts.find(fact) != d_facts.end()) - { - // duplicate - return false; - } - Node symFact = CDProof::getSymmFact(fact); - if (!symFact.isNull()) - { - if (d_facts.find(symFact) != d_facts.end()) - { - // duplicate due to symmetry - return false; - } - } - } - // note that this replaces the value fact is mapped to if there is already one - d_facts.insert(fact, std::make_shared(ps)); - return true; -} - -std::shared_ptr BufferedProofGenerator::getProofFor(Node fact) -{ - Trace("pfee-fact-gen") << "BufferedProofGenerator::getProofFor: " << fact - << std::endl; - NodeProofStepMap::iterator it = d_facts.find(fact); - if (it == d_facts.end()) - { - Node symFact = CDProof::getSymmFact(fact); - if (symFact.isNull()) - { - Trace("pfee-fact-gen") << "...cannot find step" << std::endl; - Assert(false); - return nullptr; - } - it = d_facts.find(symFact); - if (it == d_facts.end()) - { - Assert(false); - Trace("pfee-fact-gen") << "...cannot find step (no sym)" << std::endl; - return nullptr; - } - } - Trace("pfee-fact-gen") << "...return via step " << *(*it).second << std::endl; - CDProof cdp(d_pnm); - cdp.addStep(fact, *(*it).second); - return cdp.getProofFor(fact); -} - -bool BufferedProofGenerator::hasProofFor(Node f) -{ - NodeProofStepMap::iterator it = d_facts.find(f); - if (it == d_facts.end()) - { - Node symFact = CDProof::getSymmFact(f); - if (symFact.isNull()) - { - return false; - } - it = d_facts.find(symFact); - if (it == d_facts.end()) - { - return false; - } - } - return true; -} - -} // namespace cvc5 diff --git a/src/expr/buffered_proof_generator.h b/src/expr/buffered_proof_generator.h deleted file mode 100644 index 2e1ef6c53..000000000 --- a/src/expr/buffered_proof_generator.h +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Haniel Barbosa, Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * A proof generator for buffered proof steps. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__BUFFERED_PROOF_GENERATOR_H -#define CVC5__EXPR__BUFFERED_PROOF_GENERATOR_H - -#include "context/cdhashmap.h" -#include "expr/proof_generator.h" - -namespace cvc5 { - -class ProofNodeManager; -class ProofStep; - -/** - * The proof generator for buffered steps. This class is a context-dependent - * mapping from formulas to proof steps. It does not generate ProofNodes until it - * is asked to provide a proof for a given fact. - */ -class BufferedProofGenerator : public ProofGenerator -{ - typedef context::CDHashMap> NodeProofStepMap; - - public: - BufferedProofGenerator(context::Context* c, ProofNodeManager* pnm); - ~BufferedProofGenerator() {} - /** add step - * Unless the overwrite policy is ALWAYS it does not replace previously - * registered steps (modulo (dis)equality symmetry). - */ - bool addStep(Node fact, - ProofStep ps, - CDPOverwrite opolicy = CDPOverwrite::NEVER); - /** Get proof for. It is robust to (dis)equality symmetry. */ - std::shared_ptr getProofFor(Node f) override; - /** Whether a step has been registered for f. */ - bool hasProofFor(Node f) override; - /** identify */ - std::string identify() const override { return "BufferedProofGenerator"; } - - private: - /** maps expected to ProofStep */ - NodeProofStepMap d_facts; - /** the proof node manager */ - ProofNodeManager* d_pnm; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__BUFFERED_PROOF_GENERATOR_H */ diff --git a/src/expr/lazy_proof.cpp b/src/expr/lazy_proof.cpp deleted file mode 100644 index 8a54637fa..000000000 --- a/src/expr/lazy_proof.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of lazy proof utility. - */ - -#include "expr/lazy_proof.h" - -#include "expr/proof_ensure_closed.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -LazyCDProof::LazyCDProof(ProofNodeManager* pnm, - ProofGenerator* dpg, - context::Context* c, - std::string name) - : CDProof(pnm, c, name), d_gens(c ? c : &d_context), d_defaultGen(dpg) -{ -} - -LazyCDProof::~LazyCDProof() {} - -std::shared_ptr LazyCDProof::getProofFor(Node fact) -{ - Trace("lazy-cdproof") << "LazyCDProof::mkLazyProof " << fact << std::endl; - // make the proof, which should always be non-null, since we construct an - // assumption in the worst case. - std::shared_ptr opf = CDProof::getProofFor(fact); - Assert(opf != nullptr); - if (!hasGenerators()) - { - Trace("lazy-cdproof") << "...no generators, finished" << std::endl; - // optimization: no generators, we are done - return opf; - } - // otherwise, we traverse the proof opf and fill in the ASSUME leafs that - // have generators - std::unordered_set visited; - std::unordered_set::iterator it; - std::vector visit; - ProofNode* cur; - visit.push_back(opf.get()); - do - { - cur = visit.back(); - visit.pop_back(); - it = visited.find(cur); - - if (it == visited.end()) - { - visited.insert(cur); - Node cfact = cur->getResult(); - if (getProof(cfact).get() != cur) - { - // We don't own this proof, skip it. This is to ensure that this method - // is idempotent, since it may be the case that a previous call to - // getProofFor connected a proof from a proof generator as a child of - // a ProofNode in the range of the map in CDProof. Thus, this ensures - // we don't touch such proofs. - Trace("lazy-cdproof") << "...skip unowned proof" << std::endl; - } - else if (cur->getRule() == PfRule::ASSUME) - { - bool isSym = false; - ProofGenerator* pg = getGeneratorFor(cfact, isSym); - if (pg != nullptr) - { - Trace("lazy-cdproof") - << "LazyCDProof: Call generator " << pg->identify() - << " for assumption " << cfact << std::endl; - Node cfactGen = isSym ? CDProof::getSymmFact(cfact) : cfact; - Assert(!cfactGen.isNull()); - // Do not use the addProofTo interface, instead use the update node - // interface, since this ensures that we don't take ownership for - // the current proof. Instead, it is only linked, and ignored on - // future calls to getProofFor due to the check above. - std::shared_ptr pgc = pg->getProofFor(cfactGen); - // If the proof was null, then the update is not performed. This is - // not considered an error, since this behavior is equivalent to - // if pg had provided the proof (ASSUME cfactGen). Ensuring the - // proper behavior wrt closed proofs should be done outside this - // method. - if (pgc != nullptr) - { - Trace("lazy-cdproof-gen") - << "LazyCDProof: stored proof: " << *pgc.get() << std::endl; - - if (isSym) - { - d_manager->updateNode(cur, PfRule::SYMM, {pgc}, {}); - } - else - { - d_manager->updateNode(cur, pgc.get()); - } - Trace("lazy-cdproof") << "LazyCDProof: Successfully added fact for " - << cfactGen << std::endl; - } - } - else - { - Trace("lazy-cdproof") << "LazyCDProof: " << identify() - << " : No generator for " << cfact << std::endl; - } - // Notice that we do not traverse the proofs that have been generated - // lazily by the proof generators here. In other words, we assume that - // the proofs from provided proof generators are final and need - // no further modification by this class. - } - else - { - const std::vector>& cc = cur->getChildren(); - for (const std::shared_ptr& cp : cc) - { - visit.push_back(cp.get()); - } - } - } - } while (!visit.empty()); - // we have now updated the ASSUME leafs of opf, return it - Trace("lazy-cdproof") << "...finished" << std::endl; - Assert(opf->getResult() == fact); - return opf; -} - -void LazyCDProof::addLazyStep(Node expected, - ProofGenerator* pg, - PfRule idNull, - bool isClosed, - const char* ctx, - bool forceOverwrite) -{ - if (pg == nullptr) - { - // null generator, should have given a proof rule - if (idNull == PfRule::ASSUME) - { - Unreachable() << "LazyCDProof::addLazyStep: " << identify() - << ": failed to provide proof generator for " << expected; - return; - } - Trace("lazy-cdproof") << "LazyCDProof::addLazyStep: " << expected - << " set (trusted) step " << idNull << "\n"; - addStep(expected, idNull, {}, {expected}); - return; - } - Trace("lazy-cdproof") << "LazyCDProof::addLazyStep: " << expected - << " set to generator " << pg->identify() << "\n"; - if (!forceOverwrite) - { - NodeProofGeneratorMap::const_iterator it = d_gens.find(expected); - if (it != d_gens.end()) - { - // don't overwrite something that is already there - return; - } - } - // just store now - d_gens.insert(expected, pg); - // debug checking - if (isClosed) - { - Trace("lazy-cdproof-debug") << "Checking closed..." << std::endl; - pfgEnsureClosed(expected, pg, "lazy-cdproof-debug", ctx); - } -} - -ProofGenerator* LazyCDProof::getGeneratorFor(Node fact, - bool& isSym) -{ - isSym = false; - NodeProofGeneratorMap::const_iterator it = d_gens.find(fact); - if (it != d_gens.end()) - { - return (*it).second; - } - Node factSym = CDProof::getSymmFact(fact); - // could be symmetry - if (factSym.isNull()) - { - // can't be symmetry, return the default generator - return d_defaultGen; - } - it = d_gens.find(factSym); - if (it != d_gens.end()) - { - isSym = true; - return (*it).second; - } - // return the default generator - return d_defaultGen; -} - -bool LazyCDProof::hasGenerators() const -{ - return !d_gens.empty() || d_defaultGen != nullptr; -} - -bool LazyCDProof::hasGenerator(Node fact) const -{ - if (d_defaultGen != nullptr) - { - return true; - } - NodeProofGeneratorMap::const_iterator it = d_gens.find(fact); - if (it != d_gens.end()) - { - return true; - } - // maybe there is a symmetric fact? - Node factSym = CDProof::getSymmFact(fact); - if (!factSym.isNull()) - { - it = d_gens.find(factSym); - } - return it != d_gens.end(); -} - -} // namespace cvc5 diff --git a/src/expr/lazy_proof.h b/src/expr/lazy_proof.h deleted file mode 100644 index efcda94bd..000000000 --- a/src/expr/lazy_proof.h +++ /dev/null @@ -1,110 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Lazy proof utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__LAZY_PROOF_H -#define CVC5__EXPR__LAZY_PROOF_H - -#include "expr/proof.h" - -namespace cvc5 { - -class ProofGenerator; -class ProofNodeManager; - -/** - * A (context-dependent) lazy proof. This class is an extension of CDProof - * that additionally maps facts to proof generators in a context-dependent - * manner. It extends CDProof with an additional method, addLazyStep for adding - * steps to a proof via a given proof generator. - */ -class LazyCDProof : public CDProof -{ - public: - /** Constructor - * - * @param pnm The proof node manager for constructing ProofNode objects. - * @param dpg The (optional) default proof generator, which is called - * for facts that have no explicitly provided generator. - * @param c The context that this class depends on. If none is provided, - * this class is context-independent. - */ - LazyCDProof(ProofNodeManager* pnm, - ProofGenerator* dpg = nullptr, - context::Context* c = nullptr, - std::string name = "LazyCDProof"); - ~LazyCDProof(); - /** - * Get lazy proof for fact, or nullptr if it does not exist. This may - * additionally call proof generators to generate proofs for ASSUME nodes that - * don't yet have a concrete proof. - */ - std::shared_ptr getProofFor(Node fact) override; - /** Add step by generator - * - * This method stores that expected can be proven by proof generator pg if - * it is required to do so. This mapping is maintained in the remainder of - * the current context (according to the context c provided to this class). - * - * It is important to note that pg is asked to provide a proof for expected - * only when no other call for the fact expected is provided via the addStep - * method of this class. In particular, pg is asked to prove expected when it - * appears as the conclusion of an ASSUME leaf within CDProof::getProofFor. - * - * @param expected The fact that can be proven. - * @param pg The generator that can proof expected. - * @param trustId If a null proof generator is provided, we add a step to - * the proof that has trustId as the rule and expected as the sole argument. - * We do this only if trustId is not PfRule::ASSUME. This is primarily used - * for identifying the kind of hole when a proof generator is not given. - * @param isClosed Whether to expect that pg can provide a closed proof for - * this fact. - * @param ctx The context we are in (for debugging). - * @param forceOverwrite If this flag is true, then this call overwrites - * an existing proof generator provided for expected, if one was provided - * via a previous call to addLazyStep in the current context. - */ - void addLazyStep(Node expected, - ProofGenerator* pg, - PfRule trustId = PfRule::ASSUME, - bool isClosed = false, - const char* ctx = "LazyCDProof::addLazyStep", - bool forceOverwrite = false); - /** - * Does this have any proof generators? This method always returns true - * if the default is non-null. - */ - bool hasGenerators() const; - /** Does the given fact have an explicitly provided generator? */ - bool hasGenerator(Node fact) const; - - protected: - typedef context::CDHashMap NodeProofGeneratorMap; - /** Maps facts that can be proven to generators */ - NodeProofGeneratorMap d_gens; - /** The default proof generator */ - ProofGenerator* d_defaultGen; - /** - * Get generator for fact, or nullptr if it doesnt exist. This method is - * robust to symmetry of (dis)equality. It updates isSym to true if a - * proof generator for the symmetric form of fact was provided. - */ - ProofGenerator* getGeneratorFor(Node fact, bool& isSym); -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__LAZY_PROOF_H */ diff --git a/src/expr/lazy_proof_chain.cpp b/src/expr/lazy_proof_chain.cpp deleted file mode 100644 index 3ce2212be..000000000 --- a/src/expr/lazy_proof_chain.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Haniel Barbosa, Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of lazy proof utility. - */ - -#include "expr/lazy_proof_chain.h" - -#include "expr/proof.h" -#include "expr/proof_ensure_closed.h" -#include "expr/proof_node.h" -#include "expr/proof_node_algorithm.h" -#include "expr/proof_node_manager.h" -#include "options/proof_options.h" - -namespace cvc5 { - -LazyCDProofChain::LazyCDProofChain(ProofNodeManager* pnm, - bool cyclic, - context::Context* c, - ProofGenerator* defGen, - bool defRec) - : d_manager(pnm), - d_cyclic(cyclic), - d_defRec(defRec), - d_context(), - d_gens(c ? c : &d_context), - d_defGen(defGen) -{ -} - -LazyCDProofChain::~LazyCDProofChain() {} - -const std::map> LazyCDProofChain::getLinks() - const -{ - std::map> links; - for (const std::pair& link : d_gens) - { - Assert(link.second); - std::shared_ptr pfn = link.second->getProofFor(link.first); - Assert(pfn); - links[link.first] = pfn; - } - return links; -} - -std::shared_ptr LazyCDProofChain::getProofFor(Node fact) -{ - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor " << fact << "\n"; - // which facts have had proofs retrieved for. This is maintained to avoid - // cycles. It also saves the proof node of the fact - std::unordered_map> toConnect; - // leaves of proof node links in the chain, i.e. assumptions, yet to be - // expanded - std::unordered_map>> - assumptionsToExpand; - // invariant of the loop below, the first iteration notwithstanding: - // visit = domain(assumptionsToExpand) \ domain(toConnect) - std::vector visit{fact}; - std::unordered_map visited; - Node cur; - do - { - cur = visit.back(); - visit.pop_back(); - auto itVisited = visited.find(cur); - // pre-traversal - if (itVisited == visited.end()) - { - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: check " << cur << "\n"; - Assert(toConnect.find(cur) == toConnect.end()); - bool rec = true; - ProofGenerator* pg = getGeneratorForInternal(cur, rec); - if (!pg) - { - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: nothing to do\n"; - // nothing to do for this fact, it'll be a leaf in the final proof - // node, don't post-traverse. - visited[cur] = true; - continue; - } - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: Call generator " << pg->identify() - << " for chain link " << cur << "\n"; - std::shared_ptr curPfn = pg->getProofFor(cur); - if (curPfn == nullptr) - { - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: No proof found, skip\n"; - visited[cur] = true; - continue; - } - // map node whose proof node must be expanded to the respective poof node - toConnect[cur] = curPfn; - if (!rec) - { - // we don't want to recursively connect this proof - visited[cur] = true; - continue; - } - Trace("lazy-cdproofchain-debug") - << "LazyCDProofChain::getProofFor: stored proof: " << *curPfn.get() - << "\n"; - // retrieve free assumptions and their respective proof nodes - std::map>> famap; - expr::getFreeAssumptionsMap(curPfn, famap); - if (Trace.isOn("lazy-cdproofchain")) - { - unsigned alreadyToVisit = 0; - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: " << famap.size() - << " free assumptions:\n"; - for (auto fap : famap) - { - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: - " << fap.first << "\n"; - alreadyToVisit += - std::find(visit.begin(), visit.end(), fap.first) != visit.end() - ? 1 - : 0; - } - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: " << alreadyToVisit - << " already to visit\n"; - } - // mark for post-traversal if we are controlling cycles - if (d_cyclic) - { - Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: marking " - << cur << " for cycle check\n"; - visit.push_back(cur); - visited[cur] = false; - } - else - { - visited[cur] = true; - } - // enqueue free assumptions to process - for (const std::pair>>& - fap : famap) - { - // check cycles - if (d_cyclic) - { - // cycles are assumptions being *currently* expanded and seen again, - // i.e. in toConnect and not yet post-visited - auto itToConnect = toConnect.find(fap.first); - if (itToConnect != toConnect.end() && !visited[fap.first]) - { - // Since we have a cycle with an assumption, this fact will be an - // assumption in the final proof node produced by this - // method. Thus we erase it as something to be connected, which - // will keep it as an assumption. - Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: " - "removing cyclic assumption " - << fap.first << " from expansion\n"; - continue; - } - } - // We always add assumptions to visit so that their last seen occurrence - // is expanded (rather than the first seen occurrence, if we were not - // adding assumptions, say, in assumptionsToExpand). This is so because - // in the case where we are checking cycles this is necessary (and - // harmless when we are not). For example the cycle - // - // a2 - // ... - // ---- - // a1 - // ... - // -------- - // a0 a1 a2 - // ... - // --------------- - // n - // - // in which a2 has a1 as an assumption, which has a2 an assumption, - // would not be captured if we did not revisit a1, which is an - // assumption of n and this already occur in assumptionsToExpand when - // it shows up again as an assumption of a2. - visit.push_back(fap.first); - // add assumption proof nodes to be updated - assumptionsToExpand[fap.first].insert( - assumptionsToExpand[fap.first].end(), - fap.second.begin(), - fap.second.end()); - } - if (d_cyclic) - { - Trace("lazy-cdproofchain") << push; - Trace("lazy-cdproofchain-debug") << push; - } - } - else if (!itVisited->second) - { - visited[cur] = true; - Trace("lazy-cdproofchain") << pop; - Trace("lazy-cdproofchain-debug") << pop; - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: post-visited " << cur << "\n"; - } - else - { - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: already fully processed " << cur - << "\n"; - } - } while (!visit.empty()); - // expand all assumptions marked to be connected - for (const std::pair>& npfn : - toConnect) - { - auto it = assumptionsToExpand.find(npfn.first); - if (it == assumptionsToExpand.end()) - { - Assert(npfn.first == fact); - continue; - } - Assert(npfn.second); - Trace("lazy-cdproofchain") - << "LazyCDProofChain::getProofFor: expand assumption " << npfn.first - << "\n"; - Trace("lazy-cdproofchain-debug") - << "LazyCDProofChain::getProofFor: ...expand to " << *npfn.second.get() - << "\n"; - // update each assumption proof node - for (std::shared_ptr pfn : it->second) - { - d_manager->updateNode(pfn.get(), npfn.second.get()); - } - } - // final proof of fact - auto it = toConnect.find(fact); - if (it == toConnect.end()) - { - return nullptr; - } - return it->second; -} - -void LazyCDProofChain::addLazyStep(Node expected, ProofGenerator* pg) -{ - Assert(pg != nullptr); - Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected - << " set to generator " << pg->identify() << "\n"; - // note this will replace the generator for expected, if any - d_gens.insert(expected, pg); -} - -void LazyCDProofChain::addLazyStep(Node expected, - ProofGenerator* pg, - const std::vector& assumptions, - const char* ctx) -{ - Assert(pg != nullptr); - Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected - << " set to generator " << pg->identify() << "\n"; - // note this will rewrite the generator for expected, if any - d_gens.insert(expected, pg); - // check if chain is closed if options::proofEagerChecking() is on - if (options::proofEagerChecking()) - { - Trace("lazy-cdproofchain") - << "LazyCDProofChain::addLazyStep: Checking closed proof...\n"; - std::shared_ptr pfn = pg->getProofFor(expected); - std::vector allowedLeaves{assumptions.begin(), assumptions.end()}; - // add all current links in the chain - for (const std::pair& link : d_gens) - { - allowedLeaves.push_back(link.first); - } - if (Trace.isOn("lazy-cdproofchain")) - { - Trace("lazy-cdproofchain") << "Checking relative to leaves...\n"; - for (const Node& n : allowedLeaves) - { - Trace("lazy-cdproofchain") << " - " << n << "\n"; - } - Trace("lazy-cdproofchain") << "\n"; - } - pfnEnsureClosedWrt(pfn.get(), allowedLeaves, "lazy-cdproofchain", ctx); - } -} - -bool LazyCDProofChain::hasGenerator(Node fact) const -{ - return d_gens.find(fact) != d_gens.end(); -} - -ProofGenerator* LazyCDProofChain::getGeneratorFor(Node fact) -{ - bool rec = true; - return getGeneratorForInternal(fact, rec); -} - -ProofGenerator* LazyCDProofChain::getGeneratorForInternal(Node fact, bool& rec) -{ - auto it = d_gens.find(fact); - if (it != d_gens.end()) - { - return (*it).second; - } - // otherwise, if no explicit generators, we use the default one - if (d_defGen != nullptr) - { - rec = d_defRec; - return d_defGen; - } - return nullptr; -} - -std::string LazyCDProofChain::identify() const { return "LazyCDProofChain"; } - -} // namespace cvc5 diff --git a/src/expr/lazy_proof_chain.h b/src/expr/lazy_proof_chain.h deleted file mode 100644 index 1abb3f84e..000000000 --- a/src/expr/lazy_proof_chain.h +++ /dev/null @@ -1,154 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Haniel Barbosa, Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Lazy proof chain utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__LAZY_PROOF_CHAIN_H -#define CVC5__EXPR__LAZY_PROOF_CHAIN_H - -#include - -#include "context/cdhashmap.h" -#include "expr/proof_generator.h" - -namespace cvc5 { - -class ProofNodeManager; - -/** - * A (context-dependent) lazy generator for proof chains. This class is an - * extension of ProofGenerator that additionally that maps facts to proof - * generators in a context-dependent manner. The map is built with the addition - * of lazy steps mapping facts to proof generators. More importantly, its - * getProofFor method expands the proof generators registered to this class by - * connecting, for the proof generated to one fact, assumptions to the proofs - * generated for those assumptinos that are registered in the chain. - */ -class LazyCDProofChain : public ProofGenerator -{ - public: - /** Constructor - * - * @param pnm The proof node manager for constructing ProofNode objects. - * @param cyclic Whether this instance is robust to cycles in the chain. - * @param c The context that this class depends on. If none is provided, - * this class is context-independent. - * @param defGen The default generator to be used if no generator exists - * for a step. - * @param defRec Whether this instance expands proofs from defGen recursively. - */ - LazyCDProofChain(ProofNodeManager* pnm, - bool cyclic = true, - context::Context* c = nullptr, - ProofGenerator* defGen = nullptr, - bool defRec = true); - ~LazyCDProofChain(); - /** - * Get lazy proof for fact, or nullptr if it does not exist, by connecting the - * proof nodes generated for each intermediate relevant fact registered in the - * chain (i.e., in the domain of d_gens). - * - * For example, if d_gens consists of the following pairs - * - * --- (A, PG1) - * --- (B, PG2) - * --- (C, PG3) - * - * and getProofFor(A) is called, with PG1 generating a proof with assumptions - * B and D, then B is expanded, with its assumption proof node being updated - * to the expanded proof node, while D is not. Assuming PG2 provides a proof - * with assumptions C and E, then C is expanded and E is not. Suppose PG3 - * gives a closed proof, thus the call getProofFor(A) produces a proof node - * - * A : ( ... B : ( ... C : (...), ... ASSUME( E ) ), ... ASSUME( D ) ) - * - * Note that the expansions are done directly on the proof nodes produced by - * the generators. - * - * If this instance has been set to be robust to cyclic proofs (i.e., d_cyclic - * is true), then the construction of the proof chain checks that there are no - * cycles, i.e., a given fact would have itself as an assumption when - * connecting the chain links. If such a cycle were to be detected then the - * fact will be marked as an assumption and not expanded in the final proof - * node. The method does not fail. - */ - std::shared_ptr getProofFor(Node fact) override; - /** Add step by generator - * - * This method stores that expected can be proven by proof generator pg if - * it is required to do so. This mapping is maintained in the remainder of - * the current context (according to the context c provided to this class). - * - * Moreover the lazy steps of this class are expected to fulfill the - * requirement that pg.getProofFor(expected) generates a proof node closed - * with relation to - * (1) a fixed set F1, ..., Fn, - * (2) formulas in the current domain of d_gens. - * - * This is so that we only add links to the chain that depend on a fixed set - * of assumptions or in other links. - * - * @param expected The fact that can be proven. - * @param pg The generator that can proof expected. - * @param assumptions The fixed set of assumptions with relation to which the - * chain, now augmented with expect, must be closed. - * @param ctx The context we are in (for debugging). - */ - void addLazyStep(Node expected, - ProofGenerator* pg, - const std::vector& assumptions, - const char* ctx = "LazyCDProofChain::addLazyStep"); - - /** As above but does not do the closedness check. */ - void addLazyStep(Node expected, ProofGenerator* pg); - - /** Does the given fact have an explicitly provided generator? */ - bool hasGenerator(Node fact) const; - - /** - * Get generator for fact, or nullptr if it doesnt exist. - */ - ProofGenerator* getGeneratorFor(Node fact); - - /** identify */ - std::string identify() const override; - - /** Retrieve, for each fact in d_gens, it mapped to the proof node generated - * by its generator in d_gens. */ - const std::map> getLinks() const; - - private: - /** - * Get generator for fact, or nullptr if it doesnt exist. Updates rec to - * true if we should recurse on its proof. - */ - ProofGenerator* getGeneratorForInternal(Node fact, bool& rec); - /** The proof manager, used for allocating new ProofNode objects */ - ProofNodeManager* d_manager; - /** Whether this instance is robust to cycles in the chain. */ - bool d_cyclic; - /** Whether we expand recursively (for the default generator) */ - bool d_defRec; - /** A dummy context used by this class if none is provided */ - context::Context d_context; - /** Maps facts that can be proven to generators */ - context::CDHashMap d_gens; - /** The default proof generator (if one exists) */ - ProofGenerator* d_defGen; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__LAZY_PROOF_CHAIN_H */ diff --git a/src/expr/proof.cpp b/src/expr/proof.cpp deleted file mode 100644 index 289a5be5b..000000000 --- a/src/expr/proof.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof. - */ - -#include "expr/proof.h" - -#include "expr/proof_checker.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -CDProof::CDProof(ProofNodeManager* pnm, - context::Context* c, - std::string name, - bool autoSymm) - : d_manager(pnm), - d_context(), - d_nodes(c ? c : &d_context), - d_name(name), - d_autoSymm(autoSymm) -{ -} - -CDProof::~CDProof() {} - -std::shared_ptr CDProof::getProofFor(Node fact) -{ - std::shared_ptr pf = getProofSymm(fact); - if (pf != nullptr) - { - return pf; - } - // add as assumption - std::vector pargs = {fact}; - std::vector> passume; - std::shared_ptr pfa = - d_manager->mkNode(PfRule::ASSUME, passume, pargs, fact); - d_nodes.insert(fact, pfa); - return pfa; -} - -std::shared_ptr CDProof::getProof(Node fact) const -{ - NodeProofNodeMap::iterator it = d_nodes.find(fact); - if (it != d_nodes.end()) - { - return (*it).second; - } - return nullptr; -} - -std::shared_ptr CDProof::getProofSymm(Node fact) -{ - Trace("cdproof") << "CDProof::getProofSymm: " << fact << std::endl; - std::shared_ptr pf = getProof(fact); - if (pf != nullptr && !isAssumption(pf.get())) - { - Trace("cdproof") << "...existing non-assume " << pf->getRule() << std::endl; - return pf; - } - else if (!d_autoSymm) - { - Trace("cdproof") << "...not auto considering symmetry" << std::endl; - return pf; - } - Node symFact = getSymmFact(fact); - if (symFact.isNull()) - { - Trace("cdproof") << "...no possible symm" << std::endl; - // no symmetry possible, return original proof (possibly assumption) - return pf; - } - // See if a proof exists for the opposite direction, if so, add the step. - // Notice that SYMM is also disallowed. - std::shared_ptr pfs = getProof(symFact); - if (pfs != nullptr) - { - // The symmetric fact exists, and the current one either does not, or is - // an assumption. We make a new proof that applies SYMM to pfs. - std::vector> pschild; - pschild.push_back(pfs); - std::vector args; - if (pf == nullptr) - { - Trace("cdproof") << "...fresh make symm" << std::endl; - std::shared_ptr psym = - d_manager->mkNode(PfRule::SYMM, pschild, args, fact); - Assert(psym != nullptr); - d_nodes.insert(fact, psym); - return psym; - } - else if (!isAssumption(pfs.get())) - { - // if its not an assumption, make the connection - Trace("cdproof") << "...update symm" << std::endl; - // update pf - bool sret = d_manager->updateNode(pf.get(), PfRule::SYMM, pschild, args); - AlwaysAssert(sret); - } - } - else - { - Trace("cdproof") << "...no symm, return " - << (pf == nullptr ? "null" : "non-null") << std::endl; - } - // return original proof (possibly assumption) - return pf; -} - -bool CDProof::addStep(Node expected, - PfRule id, - const std::vector& children, - const std::vector& args, - bool ensureChildren, - CDPOverwrite opolicy) -{ - Trace("cdproof") << "CDProof::addStep: " << identify() << " : " << id << " " - << expected << ", ensureChildren = " << ensureChildren - << ", overwrite policy = " << opolicy << std::endl; - Trace("cdproof-debug") << "CDProof::addStep: " << identify() - << " : children: " << children << "\n"; - Trace("cdproof-debug") << "CDProof::addStep: " << identify() - << " : args: " << args << "\n"; - // We must always provide expected to this method - Assert(!expected.isNull()); - - std::shared_ptr pprev = getProofSymm(expected); - if (pprev != nullptr) - { - if (!shouldOverwrite(pprev.get(), id, opolicy)) - { - // we should not overwrite the current step - Trace("cdproof") << "...success, no overwrite" << std::endl; - return true; - } - Trace("cdproof") << "existing proof " << pprev->getRule() - << ", overwrite..." << std::endl; - // we will overwrite the existing proof node by updating its contents below - } - // collect the child proofs, for each premise - std::vector> pchildren; - for (const Node& c : children) - { - Trace("cdproof") << "- get child " << c << std::endl; - std::shared_ptr pc = getProofSymm(c); - if (pc == nullptr) - { - if (ensureChildren) - { - // failed to get a proof for a child, fail - Trace("cdproof") << "...fail, no child" << std::endl; - return false; - } - Trace("cdproof") << "--- add assume" << std::endl; - // otherwise, we initialize it as an assumption - std::vector pcargs = {c}; - std::vector> pcassume; - pc = d_manager->mkNode(PfRule::ASSUME, pcassume, pcargs, c); - // assumptions never fail to check - Assert(pc != nullptr); - d_nodes.insert(c, pc); - } - pchildren.push_back(pc); - } - - // the user may have provided SYMM of an assumption - if (id == PfRule::SYMM) - { - Assert(pchildren.size() == 1); - if (isAssumption(pchildren[0].get())) - { - // the step we are constructing is a (symmetric fact of an) assumption, so - // there is no use adding it to the proof. - return true; - } - } - - bool ret = true; - // create or update it - std::shared_ptr pthis; - if (pprev == nullptr) - { - Trace("cdproof") << " new node " << expected << "..." << std::endl; - pthis = d_manager->mkNode(id, pchildren, args, expected); - if (pthis == nullptr) - { - // failed to construct the node, perhaps due to a proof checking failure - Trace("cdproof") << "...fail, proof checking" << std::endl; - return false; - } - d_nodes.insert(expected, pthis); - } - else - { - Trace("cdproof") << " update node " << expected << "..." << std::endl; - // update its value - pthis = pprev; - // We return the value of updateNode here. This means this method may return - // false if this call failed, regardless of whether we already have a proof - // step for expected. - ret = d_manager->updateNode(pthis.get(), id, pchildren, args); - } - if (ret) - { - // the result of the proof node should be expected - Assert(pthis->getResult() == expected); - - // notify new proof - notifyNewProof(expected); - } - - Trace("cdproof") << "...return " << ret << std::endl; - return ret; -} - -void CDProof::notifyNewProof(Node expected) -{ - if (!d_autoSymm) - { - return; - } - // ensure SYMM proof is also linked to an existing proof, if it is an - // assumption. - Node symExpected = getSymmFact(expected); - if (!symExpected.isNull()) - { - Trace("cdproof") << " check connect symmetry " << symExpected << std::endl; - // if it exists, we may need to update it - std::shared_ptr pfs = getProof(symExpected); - if (pfs != nullptr) - { - Trace("cdproof") << " connect via getProofSymm method..." << std::endl; - // call the get function with symmetry, which will do the update - std::shared_ptr pfss = getProofSymm(symExpected); - } - else - { - Trace("cdproof") << " no connect" << std::endl; - } - } -} - -bool CDProof::addStep(Node expected, - const ProofStep& step, - bool ensureChildren, - CDPOverwrite opolicy) -{ - return addStep(expected, - step.d_rule, - step.d_children, - step.d_args, - ensureChildren, - opolicy); -} - -bool CDProof::addSteps(const ProofStepBuffer& psb, - bool ensureChildren, - CDPOverwrite opolicy) -{ - const std::vector>& steps = psb.getSteps(); - for (const std::pair& ps : steps) - { - if (!addStep(ps.first, ps.second, ensureChildren, opolicy)) - { - return false; - } - } - return true; -} - -bool CDProof::addProof(std::shared_ptr pn, - CDPOverwrite opolicy, - bool doCopy) -{ - if (!doCopy) - { - // If we aren't doing a deep copy, we either store pn or link its top - // node into the existing pointer - Node curFact = pn->getResult(); - std::shared_ptr cur = getProofSymm(curFact); - if (cur == nullptr) - { - // Assert that the checker of this class agrees with (the externally - // provided) pn. This ensures that if pn was checked by a different - // checker than the one of the manager in this class, then it is double - // checked here, so that this class maintains the invariant that all of - // its nodes in d_nodes have been checked by the underlying checker. - Assert(d_manager->getChecker() == nullptr - || d_manager->getChecker()->check(pn.get(), curFact) == curFact); - // just store the proof for fact - d_nodes.insert(curFact, pn); - } - else if (shouldOverwrite(cur.get(), pn->getRule(), opolicy)) - { - // We update cur to have the structure of the top node of pn. Notice that - // the interface to update this node will ensure that the proof apf is a - // proof of the assumption. If it does not, then pn was wrong. - if (!d_manager->updateNode( - cur.get(), pn->getRule(), pn->getChildren(), pn->getArguments())) - { - return false; - } - } - // also need to connect via SYMM if necessary - notifyNewProof(curFact); - return true; - } - std::unordered_map visited; - std::unordered_map::iterator it; - std::vector visit; - ProofNode* cur; - Node curFact; - visit.push_back(pn.get()); - bool retValue = true; - do - { - cur = visit.back(); - curFact = cur->getResult(); - visit.pop_back(); - it = visited.find(cur); - if (it == visited.end()) - { - // visit the children - visited[cur] = false; - visit.push_back(cur); - const std::vector>& cs = cur->getChildren(); - for (const std::shared_ptr& c : cs) - { - visit.push_back(c.get()); - } - } - else if (!it->second) - { - // we always call addStep, which may or may not overwrite the - // current step - std::vector pexp; - const std::vector>& cs = cur->getChildren(); - for (const std::shared_ptr& c : cs) - { - Assert(!c->getResult().isNull()); - pexp.push_back(c->getResult()); - } - // can ensure children at this point - bool res = addStep( - curFact, cur->getRule(), pexp, cur->getArguments(), true, opolicy); - // should always succeed - Assert(res); - retValue = retValue && res; - visited[cur] = true; - } - } while (!visit.empty()); - - return retValue; -} - -bool CDProof::hasStep(Node fact) -{ - std::shared_ptr pf = getProof(fact); - if (pf != nullptr && !isAssumption(pf.get())) - { - return true; - } - else if (!d_autoSymm) - { - return false; - } - Node symFact = getSymmFact(fact); - if (symFact.isNull()) - { - return false; - } - pf = getProof(symFact); - if (pf != nullptr && !isAssumption(pf.get())) - { - return true; - } - return false; -} - -ProofNodeManager* CDProof::getManager() const { return d_manager; } - -bool CDProof::shouldOverwrite(ProofNode* pn, PfRule newId, CDPOverwrite opol) -{ - Assert(pn != nullptr); - // we overwrite only if opol is CDPOverwrite::ALWAYS, or if - // opol is CDPOverwrite::ASSUME_ONLY and the previously - // provided proof pn was an assumption and the currently provided step is not - return opol == CDPOverwrite::ALWAYS - || (opol == CDPOverwrite::ASSUME_ONLY && isAssumption(pn) - && newId != PfRule::ASSUME); -} - -bool CDProof::isAssumption(ProofNode* pn) -{ - PfRule rule = pn->getRule(); - if (rule == PfRule::ASSUME) - { - return true; - } - else if (rule == PfRule::SYMM) - { - const std::vector>& pc = pn->getChildren(); - Assert(pc.size() == 1); - return pc[0]->getRule() == PfRule::ASSUME; - } - return false; -} - -bool CDProof::isSame(TNode f, TNode g) -{ - if (f == g) - { - return true; - } - Kind fk = f.getKind(); - Kind gk = g.getKind(); - if (fk == EQUAL && gk == EQUAL && f[0] == g[1] && f[1] == g[0]) - { - // symmetric equality - return true; - } - if (fk == NOT && gk == NOT && f[0].getKind() == EQUAL - && g[0].getKind() == EQUAL && f[0][0] == g[0][1] && f[0][1] == g[0][0]) - { - // symmetric disequality - return true; - } - return false; -} - -Node CDProof::getSymmFact(TNode f) -{ - bool polarity = f.getKind() != NOT; - TNode fatom = polarity ? f : f[0]; - if (fatom.getKind() != EQUAL || fatom[0] == fatom[1]) - { - return Node::null(); - } - Node symFact = fatom[1].eqNode(fatom[0]); - return polarity ? symFact : symFact.notNode(); -} - -std::string CDProof::identify() const { return d_name; } - -} // namespace cvc5 diff --git a/src/expr/proof.h b/src/expr/proof.h deleted file mode 100644 index 496815938..000000000 --- a/src/expr/proof.h +++ /dev/null @@ -1,278 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_H -#define CVC5__EXPR__PROOF_H - -#include - -#include "context/cdhashmap.h" -#include "expr/node.h" -#include "expr/proof_generator.h" -#include "expr/proof_step_buffer.h" - -namespace cvc5 { - -class ProofNode; -class ProofNodeManager; - -/** - * A (context-dependent) proof. - * - * This maintains a context-dependent map from formulas to ProofNode shared - * pointers. When a step is added, it internally uses mutable ProofNode pointers - * to link the steps in the proof together. - * - * It is important to note that the premises of proof steps given to this class - * are *Node* not *ProofNode*. In other words, the user of this class does - * not have to manage ProofNode pointers while using this class. Instead, - * ProofNode is the final product of this class, via the getProof interface, - * which returns a ProofNode object that has linked together the proof steps - * added to this object. - * - * Its main interface is the function addStep, which registers a single - * step in the proof. Its interface is: - * addStep( , , , , ...... ) - * where conclusion is what to be proven, rule is an identifier, premises - * are the formulas that imply conclusion, and args are additional arguments to - * the proof rule application. The options include whether we ensure children - * proofs already exist for the premises and our policty for overwriting - * existing steps. - * - * As an example, let A, B, C, D be formulas represented by Nodes. If we - * call: - * - addStep( A, ID_A, { B, C }, {} ) - * - addStep( B, ID_B, { D }, {} ) - * - addStep( E, ID_E, {}, {} ) - * with default options, then getProof( A ) returns the ProofNode of the form: - * ID_A( ID_B( ASSUME( D ) ), ASSUME( C ) ) - * Notice that the above calls to addStep can be made in either order. The - * proof step for E was not involved in the proof of A. - * - * If the user wants to combine proofs, then a addProof interface is - * available. The method addProof can be seen as syntax sugar for making - * multiple calls to addStep. Continuing the above example, if we call: - * - addProof( F, ID_F( ASSUME( A ), ASSUME( E ) ) ) - * is the same as calling steps corresponding the steps in the provided proof - * bottom-up, starting from the leaves. - * --- addStep( A, ASSUME, {}, {}, true ) - * --- addStep( E, ASSUME, {}, {}, true ) - * --- addStep( F, ID_F, { A, E }, {}, true ) - * The fifth argument provided indicates that we want to ensure child proofs - * are already available for the step (see ensureChildren in addStep below). - * This will result in getProof( F ) returning: - * ID_F( ID_A( ID_B( ASSUME( D ) ), ASSUME( C ) ), ID_E() ) - * Notice that the proof of A by ID_A was not overwritten by ASSUME in the - * addStep call above. - * - * The default policy for overwriting proof steps (CDPOverwrite::ASSUME_ONLY) - * gives ASSUME a special status. An ASSUME step can be seen as a step whose - * justification has not yet been provided. Thus, it is always overwritten. - * Other proof rules are never overwritten, unless the argument opolicy is - * CDPOverwrite::ALWAYS. - * - * As an another example, say that we call: - * - addStep( B, ID_B1 {}, {} ) - * - addStep( A, ID_A1, {B, C}, {} ) - * At this point, getProof( A ) returns: - * ID_A1( ID_B1(), ASSUME(C) ) - * Now, assume an additional call is made to addProof, where notice - * the overwrite policy is CDPOverwrite::ASSUME_ONLY by default: - * - addProof( D, ID_D( ID_A2( ID_B2(), ID_C() ) ) ) - * where assume ID_B2() and ID_C() prove B and C respectively. This call is - * equivalent to calling: - * --- addStep( B, ID_B2, {}, {}, true ) - * --- addStep( C, ID_C, {}, {}, true ) - * --- addStep( A, ID_A2, {B, C}, {}, true ) - * --- addStep( D, ID_D, {A}, {}, true ) - * Afterwards, getProof( D ) returns: - * ID_D( ID_A1( ID_B1(), ID_C() ) ) - * Notice that the steps with ID_A1 and ID_B1 were not overwritten by this call, - * whereas the assumption of C was overwritten by the proof ID_C(). Notice that - * the proof of D was completely traversed in addProof, despite encountering - * formulas, A and B, that were already given proof steps. - * - * This class requires a ProofNodeManager object, which is a trusted way of - * allocating ProofNode pointers. This manager may have an underlying checker, - * although this is not required. It also (optionally) takes a context c. - * If no context is provided, then this proof is context-independent. This - * is implemented internally by using a dummy context that is never pushed - * or popped. The map from Nodes to ProofNodes is context-dependent and is - * backtracked when its context backtracks. - * - * An important invariant of this object is that there exists (at most) one - * proof step for each Node. Thus, the ProofNode objects returned by this - * class share proofs for common subformulas, as guaranteed by the fact that - * Node objects have perfect sharing. - * - * Notice that this class is agnostic to symmetry of equality. In other - * words, adding a step that concludes (= x y) is effectively the same as - * providing a step that concludes (= y x) from the point of view of a user - * of this class. This is accomplished by adding SYMM steps on demand when - * a formula is looked up. For example say we call: - * - addStep( (= x y), ID_1 {}, {} ) - * - addStep( P, ID_2, {(= y x)}, {} ) - * Afterwards, getProof( P ) returns: - * ID_2( SYMM( ID_1() ) ) - * where notice SYMM was added so that (= y x) is a premise of the application - * of ID_2. More generally, CDProof::isSame(F,G) returns true if F and G are - * essentially the same formula according to this class. - */ -class CDProof : public ProofGenerator -{ - public: - /** - * @param pnm The proof node manager responsible for constructor ProofNode - * @param c The context this proof depends on - * @param name The name of this proof (for debugging) - * @param autoSymm Whether this proof automatically adds symmetry steps based - * on policy documented above. - */ - CDProof(ProofNodeManager* pnm, - context::Context* c = nullptr, - std::string name = "CDProof", - bool autoSymm = true); - virtual ~CDProof(); - /** - * Make proof for fact. - * - * This method always returns a non-null ProofNode. It may generate new - * steps so that a ProofNode can be constructed for fact. In particular, - * if no step exists for fact, then we may construct and return ASSUME(fact). - * If fact is of the form (= t s), no step exists for fact, but a proof - * P for (= s t) exists, then this method returns SYMM(P). - * - * Notice that this call does *not* clone the ProofNode object. Hence, the - * returned proof may be updated by further calls to this class. The caller - * should call ProofNode::clone if they want to own it. - */ - std::shared_ptr getProofFor(Node fact) override; - /** Add step - * - * @param expected The intended conclusion of this proof step. This must be - * non-null. - * @param id The identifier for the proof step. - * @param children The antecendants of the proof step. Each children[i] should - * be a fact previously added as a conclusion of an addStep call when - * ensureChildren is true. - * @param args The arguments of the proof step. - * @param ensureChildren Whether we wish to ensure steps have been added - * for all nodes in children - * @param opolicy Policy for whether to overwrite if a step for - * expected was already provided (via a previous call to addStep) - * @return The true if indeed the proof step proves expected, or - * false otherwise. The latter can happen if the proof has a different (or - * invalid) conclusion, or if one of the children does not have a proof and - * ensureChildren is true. - * - * This method may create proofs justified by ASSUME of the facts in - * children that have not already been proven when ensureChildren is false. - * Notice that ensureChildren should be true if the proof is being - * constructed is a strictly eager fashion (bottom up from its leaves), while - * ensureChildren should be false if the steps are added lazily or out - * of order. - * - * This method only overwrites proofs for facts that were added as - * steps with id ASSUME when opolicy is CDPOverwrite::ASSUME_ONLY, and always - * (resp. never) overwrites an existing step if one was provided when opolicy - * is CDPOverwrite::ALWAYS (resp. CDPOverwrite::NEVER). - */ - bool addStep(Node expected, - PfRule id, - const std::vector& children, - const std::vector& args, - bool ensureChildren = false, - CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY); - /** Version with ProofStep */ - bool addStep(Node expected, - const ProofStep& step, - bool ensureChildren = false, - CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY); - /** Version with ProofStepBuffer */ - bool addSteps(const ProofStepBuffer& psb, - bool ensureChildren = false, - CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY); - /** Add proof - * - * @param pn The proof of the given fact. - * @param opolicy Policy for whether to force overwriting if a step - * was already provided. This is used for each node in the proof if doCopy - * is true. - * @param doCopy Whether we make a deep copy of the pn. - * @return true if all steps were successfully added to this class. If it - * returns true, it registers a copy of all of the subnodes of pn to this - * proof class. - * - * If doCopy is true, this method is implemented by calling addStep above for - * all subnodes of pn, traversed as a dag. This means that fresh ProofNode - * objects may be generated by this call, and further modifications to pn do - * not impact the internal data of this class. - */ - bool addProof(std::shared_ptr pn, - CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY, - bool doCopy = false); - /** Return true if fact already has a proof step */ - bool hasStep(Node fact); - /** Get the proof manager for this proof */ - ProofNodeManager* getManager() const; - /** - * Is same? Returns true if f and g are the same formula, or if they are - * symmetric equalities. If two nodes f and g are the same, then a proof for - * f suffices as a proof for g according to this class. - */ - static bool isSame(TNode f, TNode g); - /** Get proof for fact, or nullptr if it does not exist. */ - std::shared_ptr getProof(Node fact) const; - /** - * Get symmetric fact (a g such that isSame returns true for isSame(f,g)), or - * null if none exist. - */ - static Node getSymmFact(TNode f); - /** identify */ - std::string identify() const override; - - protected: - typedef context::CDHashMap> NodeProofNodeMap; - /** The proof manager, used for allocating new ProofNode objects */ - ProofNodeManager* d_manager; - /** A dummy context used by this class if none is provided */ - context::Context d_context; - /** The nodes of the proof */ - NodeProofNodeMap d_nodes; - /** Name identifier */ - std::string d_name; - /** Whether we automatically add symmetry steps */ - bool d_autoSymm; - /** Ensure fact sym */ - std::shared_ptr getProofSymm(Node fact); - /** - * Returns true if we should overwrite proof node pn with a step having id - * newId, based on policy opol. - */ - static bool shouldOverwrite(ProofNode* pn, PfRule newId, CDPOverwrite opol); - /** Returns true if pn is an assumption. */ - static bool isAssumption(ProofNode* pn); - /** - * Notify new proof, called when a new proof of expected is provided. This is - * used internally to connect proofs of symmetric facts, when necessary. - */ - void notifyNewProof(Node expected); -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_MANAGER_H */ diff --git a/src/expr/proof_checker.cpp b/src/expr/proof_checker.cpp deleted file mode 100644 index 69f880ed5..000000000 --- a/src/expr/proof_checker.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof checker. - */ - -#include "expr/proof_checker.h" - -#include "expr/proof_node.h" -#include "expr/skolem_manager.h" -#include "options/proof_options.h" -#include "smt/smt_statistics_registry.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -Node ProofRuleChecker::check(PfRule id, - const std::vector& children, - const std::vector& args) -{ - // call instance-specific checkInternal method - return checkInternal(id, children, args); -} - -bool ProofRuleChecker::getUInt32(TNode n, uint32_t& i) -{ - // must be a non-negative integer constant that fits an unsigned int - if (n.isConst() && n.getType().isInteger() - && n.getConst().sgn() >= 0 - && n.getConst().getNumerator().fitsUnsignedInt()) - { - i = n.getConst().getNumerator().toUnsignedInt(); - return true; - } - return false; -} - -bool ProofRuleChecker::getBool(TNode n, bool& b) -{ - if (n.isConst() && n.getType().isBoolean()) - { - b = n.getConst(); - return true; - } - return false; -} - -bool ProofRuleChecker::getKind(TNode n, Kind& k) -{ - uint32_t i; - if (!getUInt32(n, i)) - { - return false; - } - k = static_cast(i); - return true; -} - -Node ProofRuleChecker::mkKindNode(Kind k) -{ - if (k == UNDEFINED_KIND) - { - // UNDEFINED_KIND is negative, hence return null to avoid cast - return Node::null(); - } - return NodeManager::currentNM()->mkConst(Rational(static_cast(k))); -} - -ProofCheckerStatistics::ProofCheckerStatistics() - : d_ruleChecks(smtStatisticsRegistry().registerHistogram( - "ProofCheckerStatistics::ruleChecks")), - d_totalRuleChecks(smtStatisticsRegistry().registerInt( - "ProofCheckerStatistics::totalRuleChecks")) -{ -} - -Node ProofChecker::check(ProofNode* pn, Node expected) -{ - return check(pn->getRule(), pn->getChildren(), pn->getArguments(), expected); -} - -Node ProofChecker::check( - PfRule id, - const std::vector>& children, - const std::vector& args, - Node expected) -{ - // optimization: immediately return for ASSUME - if (id == PfRule::ASSUME) - { - Assert(children.empty()); - Assert(args.size() == 1 && args[0].getType().isBoolean()); - Assert(expected.isNull() || expected == args[0]); - return expected; - } - // record stat - d_stats.d_ruleChecks << id; - ++d_stats.d_totalRuleChecks; - Trace("pfcheck") << "ProofChecker::check: " << id << std::endl; - std::vector cchildren; - for (const std::shared_ptr& pc : children) - { - Assert(pc != nullptr); - Node cres = pc->getResult(); - if (cres.isNull()) - { - Trace("pfcheck") << "ProofChecker::check: failed child" << std::endl; - Unreachable() - << "ProofChecker::check: child proof was invalid (null conclusion)" - << std::endl; - // should not have been able to create such a proof node - return Node::null(); - } - cchildren.push_back(cres); - if (Trace.isOn("pfcheck")) - { - std::stringstream ssc; - pc->printDebug(ssc); - Trace("pfcheck") << " child: " << ssc.str() << " : " << cres - << std::endl; - } - } - Trace("pfcheck") << " args: " << args << std::endl; - Trace("pfcheck") << " expected: " << expected << std::endl; - std::stringstream out; - // we use trusted (null) checkers here, since we want the proof generation to - // proceed without failing here. We always enable output since a failure - // implies that we will exit with the error message below. - Node res = checkInternal(id, cchildren, args, expected, out, true, true); - if (res.isNull()) - { - Trace("pfcheck") << "ProofChecker::check: failed" << std::endl; - Unreachable() << "ProofChecker::check: failed, " << out.str() << std::endl; - // it did not match the given expectation, fail - return Node::null(); - } - Trace("pfcheck") << "ProofChecker::check: success!" << std::endl; - return res; -} - -Node ProofChecker::checkDebug(PfRule id, - const std::vector& cchildren, - const std::vector& args, - Node expected, - const char* traceTag) -{ - std::stringstream out; - bool traceEnabled = Trace.isOn(traceTag); - // Since we are debugging, we want to treat trusted (null) checkers as - // a failure. We only enable output if the trace is enabled for efficiency. - Node res = - checkInternal(id, cchildren, args, expected, out, false, traceEnabled); - if (traceEnabled) - { - Trace(traceTag) << "ProofChecker::checkDebug: " << id; - if (res.isNull()) - { - Trace(traceTag) << " failed, " << out.str() << std::endl; - } - else - { - Trace(traceTag) << " success" << std::endl; - } - Trace(traceTag) << "cchildren: " << cchildren << std::endl; - Trace(traceTag) << " args: " << args << std::endl; - } - return res; -} - -Node ProofChecker::checkInternal(PfRule id, - const std::vector& cchildren, - const std::vector& args, - Node expected, - std::stringstream& out, - bool useTrustedChecker, - bool enableOutput) -{ - std::map::iterator it = d_checker.find(id); - if (it == d_checker.end()) - { - // no checker for the rule - if (enableOutput) - { - out << "no checker for rule " << id << std::endl; - } - return Node::null(); - } - else if (it->second == nullptr) - { - if (useTrustedChecker) - { - Notice() << "ProofChecker::check: trusting PfRule " << id << std::endl; - // trusted checker - return expected; - } - else - { - if (enableOutput) - { - out << "trusted checker for rule " << id << std::endl; - } - return Node::null(); - } - } - // check it with the corresponding checker - Node res = it->second->check(id, cchildren, args); - if (!expected.isNull()) - { - Node expectedw = expected; - if (res != expectedw) - { - if (enableOutput) - { - out << "result does not match expected value." << std::endl - << " PfRule: " << id << std::endl; - for (const Node& c : cchildren) - { - out << " child: " << c << std::endl; - } - for (const Node& a : args) - { - out << " arg: " << a << std::endl; - } - out << " result: " << res << std::endl - << " expected: " << expected << std::endl; - } - // it did not match the given expectation, fail - return Node::null(); - } - } - // fails if pedantic level is not met - if (options::proofEagerChecking()) - { - std::stringstream serr; - if (isPedanticFailure(id, serr, enableOutput)) - { - if (enableOutput) - { - out << serr.str() << std::endl; - if (Trace.isOn("proof-pedantic")) - { - Trace("proof-pedantic") - << "Failed pedantic check for " << id << std::endl; - Trace("proof-pedantic") << "Expected: " << expected << std::endl; - out << "Expected: " << expected << std::endl; - } - } - return Node::null(); - } - } - return res; -} - -void ProofChecker::registerChecker(PfRule id, ProofRuleChecker* psc) -{ - std::map::iterator it = d_checker.find(id); - if (it != d_checker.end()) - { - // checker is already provided - Notice() << "ProofChecker::registerChecker: checker already exists for " - << id << std::endl; - return; - } - d_checker[id] = psc; -} - -void ProofChecker::registerTrustedChecker(PfRule id, - ProofRuleChecker* psc, - uint32_t plevel) -{ - AlwaysAssert(plevel <= 10) << "ProofChecker::registerTrustedChecker: " - "pedantic level must be 0-10, got " - << plevel << " for " << id; - registerChecker(id, psc); - // overwrites if already there - if (d_plevel.find(id) != d_plevel.end()) - { - Notice() << "ProofChecker::registerTrustedRule: already provided pedantic " - "level for " - << id << std::endl; - } - d_plevel[id] = plevel; -} - -ProofRuleChecker* ProofChecker::getCheckerFor(PfRule id) -{ - std::map::const_iterator it = d_checker.find(id); - if (it == d_checker.end()) - { - return nullptr; - } - return it->second; -} - -uint32_t ProofChecker::getPedanticLevel(PfRule id) const -{ - std::map::const_iterator itp = d_plevel.find(id); - if (itp != d_plevel.end()) - { - return itp->second; - } - return 0; -} - -bool ProofChecker::isPedanticFailure(PfRule id, - std::ostream& out, - bool enableOutput) const -{ - if (d_pclevel == 0) - { - return false; - } - std::map::const_iterator itp = d_plevel.find(id); - if (itp != d_plevel.end()) - { - if (itp->second <= d_pclevel) - { - if (enableOutput) - { - out << "pedantic level for " << id << " not met (rule level is " - << itp->second << " which is at or below the pedantic level " - << d_pclevel << ")"; - bool pedanticTraceEnabled = Trace.isOn("proof-pedantic"); - if (!pedanticTraceEnabled) - { - out << ", use -t proof-pedantic for details"; - } - } - return true; - } - } - return false; -} - -} // namespace cvc5 diff --git a/src/expr/proof_checker.h b/src/expr/proof_checker.h deleted file mode 100644 index e778f687e..000000000 --- a/src/expr/proof_checker.h +++ /dev/null @@ -1,206 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof checker utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_CHECKER_H -#define CVC5__EXPR__PROOF_CHECKER_H - -#include - -#include "expr/node.h" -#include "expr/proof_rule.h" -#include "util/statistics_stats.h" - -namespace cvc5 { - -class ProofChecker; -class ProofNode; - -/** A virtual base class for checking a proof rule */ -class ProofRuleChecker -{ - public: - ProofRuleChecker() {} - virtual ~ProofRuleChecker() {} - /** - * This checks a single step in a proof. - * - * Return the formula that is proven by a proof node with the given id, - * premises and arguments, or null if such a proof node is not well-formed. - * - * Note that the input/output of this method expects to be terms in *Skolem - * form*, which is passed to checkInternal below. Rule checkers may - * convert premises to witness form when necessary. - * - * @param id The id of the proof node to check - * @param children The premises of the proof node to check. These are nodes - * corresponding to the conclusion (ProofNode::getResult) of the children - * of the proof node we are checking in Skolem form. - * @param args The arguments of the proof node to check - * @return The conclusion of the proof node, in Skolem form, if successful or - * null if such a proof node is malformed. - */ - Node check(PfRule id, - const std::vector& children, - const std::vector& args); - - /** get an index from a node, return false if we fail */ - static bool getUInt32(TNode n, uint32_t& i); - /** get a Boolean from a node, return false if we fail */ - static bool getBool(TNode n, bool& b); - /** get a Kind from a node, return false if we fail */ - static bool getKind(TNode n, Kind& k); - /** Make a Kind into a node */ - static Node mkKindNode(Kind k); - - /** Register all rules owned by this rule checker into pc. */ - virtual void registerTo(ProofChecker* pc) {} - - protected: - /** - * This checks a single step in a proof. - * - * @param id The id of the proof node to check - * @param children The premises of the proof node to check. These are nodes - * corresponding to the conclusion (ProofNode::getResult) of the children - * of the proof node we are checking. - * @param args The arguments of the proof node to check - * @return The conclusion of the proof node if successful or null if such a - * proof node is malformed. - */ - virtual Node checkInternal(PfRule id, - const std::vector& children, - const std::vector& args) = 0; -}; - -/** Statistics class */ -class ProofCheckerStatistics -{ - public: - ProofCheckerStatistics(); - /** Counts the number of checks for each kind of proof rule */ - HistogramStat d_ruleChecks; - /** Total number of rule checks */ - IntStat d_totalRuleChecks; -}; - -/** A class for checking proofs */ -class ProofChecker -{ - public: - ProofChecker(uint32_t pclevel = 0) : d_pclevel(pclevel) {} - ~ProofChecker() {} - /** - * Return the formula that is proven by proof node pn, or null if pn is not - * well-formed. If expected is non-null, then we return null if pn does not - * prove expected. - * - * @param pn The proof node to check - * @param expected The (optional) formula that is the expected conclusion of - * the proof node. - * @return The conclusion of the proof node if successful or null if the - * proof is malformed, or if no checker is available for id. - */ - Node check(ProofNode* pn, Node expected = Node::null()); - /** Same as above, with explicit arguments - * - * @param id The id of the proof node to check - * @param children The children of the proof node to check - * @param args The arguments of the proof node to check - * @param expected The (optional) formula that is the expected conclusion of - * the proof node. - * @return The conclusion of the proof node if successful or null if the - * proof is malformed, or if no checker is available for id. - */ - Node check(PfRule id, - const std::vector>& children, - const std::vector& args, - Node expected = Node::null()); - /** - * Same as above, without conclusions instead of proof node children. This - * is used for debugging. In particular, this function does not throw an - * assertion failure when a proof step is malformed and can be used without - * constructing proof nodes. - * - * @param id The id of the proof node to check - * @param children The conclusions of the children of the proof node to check - * @param args The arguments of the proof node to check - * @param expected The (optional) formula that is the expected conclusion of - * the proof node. - * @param traceTag The trace tag to print debug information to - * @return The conclusion of the proof node if successful or null if the - * proof is malformed, or if no checker is available for id. - */ - Node checkDebug(PfRule id, - const std::vector& cchildren, - const std::vector& args, - Node expected = Node::null(), - const char* traceTag = ""); - /** Indicate that psc is the checker for proof rule id */ - void registerChecker(PfRule id, ProofRuleChecker* psc); - /** - * Indicate that id is a trusted rule with the given pedantic level, e.g.: - * 0: (mandatory) always a failure to use the given id - * 1: (major) failure on all (non-zero) pedantic levels - * 10: (minor) failure only on pedantic levels >= 10. - */ - void registerTrustedChecker(PfRule id, - ProofRuleChecker* psc, - uint32_t plevel = 10); - /** get checker for */ - ProofRuleChecker* getCheckerFor(PfRule id); - - /** - * Get the pedantic level for id if it has been assigned a pedantic - * level via registerTrustedChecker above, or zero otherwise. - */ - uint32_t getPedanticLevel(PfRule id) const; - - /** - * Is pedantic failure? If so, we return true and write a debug message on the - * output stream out if enableOutput is true. - */ - bool isPedanticFailure(PfRule id, - std::ostream& out, - bool enableOutput = true) const; - - private: - /** statistics class */ - ProofCheckerStatistics d_stats; - /** Maps proof rules to their checker */ - std::map d_checker; - /** Maps proof trusted rules to their pedantic level */ - std::map d_plevel; - /** The pedantic level of this checker */ - uint32_t d_pclevel; - /** - * Check internal. This is used by check and checkDebug above. It writes - * checking errors on out when enableOutput is true. We treat trusted checkers - * (nullptr in the range of the map d_checker) as failures if - * useTrustedChecker = false. - */ - Node checkInternal(PfRule id, - const std::vector& cchildren, - const std::vector& args, - Node expected, - std::stringstream& out, - bool useTrustedChecker, - bool enableOutput); -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_CHECKER_H */ diff --git a/src/expr/proof_ensure_closed.cpp b/src/expr/proof_ensure_closed.cpp deleted file mode 100644 index e89bd6692..000000000 --- a/src/expr/proof_ensure_closed.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of debug checks for ensuring proofs are closed. - */ - -#include "expr/proof_ensure_closed.h" - -#include - -#include "expr/proof_generator.h" -#include "expr/proof_node.h" -#include "expr/proof_node_algorithm.h" -#include "options/proof_options.h" -#include "options/smt_options.h" - -namespace cvc5 { - -/** - * Ensure closed with respect to assumptions, internal version, which - * generalizes the check for a proof generator or a proof node. - */ -void ensureClosedWrtInternal(Node proven, - ProofGenerator* pg, - ProofNode* pnp, - const std::vector& assumps, - const char* c, - const char* ctx, - bool reqGen) -{ - if (!options::produceProofs()) - { - // proofs not enabled, do not do check - return; - } - bool isTraceDebug = Trace.isOn(c); - if (!options::proofEagerChecking() && !isTraceDebug) - { - // trace is off and proof new eager checking is off, do not do check - return; - } - std::stringstream sdiag; - bool isTraceOn = Trace.isOn(c); - if (!isTraceOn) - { - sdiag << ", use -t " << c << " for details"; - } - bool dumpProofTraceOn = Trace.isOn("dump-proof-error"); - if (!dumpProofTraceOn) - { - sdiag << ", use -t dump-proof-error for details on proof"; - } - // get the proof node in question, which is either provided or built by the - // proof generator - std::shared_ptr pn; - std::stringstream ss; - if (pnp != nullptr) - { - Assert(pg == nullptr); - ss << "ProofNode in context " << ctx; - } - else - { - ss << "ProofGenerator: " << (pg == nullptr ? "null" : pg->identify()) - << " in context " << ctx; - if (pg == nullptr) - { - // only failure if flag is true - if (reqGen) - { - Unreachable() << "...ensureClosed: no generator in context " << ctx - << sdiag.str(); - } - } - else - { - Assert(!proven.isNull()); - pn = pg->getProofFor(proven); - pnp = pn.get(); - } - } - Trace(c) << "=== ensureClosed: " << ss.str() << std::endl; - Trace(c) << "Proven: " << proven << std::endl; - if (pnp == nullptr) - { - if (pg == nullptr) - { - // did not require generator - Assert(!reqGen); - Trace(c) << "...ensureClosed: no generator in context " << ctx - << std::endl; - return; - } - } - // if we don't have a proof node, a generator failed - if (pnp == nullptr) - { - Assert(pg != nullptr); - AlwaysAssert(false) << "...ensureClosed: null proof from " << ss.str() - << sdiag.str(); - } - std::vector fassumps; - expr::getFreeAssumptions(pnp, fassumps); - bool isClosed = true; - std::stringstream ssf; - for (const Node& fa : fassumps) - { - if (std::find(assumps.begin(), assumps.end(), fa) == assumps.end()) - { - isClosed = false; - ssf << "- " << fa << std::endl; - } - } - if (!isClosed) - { - Trace(c) << "Free assumptions:" << std::endl; - Trace(c) << ssf.str(); - if (!assumps.empty()) - { - Trace(c) << "Expected assumptions:" << std::endl; - for (const Node& a : assumps) - { - Trace(c) << "- " << a << std::endl; - } - } - if (dumpProofTraceOn) - { - Trace("dump-proof-error") << " Proof: " << *pnp << std::endl; - } - } - AlwaysAssert(isClosed) << "...ensureClosed: open proof from " << ss.str() - << sdiag.str(); - Trace(c) << "...ensureClosed: success" << std::endl; - Trace(c) << "====" << std::endl; -} - -void pfgEnsureClosed(Node proven, - ProofGenerator* pg, - const char* c, - const char* ctx, - bool reqGen) -{ - Assert(!proven.isNull()); - // proof generator may be null - std::vector assumps; - ensureClosedWrtInternal(proven, pg, nullptr, assumps, c, ctx, reqGen); -} - -void pfgEnsureClosedWrt(Node proven, - ProofGenerator* pg, - const std::vector& assumps, - const char* c, - const char* ctx, - bool reqGen) -{ - Assert(!proven.isNull()); - // proof generator may be null - ensureClosedWrtInternal(proven, pg, nullptr, assumps, c, ctx, reqGen); -} - -void pfnEnsureClosed(ProofNode* pn, const char* c, const char* ctx) -{ - ensureClosedWrtInternal(Node::null(), nullptr, pn, {}, c, ctx, false); -} - -void pfnEnsureClosedWrt(ProofNode* pn, - const std::vector& assumps, - const char* c, - const char* ctx) -{ - ensureClosedWrtInternal(Node::null(), nullptr, pn, assumps, c, ctx, false); -} - -} // namespace cvc5 diff --git a/src/expr/proof_ensure_closed.h b/src/expr/proof_ensure_closed.h deleted file mode 100644 index 3d126a4a1..000000000 --- a/src/expr/proof_ensure_closed.h +++ /dev/null @@ -1,73 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Debug checks for ensuring proofs are closed. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_ENSURE_CLOSED_H -#define CVC5__EXPR__PROOF_ENSURE_CLOSED_H - -#include "expr/node.h" - -namespace cvc5 { - -class ProofGenerator; -class ProofNode; - -/** - * Debug check closed on Trace c. Context ctx is string for debugging. - * This method throws an assertion failure if pg cannot provide a closed - * proof for fact proven. This is checked only if --proof-eager-checking - * is enabled or the Trace c is enabled. - * - * @param reqGen Whether we consider a null generator to be a failure. - */ -void pfgEnsureClosed(Node proven, - ProofGenerator* pg, - const char* c, - const char* ctx, - bool reqGen = true); - -/** - * Debug check closed with Trace c. Context ctx is string for debugging and - * assumps is the set of allowed open assertions. This method throws an - * assertion failure if pg cannot provide a proof for fact proven whose - * free assumptions are contained in assumps. - * - * @param reqGen Whether we consider a null generator to be a failure. - */ -void pfgEnsureClosedWrt(Node proven, - ProofGenerator* pg, - const std::vector& assumps, - const char* c, - const char* ctx, - bool reqGen = true); - -/** - * Debug check closed with Trace c, proof node versions. This gives an - * assertion failure if pn is not closed. Detailed information is printed - * on trace c. Context ctx is a string used for debugging. - */ -void pfnEnsureClosed(ProofNode* pn, const char* c, const char* ctx); -/** - * Same as above, but throws an assertion failure only if the free assumptions - * of pn are not contained in assumps. - */ -void pfnEnsureClosedWrt(ProofNode* pn, - const std::vector& assumps, - const char* c, - const char* ctx); -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_ENSURE_CLOSED_H */ diff --git a/src/expr/proof_generator.cpp b/src/expr/proof_generator.cpp deleted file mode 100644 index 7c034c10d..000000000 --- a/src/expr/proof_generator.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof generator utility. - */ - -#include "expr/proof_generator.h" - -#include - -#include "expr/proof.h" -#include "expr/proof_node.h" -#include "expr/proof_node_algorithm.h" -#include "options/smt_options.h" - -namespace cvc5 { - -std::ostream& operator<<(std::ostream& out, CDPOverwrite opol) -{ - switch (opol) - { - case CDPOverwrite::ALWAYS: out << "ALWAYS"; break; - case CDPOverwrite::ASSUME_ONLY: out << "ASSUME_ONLY"; break; - case CDPOverwrite::NEVER: out << "NEVER"; break; - default: out << "CDPOverwrite:unknown"; break; - } - return out; -} - -ProofGenerator::ProofGenerator() {} - -ProofGenerator::~ProofGenerator() {} - -std::shared_ptr ProofGenerator::getProofFor(Node f) -{ - Unreachable() << "ProofGenerator::getProofFor: " << identify() - << " has no implementation" << std::endl; - return nullptr; -} - -bool ProofGenerator::addProofTo(Node f, - CDProof* pf, - CDPOverwrite opolicy, - bool doCopy) -{ - Trace("pfgen") << "ProofGenerator::addProofTo: " << f << "..." << std::endl; - Assert(pf != nullptr); - // plug in the proof provided by the generator, if it exists - std::shared_ptr apf = getProofFor(f); - if (apf != nullptr) - { - Trace("pfgen") << "...got proof " << *apf.get() << std::endl; - if (pf->addProof(apf, opolicy, doCopy)) - { - Trace("pfgen") << "...success!" << std::endl; - return true; - } - Trace("pfgen") << "...failed to add proof" << std::endl; - } - else - { - Trace("pfgen") << "...failed, no proof" << std::endl; - Assert(false) << "Failed to get proof from generator for fact " << f; - } - return false; -} - -} // namespace cvc5 diff --git a/src/expr/proof_generator.h b/src/expr/proof_generator.h deleted file mode 100644 index 2e87ab756..000000000 --- a/src/expr/proof_generator.h +++ /dev/null @@ -1,113 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * The abstract proof generator class. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_GENERATOR_H -#define CVC5__EXPR__PROOF_GENERATOR_H - -#include "expr/node.h" - -namespace cvc5 { - -class CDProof; -class ProofNode; - -/** An overwrite policy for CDProof */ -enum class CDPOverwrite : uint32_t -{ - // always overwrite an existing step. - ALWAYS, - // overwrite ASSUME with non-ASSUME steps. - ASSUME_ONLY, - // never overwrite an existing step. - NEVER, -}; -/** Writes a overwrite policy name to a stream. */ -std::ostream& operator<<(std::ostream& out, CDPOverwrite opol); - -/** - * An abstract proof generator class. - * - * A proof generator is intended to be used as a utility e.g. in theory - * solvers for constructing and storing proofs internally. A theory may have - * multiple instances of ProofGenerator objects, e.g. if it has more than one - * way of justifying lemmas or conflicts. - * - * A proof generator has two main interfaces for generating proofs: - * (1) getProofFor, and (2) addProofTo. The latter is optional. - * - * The addProofTo method can be used as an optimization for avoiding - * the construction of the ProofNode for a given fact. - * - * If no implementation of addProofTo is provided, then addProofTo(f, pf) - * calls getProofFor(f) and links the topmost ProofNode of the returned proof - * into pf. Note this top-most ProofNode can be avoided in the addProofTo - * method. - */ -class ProofGenerator -{ - public: - ProofGenerator(); - virtual ~ProofGenerator(); - /** Get the proof for formula f - * - * This forces the proof generator to construct a proof for formula f and - * return it. If this is an "eager" proof generator, this function is expected - * to be implemented as a map lookup. If this is a "lazy" proof generator, - * this function is expected to invoke a proof producing procedure of the - * generator. - * - * It should be the case that hasProofFor(f) is true. - * - * @param f The fact to get the proof for. - * @return The proof for f. - */ - virtual std::shared_ptr getProofFor(Node f); - /** - * Add the proof for formula f to proof pf. The proof of f is overwritten in - * pf based on the policy opolicy. - * - * @param f The fact to get the proof for. - * @param pf The CDProof object to add the proof to. - * @param opolicy The overwrite policy for adding to pf. - * @param doCopy Whether to do a deep copy of the proof steps into pf. - * @return True if this call was sucessful. - */ - virtual bool addProofTo(Node f, - CDProof* pf, - CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY, - bool doCopy = false); - /** - * Can we give the proof for formula f? This is used for debugging. This - * returns false if the generator cannot provide a proof of formula f. - * - * Also notice that this function does not require the proof for f to be - * constructed at the time of this call. Thus, if this is a "lazy" proof - * generator, it is expected that this call is implemented as a map lookup - * into the bookkeeping maintained by the generator only. - * - * Notice the default return value is true. In other words, a proof generator - * may choose to override this function to verify the construction, although - * we do not insist this is the case. - */ - virtual bool hasProofFor(Node f) { return true; } - /** Identify this generator (for debugging, etc..) */ - virtual std::string identify() const = 0; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_GENERATOR_H */ diff --git a/src/expr/proof_node.cpp b/src/expr/proof_node.cpp deleted file mode 100644 index 92daad8ed..000000000 --- a/src/expr/proof_node.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof node utility. - */ - -#include "expr/proof_node.h" - -#include "expr/proof_node_algorithm.h" -#include "expr/proof_node_to_sexpr.h" - -namespace cvc5 { - -ProofNode::ProofNode(PfRule id, - const std::vector>& children, - const std::vector& args) -{ - setValue(id, children, args); -} - -PfRule ProofNode::getRule() const { return d_rule; } - -const std::vector>& ProofNode::getChildren() const -{ - return d_children; -} - -const std::vector& ProofNode::getArguments() const { return d_args; } - -Node ProofNode::getResult() const { return d_proven; } - -bool ProofNode::isClosed() -{ - std::vector assumps; - expr::getFreeAssumptions(this, assumps); - return assumps.empty(); -} - -void ProofNode::setValue( - PfRule id, - const std::vector>& children, - const std::vector& args) -{ - d_rule = id; - d_children = children; - d_args = args; -} - -void ProofNode::printDebug(std::ostream& os) const -{ - // convert to sexpr and print - ProofNodeToSExpr pnts; - Node ps = pnts.convertToSExpr(this); - os << ps; -} - -std::ostream& operator<<(std::ostream& out, const ProofNode& pn) -{ - pn.printDebug(out); - return out; -} - -} // namespace cvc5 diff --git a/src/expr/proof_node.h b/src/expr/proof_node.h deleted file mode 100644 index f8d71f703..000000000 --- a/src/expr/proof_node.h +++ /dev/null @@ -1,145 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Alex Ozdemir - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof node utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_NODE_H -#define CVC5__EXPR__PROOF_NODE_H - -#include - -#include "expr/node.h" -#include "expr/proof_rule.h" - -namespace cvc5 { - -class ProofNodeManager; -class ProofNode; - -// Alias for shared pointer to a proof node -using Pf = std::shared_ptr; - -struct ProofNodeHashFunction -{ - inline size_t operator()(std::shared_ptr pfn) const; -}; /* struct ProofNodeHashFunction */ - -/** A node in a proof - * - * A ProofNode represents a single step in a proof. It contains: - * (1) d_id, an identifier indicating the kind of inference, - * (2) d_children, the child ProofNode objects indicating its premises, - * (3) d_args, additional arguments used to determine the conclusion, - * (4) d_proven, cache of the formula that this ProofNode proves. - * - * Overall, a ProofNode and its children form a directed acyclic graph. - * - * A ProofNode is partially mutable in that (1), (2) and (3) can be - * modified. A motivating example of when this is useful is when a ProofNode - * is established to be a "hole" for something to be proven later. On the other - * hand, (4) is intended to be immutable. - * - * The method setValue is private and can be called by objects that manage - * ProofNode objects in trusted ways that ensure that the node maintains - * the invariant above. Furthermore, notice that this class is not responsible - * for setting d_proven; this is done externally by a ProofNodeManager class. - * - * Notice that all fields of ProofNode are stored in ***Skolem form***. Their - * correctness is checked in ***witness form*** (for details on this - * terminology, see expr/skolem_manager.h). As a simple example, say a - * theory solver has a term t, and wants to introduce a unit lemma (= k t) - * where k is a fresh Skolem variable. It creates this variable via: - * k = SkolemManager::mkPurifySkolem(t,"k"); - * A checked ProofNode for the fact (= k t) then may have fields: - * d_rule := MACRO_SR_PRED_INTRO, - * d_children := {}, - * d_args := {(= k t)} - * d_proven := (= k t). - * Its justification via the rule MACRO_SR_PRED_INTRO (see documentation - * in theory/builtin/proof_kinds) is in terms of the witness form of the - * argument: - * (= (witness ((z T)) (= z t)) t) - * which, by that rule's side condition, is such that: - * Rewriter::rewrite((= (witness ((z T)) (= z t)) t)) = true. - * Notice that the correctness of the rule is justified here by rewriting - * the witness form of (= k t). The conversion to/from witness form is - * managed by ProofRuleChecker::check. - * - * An external proof checker is expected to formalize the ProofNode only in - * terms of *witness* forms. - * - * However, the rest of cvc5 sees only the *Skolem* form of arguments and - * conclusions in ProofNode, since this is what is used throughout cvc5. - */ -class ProofNode -{ - friend class ProofNodeManager; - - public: - ProofNode(PfRule id, - const std::vector>& children, - const std::vector& args); - ~ProofNode() {} - /** get the rule of this proof node */ - PfRule getRule() const; - /** Get children */ - const std::vector>& getChildren() const; - /** Get arguments */ - const std::vector& getArguments() const; - /** get what this node proves, or the null node if this is an invalid proof */ - Node getResult() const; - /** - * Returns true if this is a closed proof (i.e. it has no free assumptions). - */ - bool isClosed(); - /** Print debug on output strem os */ - void printDebug(std::ostream& os) const; - - private: - /** - * Set value, called to overwrite the contents of this ProofNode with the - * given arguments. - */ - void setValue(PfRule id, - const std::vector>& children, - const std::vector& args); - /** The proof rule */ - PfRule d_rule; - /** The children of this proof node */ - std::vector> d_children; - /** arguments of this node */ - std::vector d_args; - /** The cache of the fact that has been proven, modifiable by ProofChecker */ - Node d_proven; -}; - -inline size_t ProofNodeHashFunction::operator()( - std::shared_ptr pfn) const -{ - return pfn->getResult().getId() + static_cast(pfn->getRule()); -} - -/** - * Serializes a given proof node to the given stream. - * - * @param out the output stream to use - * @param pn the proof node to output to the stream - * @return the stream - */ -std::ostream& operator<<(std::ostream& out, const ProofNode& pn); - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_NODE_H */ diff --git a/src/expr/proof_node_algorithm.cpp b/src/expr/proof_node_algorithm.cpp deleted file mode 100644 index 4bb35b5dc..000000000 --- a/src/expr/proof_node_algorithm.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof node algorithm utilities. - */ - -#include "expr/proof_node_algorithm.h" - -#include "expr/proof_node.h" - -namespace cvc5 { -namespace expr { - -void getFreeAssumptions(ProofNode* pn, std::vector& assump) -{ - std::map>> amap; - std::shared_ptr spn = std::make_shared( - pn->getRule(), pn->getChildren(), pn->getArguments()); - getFreeAssumptionsMap(spn, amap); - for (const std::pair>>& p : - amap) - { - assump.push_back(p.first); - } -} - -void getFreeAssumptionsMap( - std::shared_ptr pn, - std::map>>& amap) -{ - // proof should not be cyclic - // visited set false after preorder traversal, true after postorder traversal - std::unordered_map visited; - std::unordered_map::iterator it; - std::vector> visit; - std::vector> traversing; - // Maps a bound assumption to the number of bindings it is under - // e.g. in (SCOPE (SCOPE (ASSUME x) (x y)) (y)), y would be mapped to 2 at - // (ASSUME x), and x would be mapped to 1. - // - // This map is used to track which nodes are in scope while traversing the - // DAG. The in-scope assumptions are keys in the map. They're removed when - // their binding count drops to zero. Let's annotate the above example to - // serve as an illustration: - // - // (SCOPE0 (SCOPE1 (ASSUME x) (x y)) (y)) - // - // This is how the map changes during the traversal: - // after previsiting SCOPE0: { y: 1 } - // after previsiting SCOPE1: { y: 2, x: 1 } - // at ASSUME: { y: 2, x: 1 } (so x is in scope!) - // after postvisiting SCOPE1: { y: 1 } - // after postvisiting SCOPE2: {} - // - std::unordered_map scopeDepth; - std::shared_ptr cur; - visit.push_back(pn); - do - { - cur = visit.back(); - visit.pop_back(); - it = visited.find(cur.get()); - const std::vector& cargs = cur->getArguments(); - if (it == visited.end()) - { - PfRule id = cur->getRule(); - if (id == PfRule::ASSUME) - { - visited[cur.get()] = true; - Assert(cargs.size() == 1); - Node f = cargs[0]; - if (!scopeDepth.count(f)) - { - amap[f].push_back(cur); - } - } - else - { - if (id == PfRule::SCOPE) - { - // mark that its arguments are bound in the current scope - for (const Node& a : cargs) - { - scopeDepth[a] += 1; - } - // will need to unbind the variables below - } - // The following loop cannot be merged with the loop above because the - // same subproof - visited[cur.get()] = false; - visit.push_back(cur); - traversing.push_back(cur); - const std::vector>& cs = cur->getChildren(); - for (const std::shared_ptr& cp : cs) - { - if (std::find(traversing.begin(), traversing.end(), cp) - != traversing.end()) - { - Unhandled() << "getFreeAssumptionsMap: cyclic proof! (use " - "--proof-eager-checking)" - << std::endl; - } - visit.push_back(cp); - } - } - } - else if (!it->second) - { - Assert(!traversing.empty()); - traversing.pop_back(); - visited[cur.get()] = true; - if (cur->getRule() == PfRule::SCOPE) - { - // unbind its assumptions - for (const Node& a : cargs) - { - auto scopeCt = scopeDepth.find(a); - Assert(scopeCt != scopeDepth.end()); - scopeCt->second -= 1; - if (scopeCt->second == 0) - { - scopeDepth.erase(scopeCt); - } - } - } - } - } while (!visit.empty()); -} - -bool containsSubproof(ProofNode* pn, ProofNode* pnc) -{ - std::unordered_set visited; - return containsSubproof(pn, pnc, visited); -} - -bool containsSubproof(ProofNode* pn, - ProofNode* pnc, - std::unordered_set& visited) -{ - std::unordered_map::iterator it; - std::vector visit; - visit.push_back(pn); - const ProofNode* cur; - while (!visit.empty()) - { - cur = visit.back(); - visit.pop_back(); - if (visited.find(cur) == visited.end()) - { - visited.insert(cur); - if (cur == pnc) - { - return true; - } - const std::vector>& children = - cur->getChildren(); - for (const std::shared_ptr& cp : children) - { - visit.push_back(cp.get()); - } - } - } - return false; -} - -} // namespace expr -} // namespace cvc5 diff --git a/src/expr/proof_node_algorithm.h b/src/expr/proof_node_algorithm.h deleted file mode 100644 index 4c89dd4bc..000000000 --- a/src/expr/proof_node_algorithm.h +++ /dev/null @@ -1,76 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof node algorithm utilities. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_NODE_ALGORITHM_H -#define CVC5__EXPR__PROOF_NODE_ALGORITHM_H - -#include - -#include "expr/node.h" - -namespace cvc5 { - -class ProofNode; - -namespace expr { - -/** - * This adds to the vector assump all formulas that are "free assumptions" of - * the proof whose root is ProofNode pn. A free assumption is a formula F - * that is an argument (in d_args) of a ProofNode whose kind is ASSUME, and - * that proof node is not beneath an application of SCOPE containing F as an - * argument. - * - * This traverses the structure of the dag represented by this ProofNode. - * Its implementation is analogous to expr::getFreeVariables. - * - * @param pn The proof node. - * @param assump The vector to add the free asumptions of pn to. - */ -void getFreeAssumptions(ProofNode* pn, std::vector& assump); -/** - * Same as above, but maps assumptions to the proof pointer(s) for that - * assumption. Notice that depending on how pn is constructed, there may be - * multiple ProofNode that are ASSUME proofs of the same assumption, which - * are each added to the range of the assumption. - * - * @param pn The proof node. - * @param amap The mapping to add the free asumptions of pn and their - * corresponding proof nodes to. - */ -void getFreeAssumptionsMap( - std::shared_ptr pn, - std::map>>& amap); - -/** - * @return true if pn contains pnc. - */ -bool containsSubproof(ProofNode* pn, ProofNode* pnc); - -/** - * Same as above, with a visited cache. - * - * @return true if pn contains pnc. - */ -bool containsSubproof(ProofNode* pn, - ProofNode* pnc, - std::unordered_set& visited); - -} // namespace expr -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_NODE_ALGORITHM_H */ diff --git a/src/expr/proof_node_manager.cpp b/src/expr/proof_node_manager.cpp deleted file mode 100644 index c2c72f35d..000000000 --- a/src/expr/proof_node_manager.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof node manager. - */ - -#include "expr/proof_node_manager.h" - -#include - -#include "expr/proof.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" -#include "expr/proof_node_algorithm.h" -#include "options/proof_options.h" -#include "theory/rewriter.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -ProofNodeManager::ProofNodeManager(ProofChecker* pc) - : d_checker(pc) -{ - d_true = NodeManager::currentNM()->mkConst(true); -} - -std::shared_ptr ProofNodeManager::mkNode( - PfRule id, - const std::vector>& children, - const std::vector& args, - Node expected) -{ - Trace("pnm") << "ProofNodeManager::mkNode " << id << " {" << expected.getId() - << "} " << expected << "\n"; - Node res = checkInternal(id, children, args, expected); - if (res.isNull()) - { - // if it was invalid, then we return the null node - return nullptr; - } - // otherwise construct the proof node and set its proven field - std::shared_ptr pn = - std::make_shared(id, children, args); - pn->d_proven = res; - return pn; -} - -std::shared_ptr ProofNodeManager::mkAssume(Node fact) -{ - Assert(!fact.isNull()); - Assert(fact.getType().isBoolean()); - return mkNode(PfRule::ASSUME, {}, {fact}, fact); -} - -std::shared_ptr ProofNodeManager::mkTrans( - const std::vector>& children, Node expected) -{ - Assert(!children.empty()); - if (children.size() == 1) - { - Assert(expected.isNull() || children[0]->getResult() == expected); - return children[0]; - } - return mkNode(PfRule::TRANS, children, {}, expected); -} - -std::shared_ptr ProofNodeManager::mkScope( - std::shared_ptr pf, - std::vector& assumps, - bool ensureClosed, - bool doMinimize, - Node expected) -{ - if (!ensureClosed) - { - return mkNode(PfRule::SCOPE, {pf}, assumps, expected); - } - Trace("pnm-scope") << "ProofNodeManager::mkScope " << assumps << std::endl; - // we first ensure the assumptions are flattened - std::unordered_set ac{assumps.begin(), assumps.end()}; - // map from the rewritten form of assumptions to the original. This is only - // computed in the rare case when we need rewriting to match the - // assumptions. An example of this is for Boolean constant equalities in - // scoped proofs from the proof equality engine. - std::unordered_map acr; - // whether we have compute the map above - bool computedAcr = false; - - // The free assumptions of the proof - std::map>> famap; - expr::getFreeAssumptionsMap(pf, famap); - std::unordered_set acu; - for (const std::pair>>& - fa : famap) - { - Node a = fa.first; - if (ac.find(a) != ac.end()) - { - // already covered by an assumption - acu.insert(a); - continue; - } - // trivial assumption - if (a == d_true) - { - Trace("pnm-scope") << "- justify trivial True assumption\n"; - for (std::shared_ptr pfs : fa.second) - { - Assert(pfs->getResult() == a); - updateNode(pfs.get(), PfRule::MACRO_SR_PRED_INTRO, {}, {a}); - } - Trace("pnm-scope") << "...finished" << std::endl; - acu.insert(a); - continue; - } - Trace("pnm-scope") << "- try matching free assumption " << a << "\n"; - // otherwise it may be due to symmetry? - Node aeqSym = CDProof::getSymmFact(a); - Trace("pnm-scope") << " - try sym " << aeqSym << "\n"; - Node aMatch; - if (!aeqSym.isNull() && ac.count(aeqSym)) - { - Trace("pnm-scope") << "- reorient assumption " << aeqSym << " via " << a - << " for " << fa.second.size() << " proof nodes" - << std::endl; - aMatch = aeqSym; - } - else - { - // Otherwise, may be derivable by rewriting. Note this is used in - // ensuring that proofs from the proof equality engine that involve - // equality with Boolean constants are closed. - if (!computedAcr) - { - computedAcr = true; - for (const Node& acc : ac) - { - Node accr = theory::Rewriter::rewrite(acc); - if (accr != acc) - { - acr[accr] = acc; - } - } - } - Node ar = theory::Rewriter::rewrite(a); - std::unordered_map::iterator itr = acr.find(ar); - if (itr != acr.end()) - { - aMatch = itr->second; - } - } - - // if we found a match either by symmetry or rewriting, then we connect - // the assumption here. - if (!aMatch.isNull()) - { - std::shared_ptr pfaa = mkAssume(aMatch); - // must correct the orientation on this leaf - std::vector> children; - children.push_back(pfaa); - for (std::shared_ptr pfs : fa.second) - { - Assert(pfs->getResult() == a); - // use SYMM if possible - if (aMatch == aeqSym) - { - updateNode(pfs.get(), PfRule::SYMM, children, {}); - } - else - { - updateNode(pfs.get(), PfRule::MACRO_SR_PRED_TRANSFORM, children, {a}); - } - } - Trace("pnm-scope") << "...finished" << std::endl; - acu.insert(aMatch); - continue; - } - // If we did not find a match, it is an error, since all free assumptions - // should be arguments to SCOPE. - std::stringstream ss; - - bool dumpProofTraceOn = Trace.isOn("dump-proof-error"); - if (dumpProofTraceOn) - { - ss << "The proof : " << *pf << std::endl; - } - ss << std::endl << "Free assumption: " << a << std::endl; - for (const Node& aprint : ac) - { - ss << "- assumption: " << aprint << std::endl; - } - if (!dumpProofTraceOn) - { - ss << "Use -t dump-proof-error for details on proof" << std::endl; - } - Unreachable() << "Generated a proof that is not closed by the scope: " - << ss.str() << std::endl; - } - if (acu.size() < ac.size()) - { - // All assumptions should match a free assumption; if one does not, then - // the explanation could have been smaller. - for (const Node& a : ac) - { - if (acu.find(a) == acu.end()) - { - Notice() << "ProofNodeManager::mkScope: assumption " << a - << " does not match a free assumption in proof" << std::endl; - } - } - } - if (doMinimize && acu.size() < ac.size()) - { - assumps.clear(); - assumps.insert(assumps.end(), acu.begin(), acu.end()); - } - else if (ac.size() < assumps.size()) - { - // remove duplicates to avoid redundant literals in clauses - assumps.clear(); - assumps.insert(assumps.end(), ac.begin(), ac.end()); - } - Node minExpected; - NodeManager* nm = NodeManager::currentNM(); - Node exp; - if (assumps.empty()) - { - // SCOPE with no arguments is a no-op, just return original - return pf; - } - Node conc = pf->getResult(); - exp = assumps.size() == 1 ? assumps[0] : nm->mkNode(AND, assumps); - if (conc.isConst() && !conc.getConst()) - { - minExpected = exp.notNode(); - } - else - { - minExpected = nm->mkNode(IMPLIES, exp, conc); - } - return mkNode(PfRule::SCOPE, {pf}, assumps, minExpected); -} - -bool ProofNodeManager::updateNode( - ProofNode* pn, - PfRule id, - const std::vector>& children, - const std::vector& args) -{ - return updateNodeInternal(pn, id, children, args, true); -} - -bool ProofNodeManager::updateNode(ProofNode* pn, ProofNode* pnr) -{ - Assert(pn != nullptr); - Assert(pnr != nullptr); - if (pn->getResult() != pnr->getResult()) - { - return false; - } - // can shortcut re-check of rule - return updateNodeInternal( - pn, pnr->getRule(), pnr->getChildren(), pnr->getArguments(), false); -} - -Node ProofNodeManager::checkInternal( - PfRule id, - const std::vector>& children, - const std::vector& args, - Node expected) -{ - Node res; - if (d_checker) - { - // check with the checker, which takes expected as argument - res = d_checker->check(id, children, args, expected); - Assert(!res.isNull()) - << "ProofNodeManager::checkInternal: failed to check proof"; - } - else - { - // otherwise we trust the expected value, if it exists - Assert(!expected.isNull()) << "ProofNodeManager::checkInternal: no checker " - "or expected value provided"; - res = expected; - } - return res; -} - -ProofChecker* ProofNodeManager::getChecker() const { return d_checker; } - -std::shared_ptr ProofNodeManager::clone( - std::shared_ptr pn) -{ - const ProofNode* orig = pn.get(); - std::unordered_map> visited; - std::unordered_map>::iterator it; - std::vector visit; - std::shared_ptr cloned; - visit.push_back(orig); - const ProofNode* cur; - while (!visit.empty()) - { - cur = visit.back(); - it = visited.find(cur); - if (it == visited.end()) - { - visited[cur] = nullptr; - const std::vector>& children = - cur->getChildren(); - for (const std::shared_ptr& cp : children) - { - visit.push_back(cp.get()); - } - continue; - } - visit.pop_back(); - if (it->second.get() == nullptr) - { - std::vector> cchildren; - const std::vector>& children = - cur->getChildren(); - for (const std::shared_ptr& cp : children) - { - it = visited.find(cp.get()); - Assert(it != visited.end()); - Assert(it->second != nullptr); - cchildren.push_back(it->second); - } - cloned = std::make_shared( - cur->getRule(), cchildren, cur->getArguments()); - visited[cur] = cloned; - // we trust the above cloning does not change what is proven - cloned->d_proven = cur->d_proven; - } - } - Assert(visited.find(orig) != visited.end()); - return visited[orig]; -} - -bool ProofNodeManager::updateNodeInternal( - ProofNode* pn, - PfRule id, - const std::vector>& children, - const std::vector& args, - bool needsCheck) -{ - Assert(pn != nullptr); - // ---------------- check for cyclic - if (options::proofEagerChecking()) - { - std::unordered_set visited; - for (const std::shared_ptr& cpc : children) - { - if (expr::containsSubproof(cpc.get(), pn, visited)) - { - std::stringstream ss; - ss << "ProofNodeManager::updateNode: attempting to make cyclic proof! " - << id << " " << pn->getResult() << ", children = " << std::endl; - for (const std::shared_ptr& cp : children) - { - ss << " " << cp->getRule() << " " << cp->getResult() << std::endl; - } - ss << "Full children:" << std::endl; - for (const std::shared_ptr& cp : children) - { - ss << " - "; - cp->printDebug(ss); - ss << std::endl; - } - Unreachable() << ss.str(); - } - } - } - // ---------------- end check for cyclic - - // should have already computed what is proven - Assert(!pn->d_proven.isNull()) - << "ProofNodeManager::updateProofNode: invalid proof provided"; - if (needsCheck) - { - // We expect to prove the same thing as before - Node res = checkInternal(id, children, args, pn->d_proven); - if (res.isNull()) - { - // if it was invalid, then we do not update - return false; - } - // proven field should already be the same as the result - Assert(res == pn->d_proven); - } - - // we update its value - pn->setValue(id, children, args); - return true; -} - -} // namespace cvc5 diff --git a/src/expr/proof_node_manager.h b/src/expr/proof_node_manager.h deleted file mode 100644 index 853d22de7..000000000 --- a/src/expr/proof_node_manager.h +++ /dev/null @@ -1,206 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof node manager utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_NODE_MANAGER_H -#define CVC5__EXPR__PROOF_NODE_MANAGER_H - -#include - -#include "expr/node.h" -#include "expr/proof_rule.h" - -namespace cvc5 { - -class ProofChecker; -class ProofNode; - -/** - * A manager for proof node objects. This is a trusted interface for creating - * and updating ProofNode objects. - * - * In more detail, we say a ProofNode is "well-formed (with respect to checker - * X)" if its d_proven field is non-null, and corresponds to the formula that - * the ProofNode proves according to X. The ProofNodeManager class constructs - * and update nodes that are well-formed with respect to its underlying checker. - * - * If no checker is provided, then the ProofNodeManager assigns the d_proven - * field of ProofNode based on the provided "expected" argument in mkNode below, - * which must be provided in this case. - * - * The ProofNodeManager is used as a trusted way of updating ProofNode objects - * via updateNode below. In particular, this method leaves the d_proven field - * unchanged and updates (if possible) the remaining content of a given proof - * node. - * - * Notice that ProofNode objects are mutable, and hence this class does not - * cache the results of mkNode. A version of this class that caches - * immutable version of ProofNode objects could be built as an extension - * or layer on top of this class. - */ -class ProofNodeManager -{ - public: - ProofNodeManager(ProofChecker* pc = nullptr); - ~ProofNodeManager() {} - /** - * This constructs a ProofNode with the given arguments. The expected - * argument, when provided, indicates the formula that the returned node - * is expected to prove. If we find that it does not, based on the underlying - * checker, this method returns nullptr. - * - * @param id The id of the proof node. - * @param children The children of the proof node. - * @param args The arguments of the proof node. - * @param expected (Optional) the expected conclusion of the proof node. - * @return the proof node, or nullptr if the given arguments do not - * consistute a proof of the expected conclusion according to the underlying - * checker, if both are provided. It also returns nullptr if neither the - * checker nor the expected field is provided, since in this case the - * conclusion is unknown. - */ - std::shared_ptr mkNode( - PfRule id, - const std::vector>& children, - const std::vector& args, - Node expected = Node::null()); - /** - * Make the proof node corresponding to the assumption of fact. - * - * @param fact The fact to assume. - * @return The ASSUME proof of fact. - */ - std::shared_ptr mkAssume(Node fact); - /** - * Make transitivity proof, where children contains one or more proofs of - * equalities that form an ordered chain. In other words, the vector children - * is a legal set of children for an application of TRANS. - */ - std::shared_ptr mkTrans( - const std::vector>& children, - Node expected = Node::null()); - - /** - * Make scope having body pf and arguments (assumptions-to-close) assumps. - * If ensureClosed is true, then this method throws an assertion failure if - * the returned proof is not closed. This is the case if a free assumption - * of pf is missing from the vector assumps. - * - * For conveinence, the proof pf may be modified to ensure that the overall - * result is closed. For instance, given input: - * pf = TRANS( ASSUME( x=y ), ASSUME( y=z ) ) - * assumps = { y=x, y=z } - * This method will modify pf to be: - * pf = TRANS( SYMM( ASSUME( y=x ) ), ASSUME( y=z ) ) - * so that y=x matches the free assumption. The returned proof is: - * SCOPE(TRANS( SYMM( ASSUME( y=x ) ), ASSUME( y=z ) ) :args { y=x, y=z }) - * - * When ensureClosed is true, duplicates are eliminated from assumps. The - * reason for this is due to performance, since in this method, assumps is - * converted to an unordered_set to do the above check and hence it is a - * convienient time to eliminate duplicate literals. - * - * Additionally, if both ensureClosed and doMinimize are true, assumps is - * updated to contain exactly the free asumptions of pf. This also includes - * having no duplicates. Furthermore, if assumps is empty after minimization, - * this method is a no-op. - * - * In each case, the update vector assumps is passed as arguments to SCOPE. - * - * @param pf The body of the proof, - * @param assumps The assumptions-to-close of the scope, - * @param ensureClosed Whether to ensure that the proof is closed, - * @param doMinimize Whether to minimize assumptions. - * @param expected the node that the scope should prove. - * @return The scoped proof. - */ - std::shared_ptr mkScope(std::shared_ptr pf, - std::vector& assumps, - bool ensureClosed = true, - bool doMinimize = false, - Node expected = Node::null()); - /** - * This method updates pn to be a proof of the form ( children, args ), - * while maintaining its d_proven field. This method returns false if this - * proof manager is using a checker, and we compute that the above proof - * is not a proof of the fact that pn previously proved. - * - * @param pn The proof node to update. - * @param id The updated id of the proof node. - * @param children The updated children of the proof node. - * @param args The updated arguments of the proof node. - * @return true if the update was successful. - * - * Notice that updateNode always returns true if there is no underlying - * checker. - */ - bool updateNode(ProofNode* pn, - PfRule id, - const std::vector>& children, - const std::vector& args); - /** - * Update node pn to have the contents of pnr. It should be the case that - * pn and pnr prove the same fact, otherwise false is returned and pn is - * unchanged. - */ - bool updateNode(ProofNode* pn, ProofNode* pnr); - /** Get the underlying proof checker */ - ProofChecker* getChecker() const; - /** - * Clone a proof node, which creates a deep copy of pn and returns it. The - * dag structure of pn is the same as that in the returned proof node. - * - * @param pn The proof node to clone - * @return the cloned proof node. - */ - std::shared_ptr clone(std::shared_ptr pn); - - private: - /** The (optional) proof checker */ - ProofChecker* d_checker; - /** the true node */ - Node d_true; - /** Check internal - * - * This returns the result of proof checking a ProofNode with the provided - * arguments with an expected conclusion, which may not null if there is - * no expected conclusion. - * - * This throws an assertion error if we fail to check such a proof node, or - * if expected is provided (non-null) and is different what is proven by the - * other arguments. - */ - Node checkInternal(PfRule id, - const std::vector>& children, - const std::vector& args, - Node expected); - /** - * Update node internal, return true if successful. This is called by - * the update node methods above. The argument needsCheck is whether we - * need to check the correctness of the rule application. This is false - * for the updateNode routine where pnr is an (already checked) proof node. - */ - bool updateNodeInternal( - ProofNode* pn, - PfRule id, - const std::vector>& children, - const std::vector& args, - bool needsCheck); -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_NODE_H */ diff --git a/src/expr/proof_node_to_sexpr.cpp b/src/expr/proof_node_to_sexpr.cpp deleted file mode 100644 index 033232959..000000000 --- a/src/expr/proof_node_to_sexpr.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof node to s-expression. - */ - -#include "expr/proof_node_to_sexpr.h" - -#include -#include - -#include "expr/proof_node.h" -#include "options/proof_options.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -ProofNodeToSExpr::ProofNodeToSExpr() -{ - NodeManager* nm = NodeManager::currentNM(); - d_conclusionMarker = nm->mkBoundVar(":conclusion", nm->sExprType()); - d_argsMarker = nm->mkBoundVar(":args", nm->sExprType()); -} - -Node ProofNodeToSExpr::convertToSExpr(const ProofNode* pn) -{ - NodeManager* nm = NodeManager::currentNM(); - std::map::iterator it; - std::vector visit; - std::vector traversing; - const ProofNode* cur; - visit.push_back(pn); - do - { - cur = visit.back(); - visit.pop_back(); - it = d_pnMap.find(cur); - - if (it == d_pnMap.end()) - { - d_pnMap[cur] = Node::null(); - traversing.push_back(cur); - visit.push_back(cur); - const std::vector>& pc = cur->getChildren(); - for (const std::shared_ptr& cp : pc) - { - if (std::find(traversing.begin(), traversing.end(), cp.get()) - != traversing.end()) - { - Unhandled() << "ProofNodeToSExpr::convertToSExpr: cyclic proof! (use " - "--proof-eager-checking)" - << std::endl; - return Node::null(); - } - visit.push_back(cp.get()); - } - } - else if (it->second.isNull()) - { - Assert(!traversing.empty()); - traversing.pop_back(); - std::vector children; - // add proof rule - children.push_back(getOrMkPfRuleVariable(cur->getRule())); - if (options::proofPrintConclusion()) - { - children.push_back(d_conclusionMarker); - children.push_back(cur->getResult()); - } - const std::vector>& pc = cur->getChildren(); - for (const std::shared_ptr& cp : pc) - { - it = d_pnMap.find(cp.get()); - Assert(it != d_pnMap.end()); - Assert(!it->second.isNull()); - children.push_back(it->second); - } - // add arguments - const std::vector& args = cur->getArguments(); - if (!args.empty()) - { - children.push_back(d_argsMarker); - // needed to ensure builtin operators are not treated as operators - // this can be the case for CONG where d_args may contain a builtin - // operator - std::vector argsSafe; - for (const Node& a : args) - { - Node av = a; - if (a.getNumChildren() == 0 - && NodeManager::operatorToKind(a) != UNDEFINED_KIND) - { - av = getOrMkNodeVariable(a); - } - argsSafe.push_back(av); - } - Node argsC = nm->mkNode(SEXPR, argsSafe); - children.push_back(argsC); - } - d_pnMap[cur] = nm->mkNode(SEXPR, children); - } - } while (!visit.empty()); - Assert(d_pnMap.find(pn) != d_pnMap.end()); - Assert(!d_pnMap.find(pn)->second.isNull()); - return d_pnMap[pn]; -} - -Node ProofNodeToSExpr::getOrMkPfRuleVariable(PfRule r) -{ - std::map::iterator it = d_pfrMap.find(r); - if (it != d_pfrMap.end()) - { - return it->second; - } - std::stringstream ss; - ss << r; - NodeManager* nm = NodeManager::currentNM(); - Node var = nm->mkBoundVar(ss.str(), nm->sExprType()); - d_pfrMap[r] = var; - return var; -} - -Node ProofNodeToSExpr::getOrMkNodeVariable(Node n) -{ - std::map::iterator it = d_nodeMap.find(n); - if (it != d_nodeMap.end()) - { - return it->second; - } - std::stringstream ss; - ss << n; - NodeManager* nm = NodeManager::currentNM(); - Node var = nm->mkBoundVar(ss.str(), nm->sExprType()); - d_nodeMap[n] = var; - return var; -} - -} // namespace cvc5 diff --git a/src/expr/proof_node_to_sexpr.h b/src/expr/proof_node_to_sexpr.h deleted file mode 100644 index d4cca8eae..000000000 --- a/src/expr/proof_node_to_sexpr.h +++ /dev/null @@ -1,70 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Conversion from ProofNode to s-expressions. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_NODE_TO_SEXPR_H -#define CVC5__EXPR__PROOF_NODE_TO_SEXPR_H - -#include - -#include "expr/node.h" -#include "expr/proof_rule.h" - -namespace cvc5 { - -class ProofNode; - -/** A class to convert ProofNode objects to s-expressions */ -class ProofNodeToSExpr -{ - public: - ProofNodeToSExpr(); - ~ProofNodeToSExpr() {} - /** Convert the given proof node to an s-expression - * - * This is useful for operations where it is useful to view a ProofNode as - * a Node. Printing is one such example, where a ProofNode can be printed - * as a dag after this conversion. - * - * The s-expression for a ProofNode has the form: - * (SEXPR (VAR "") S1 ... Sn (VAR ":args") (SEXPR )) - * where S1, ..., Sn are the s-expressions for its . - */ - Node convertToSExpr(const ProofNode* pn); - - private: - /** map proof rules to a variable */ - std::map d_pfrMap; - /** Dummy ":args" marker */ - Node d_argsMarker; - /** Dummy ":conclusion" marker */ - Node d_conclusionMarker; - /** map proof nodes to their s-expression */ - std::map d_pnMap; - /** - * map nodes to a bound variable, used for nodes that have special AST status - * like builtin operators - */ - std::map d_nodeMap; - /** get or make pf rule variable */ - Node getOrMkPfRuleVariable(PfRule r); - /** get or make node variable */ - Node getOrMkNodeVariable(Node n); -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_RULE_H */ diff --git a/src/expr/proof_node_updater.cpp b/src/expr/proof_node_updater.cpp deleted file mode 100644 index e2fd0c566..000000000 --- a/src/expr/proof_node_updater.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of a utility for updating proof nodes. - */ - -#include "expr/proof_node_updater.h" - -#include "expr/lazy_proof.h" -#include "expr/proof_ensure_closed.h" -#include "expr/proof_node_algorithm.h" -#include "expr/proof_node_manager.h" - -namespace cvc5 { - -ProofNodeUpdaterCallback::ProofNodeUpdaterCallback() {} -ProofNodeUpdaterCallback::~ProofNodeUpdaterCallback() {} - -bool ProofNodeUpdaterCallback::update(Node res, - PfRule id, - const std::vector& children, - const std::vector& args, - CDProof* cdp, - bool& continueUpdate) -{ - return false; -} - -ProofNodeUpdater::ProofNodeUpdater(ProofNodeManager* pnm, - ProofNodeUpdaterCallback& cb, - bool mergeSubproofs, - bool autoSym) - : d_pnm(pnm), - d_cb(cb), - d_debugFreeAssumps(false), - d_mergeSubproofs(mergeSubproofs), - d_autoSym(autoSym) -{ -} - -void ProofNodeUpdater::process(std::shared_ptr pf) -{ - if (d_debugFreeAssumps) - { - if (Trace.isOn("pfnu-debug")) - { - Trace("pfnu-debug2") << "Initial proof: " << *pf.get() << std::endl; - Trace("pfnu-debug") << "ProofNodeUpdater::process" << std::endl; - Trace("pfnu-debug") << "Expected free assumptions: " << std::endl; - for (const Node& fa : d_freeAssumps) - { - Trace("pfnu-debug") << "- " << fa << std::endl; - } - std::vector assump; - expr::getFreeAssumptions(pf.get(), assump); - Trace("pfnu-debug") << "Current free assumptions: " << std::endl; - for (const Node& fa : assump) - { - Trace("pfnu-debug") << "- " << fa << std::endl; - } - } - } - std::vector> traversing; - processInternal(pf, d_freeAssumps, traversing); -} - -void ProofNodeUpdater::processInternal( - std::shared_ptr pf, - const std::vector& fa, - std::vector>& traversing) -{ - Trace("pf-process") << "ProofNodeUpdater::process" << std::endl; - std::unordered_map, bool> visited; - std::unordered_map, bool>::iterator it; - std::vector> visit; - std::shared_ptr cur; - visit.push_back(pf); - std::map>::iterator itc; - // A cache from formulas to proof nodes that are in the current scope. - // Notice that we make a fresh recursive call to process if the current - // rule is SCOPE below. - std::map> resCache; - Node res; - do - { - cur = visit.back(); - visit.pop_back(); - it = visited.find(cur); - res = cur->getResult(); - if (it == visited.end()) - { - if (d_mergeSubproofs) - { - itc = resCache.find(res); - if (itc != resCache.end()) - { - // already have a proof, merge it into this one - visited[cur] = true; - d_pnm->updateNode(cur.get(), itc->second.get()); - continue; - } - } - // run update to a fixed point - bool continueUpdate = true; - while (runUpdate(cur, fa, continueUpdate) && continueUpdate) - { - Trace("pf-process-debug") << "...updated proof." << std::endl; - } - visited[cur] = !continueUpdate; - if (!continueUpdate) - { - // no further changes should be made to cur according to the callback - Trace("pf-process-debug") - << "...marked to not continue update." << std::endl; - runFinalize(cur, fa, resCache); - continue; - } - traversing.push_back(cur); - visit.push_back(cur); - // If we are not the top-level proof, we were a scope, or became a scope - // after updating, we do a separate recursive call to this method. This - // allows us to properly track the assumptions in scope, which is - // important for example to merge or to determine updates based on free - // assumptions. - if (cur->getRule() == PfRule::SCOPE && cur != pf) - { - std::vector nfa; - nfa.insert(nfa.end(), fa.begin(), fa.end()); - const std::vector& args = cur->getArguments(); - nfa.insert(nfa.end(), args.begin(), args.end()); - Trace("pfnu-debug2") << "Process new scope with " << args << std::endl; - // Process in new call separately - processInternal(cur, nfa, traversing); - continue; - } - const std::vector>& ccp = cur->getChildren(); - // now, process children - for (const std::shared_ptr& cp : ccp) - { - if (std::find(traversing.begin(), traversing.end(), cp) - != traversing.end()) - { - Unhandled() - << "ProofNodeUpdater::processInternal: cyclic proof! (use " - "--proof-eager-checking)" - << std::endl; - } - visit.push_back(cp); - } - } - else if (!it->second) - { - Assert(!traversing.empty()); - traversing.pop_back(); - visited[cur] = true; - // finalize the node - runFinalize(cur, fa, resCache); - } - } while (!visit.empty()); - Trace("pf-process") << "ProofNodeUpdater::process: finished" << std::endl; -} - -bool ProofNodeUpdater::runUpdate(std::shared_ptr cur, - const std::vector& fa, - bool& continueUpdate) -{ - // should it be updated? - if (!d_cb.shouldUpdate(cur, fa, continueUpdate)) - { - return false; - } - PfRule id = cur->getRule(); - // use CDProof to open a scope for which the callback updates - CDProof cpf(d_pnm, nullptr, "ProofNodeUpdater::CDProof", d_autoSym); - const std::vector>& cc = cur->getChildren(); - std::vector ccn; - for (const std::shared_ptr& cp : cc) - { - Node cpres = cp->getResult(); - ccn.push_back(cpres); - // store in the proof - cpf.addProof(cp); - } - Node res = cur->getResult(); - Trace("pf-process-debug") - << "Updating (" << cur->getRule() << "): " << res << std::endl; - // only if the callback updated the node - if (d_cb.update(res, id, ccn, cur->getArguments(), &cpf, continueUpdate)) - { - std::shared_ptr npn = cpf.getProofFor(res); - std::vector fullFa; - if (d_debugFreeAssumps) - { - expr::getFreeAssumptions(cur.get(), fullFa); - Trace("pfnu-debug") << "Original proof : " << *cur << std::endl; - } - // then, update the original proof node based on this one - Trace("pf-process-debug") << "Update node..." << std::endl; - d_pnm->updateNode(cur.get(), npn.get()); - Trace("pf-process-debug") << "...update node finished." << std::endl; - if (d_debugFreeAssumps) - { - fullFa.insert(fullFa.end(), fa.begin(), fa.end()); - // We have that npn is a node we occurring the final updated version of - // the proof. We can now debug based on the expected set of free - // assumptions. - Trace("pfnu-debug") << "Ensure update closed..." << std::endl; - pfnEnsureClosedWrt( - npn.get(), fullFa, "pfnu-debug", "ProofNodeUpdater:postupdate"); - } - Trace("pf-process-debug") << "..finished" << std::endl; - return true; - } - Trace("pf-process-debug") << "..finished" << std::endl; - return false; -} - -void ProofNodeUpdater::runFinalize( - std::shared_ptr cur, - const std::vector& fa, - std::map>& resCache) -{ - if (d_mergeSubproofs) - { - Node res = cur->getResult(); - // cache result if we are merging subproofs - resCache[res] = cur; - } - if (d_debugFreeAssumps) - { - // We have that npn is a node we occurring the final updated version of - // the proof. We can now debug based on the expected set of free - // assumptions. - Trace("pfnu-debug") << "Ensure update closed..." << std::endl; - pfnEnsureClosedWrt( - cur.get(), fa, "pfnu-debug", "ProofNodeUpdater:finalize"); - } -} - -void ProofNodeUpdater::setDebugFreeAssumptions( - const std::vector& freeAssumps) -{ - d_freeAssumps.clear(); - d_freeAssumps.insert( - d_freeAssumps.end(), freeAssumps.begin(), freeAssumps.end()); - d_debugFreeAssumps = true; -} - -} // namespace cvc5 diff --git a/src/expr/proof_node_updater.h b/src/expr/proof_node_updater.h deleted file mode 100644 index 215c61210..000000000 --- a/src/expr/proof_node_updater.h +++ /dev/null @@ -1,164 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * A utility for updating proof nodes. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_NODE_UPDATER_H -#define CVC5__EXPR__PROOF_NODE_UPDATER_H - -#include -#include - -#include "expr/node.h" -#include "expr/proof_node.h" - -namespace cvc5 { - -class CDProof; -class ProofNode; -class ProofNodeManager; - -/** - * A virtual callback class for updating ProofNode. An example use case of this - * class is to eliminate a proof rule by expansion. - */ -class ProofNodeUpdaterCallback -{ - public: - ProofNodeUpdaterCallback(); - virtual ~ProofNodeUpdaterCallback(); - /** Should proof pn be updated? - * - * @param pn the proof node that maybe should be updated - * @param fa the assumptions in scope - * @param continueUpdate whether we should continue recursively updating pn - * @return whether we should run the update method on pn - */ - virtual bool shouldUpdate(std::shared_ptr pn, - const std::vector& fa, - bool& continueUpdate) = 0; - /** - * Update the proof rule application, store steps in cdp. Return true if - * the proof changed. It can be assumed that cdp contains proofs of each - * fact in children. - * - * If continueUpdate is set to false in this method, then the resulting - * proof (the proof of res in cdp) is *not* called back to update by the - * proof node updater, nor are its children recursed. Otherwise, by default, - * the proof node updater will continue updating the resulting proof and will - * recursively update its children. This is analogous to marking REWRITE_DONE - * in a rewrite response. - */ - virtual bool update(Node res, - PfRule id, - const std::vector& children, - const std::vector& args, - CDProof* cdp, - bool& continueUpdate); -}; - -/** - * A generic class for updating ProofNode. It is parameterized by a callback - * class. Its process method runs this callback on all subproofs of a provided - * ProofNode application that meet some criteria - * (ProofNodeUpdaterCallback::shouldUpdate) - * and overwrites them based on the update procedure of the callback - * (ProofNodeUpdaterCallback::update), which uses local CDProof objects that - * should be filled in the callback for each ProofNode to update. This update - * process is applied in a *pre-order* traversal. - */ -class ProofNodeUpdater -{ - public: - /** - * @param pnm The proof node manager we are using - * @param cb The callback to apply to each node - * @param mergeSubproofs Whether to automatically merge subproofs within - * the same SCOPE that prove the same fact. - * @param autoSym Whether intermediate CDProof objects passed to updater - * callbacks automatically introduce SYMM steps. - */ - ProofNodeUpdater(ProofNodeManager* pnm, - ProofNodeUpdaterCallback& cb, - bool mergeSubproofs = false, - bool autoSym = true); - /** - * Post-process, which performs the main post-processing technique described - * above. - */ - void process(std::shared_ptr pf); - - /** - * Set free assumptions to freeAssumps. This indicates that we expect - * the proof we are processing to have free assumptions that are in - * freeAssumps. This enables checking when this is violated, which is - * expensive in general. It is not recommended that this method is called - * by default. - */ - void setDebugFreeAssumptions(const std::vector& freeAssumps); - - private: - /** The proof node manager */ - ProofNodeManager* d_pnm; - /** The callback */ - ProofNodeUpdaterCallback& d_cb; - /** - * Post-process, which performs the main post-processing technique described - * above. - * - * @param pf The proof to process - * @param fa The assumptions of the scope that fa is a subproof of with - * respect to the original proof. For example, if (SCOPE P :args (A B)), we - * may call this method on P with fa = { A, B }. - * @param traversing The list of proof nodes we are currently traversing - * beneath. This is used for checking for cycles in the overall proof. - */ - void processInternal(std::shared_ptr pf, - const std::vector& fa, - std::vector>& traversing); - /** - * Update proof node cur based on the callback. This modifies curr using - * ProofNodeManager::updateNode based on the proof node constructed to - * replace it by the callback. Return true if cur was updated. If - * continueUpdate is updated to false, then cur is not updated further - * and its children are not traversed. - */ - bool runUpdate(std::shared_ptr cur, - const std::vector& fa, - bool& continueUpdate); - /** - * Finalize the node cur. This is called at the moment that it is established - * that cur will appear in the final proof. We do any final debug checking - * and add it to the results cache resCache if we are merging subproofs. - */ - void runFinalize(std::shared_ptr cur, - const std::vector& fa, - std::map>& resCache); - /** Are we debugging free assumptions? */ - bool d_debugFreeAssumps; - /** The initial free assumptions */ - std::vector d_freeAssumps; - /** Whether we are merging subproofs */ - bool d_mergeSubproofs; - /** - * Whether intermediate CDProof objects passed to updater callbacks - * automatically introduce SYMM steps. - */ - bool d_autoSym; -}; - -} // namespace cvc5 - -#endif diff --git a/src/expr/proof_rule.cpp b/src/expr/proof_rule.cpp deleted file mode 100644 index f4e8078dc..000000000 --- a/src/expr/proof_rule.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof rule. - */ - -#include "expr/proof_rule.h" - -#include - -namespace cvc5 { - -const char* toString(PfRule id) -{ - switch (id) - { - //================================================= Core rules - case PfRule::ASSUME: return "ASSUME"; - case PfRule::SCOPE: return "SCOPE"; - case PfRule::SUBS: return "SUBS"; - case PfRule::REWRITE: return "REWRITE"; - case PfRule::EVALUATE: return "EVALUATE"; - case PfRule::MACRO_SR_EQ_INTRO: return "MACRO_SR_EQ_INTRO"; - case PfRule::MACRO_SR_PRED_INTRO: return "MACRO_SR_PRED_INTRO"; - case PfRule::MACRO_SR_PRED_ELIM: return "MACRO_SR_PRED_ELIM"; - case PfRule::MACRO_SR_PRED_TRANSFORM: return "MACRO_SR_PRED_TRANSFORM"; - case PfRule::REMOVE_TERM_FORMULA_AXIOM: return "REMOVE_TERM_FORMULA_AXIOM"; - //================================================= Trusted rules - case PfRule::THEORY_LEMMA: return "THEORY_LEMMA"; - case PfRule::THEORY_REWRITE: return "THEORY_REWRITE"; - case PfRule::PREPROCESS: return "PREPROCESS"; - case PfRule::PREPROCESS_LEMMA: return "PREPROCESS_LEMMA"; - case PfRule::THEORY_PREPROCESS: return "THEORY_PREPROCESS"; - case PfRule::THEORY_PREPROCESS_LEMMA: return "THEORY_PREPROCESS_LEMMA"; - case PfRule::THEORY_EXPAND_DEF: return "THEORY_EXPAND_DEF"; - case PfRule::WITNESS_AXIOM: return "WITNESS_AXIOM"; - case PfRule::TRUST_REWRITE: return "TRUST_REWRITE"; - case PfRule::TRUST_SUBS: return "TRUST_SUBS"; - case PfRule::TRUST_SUBS_MAP: return "TRUST_SUBS_MAP"; - //================================================= Boolean rules - case PfRule::RESOLUTION: return "RESOLUTION"; - case PfRule::CHAIN_RESOLUTION: return "CHAIN_RESOLUTION"; - case PfRule::FACTORING: return "FACTORING"; - case PfRule::REORDERING: return "REORDERING"; - case PfRule::MACRO_RESOLUTION: return "MACRO_RESOLUTION"; - case PfRule::MACRO_RESOLUTION_TRUST: return "MACRO_RESOLUTION_TRUST"; - case PfRule::SPLIT: return "SPLIT"; - case PfRule::EQ_RESOLVE: return "EQ_RESOLVE"; - case PfRule::MODUS_PONENS: return "MODUS_PONENS"; - case PfRule::NOT_NOT_ELIM: return "NOT_NOT_ELIM"; - case PfRule::CONTRA: return "CONTRA"; - case PfRule::AND_ELIM: return "AND_ELIM"; - case PfRule::AND_INTRO: return "AND_INTRO"; - case PfRule::NOT_OR_ELIM: return "NOT_OR_ELIM"; - case PfRule::IMPLIES_ELIM: return "IMPLIES_ELIM"; - case PfRule::NOT_IMPLIES_ELIM1: return "NOT_IMPLIES_ELIM1"; - case PfRule::NOT_IMPLIES_ELIM2: return "NOT_IMPLIES_ELIM2"; - case PfRule::EQUIV_ELIM1: return "EQUIV_ELIM1"; - case PfRule::EQUIV_ELIM2: return "EQUIV_ELIM2"; - case PfRule::NOT_EQUIV_ELIM1: return "NOT_EQUIV_ELIM1"; - case PfRule::NOT_EQUIV_ELIM2: return "NOT_EQUIV_ELIM2"; - case PfRule::XOR_ELIM1: return "XOR_ELIM1"; - case PfRule::XOR_ELIM2: return "XOR_ELIM2"; - case PfRule::NOT_XOR_ELIM1: return "NOT_XOR_ELIM1"; - case PfRule::NOT_XOR_ELIM2: return "NOT_XOR_ELIM2"; - case PfRule::ITE_ELIM1: return "ITE_ELIM1"; - case PfRule::ITE_ELIM2: return "ITE_ELIM2"; - case PfRule::NOT_ITE_ELIM1: return "NOT_ITE_ELIM1"; - case PfRule::NOT_ITE_ELIM2: return "NOT_ITE_ELIM2"; - //================================================= De Morgan rules - case PfRule::NOT_AND: return "NOT_AND"; - //================================================= CNF rules - case PfRule::CNF_AND_POS: return "CNF_AND_POS"; - case PfRule::CNF_AND_NEG: return "CNF_AND_NEG"; - case PfRule::CNF_OR_POS: return "CNF_OR_POS"; - case PfRule::CNF_OR_NEG: return "CNF_OR_NEG"; - case PfRule::CNF_IMPLIES_POS: return "CNF_IMPLIES_POS"; - case PfRule::CNF_IMPLIES_NEG1: return "CNF_IMPLIES_NEG1"; - case PfRule::CNF_IMPLIES_NEG2: return "CNF_IMPLIES_NEG2"; - case PfRule::CNF_EQUIV_POS1: return "CNF_EQUIV_POS1"; - case PfRule::CNF_EQUIV_POS2: return "CNF_EQUIV_POS2"; - case PfRule::CNF_EQUIV_NEG1: return "CNF_EQUIV_NEG1"; - case PfRule::CNF_EQUIV_NEG2: return "CNF_EQUIV_NEG2"; - case PfRule::CNF_XOR_POS1: return "CNF_XOR_POS1"; - case PfRule::CNF_XOR_POS2: return "CNF_XOR_POS2"; - case PfRule::CNF_XOR_NEG1: return "CNF_XOR_NEG1"; - case PfRule::CNF_XOR_NEG2: return "CNF_XOR_NEG2"; - case PfRule::CNF_ITE_POS1: return "CNF_ITE_POS1"; - case PfRule::CNF_ITE_POS2: return "CNF_ITE_POS2"; - case PfRule::CNF_ITE_POS3: return "CNF_ITE_POS3"; - case PfRule::CNF_ITE_NEG1: return "CNF_ITE_NEG1"; - case PfRule::CNF_ITE_NEG2: return "CNF_ITE_NEG2"; - case PfRule::CNF_ITE_NEG3: return "CNF_ITE_NEG3"; - //================================================= Equality rules - case PfRule::REFL: return "REFL"; - case PfRule::SYMM: return "SYMM"; - case PfRule::TRANS: return "TRANS"; - case PfRule::CONG: return "CONG"; - case PfRule::TRUE_INTRO: return "TRUE_INTRO"; - case PfRule::TRUE_ELIM: return "TRUE_ELIM"; - case PfRule::FALSE_INTRO: return "FALSE_INTRO"; - case PfRule::FALSE_ELIM: return "FALSE_ELIM"; - case PfRule::HO_APP_ENCODE: return "HO_APP_ENCODE"; - case PfRule::HO_CONG: return "HO_CONG"; - //================================================= Array rules - case PfRule::ARRAYS_READ_OVER_WRITE: return "ARRAYS_READ_OVER_WRITE"; - case PfRule::ARRAYS_READ_OVER_WRITE_CONTRA: - return "ARRAYS_READ_OVER_WRITE_CONTRA"; - case PfRule::ARRAYS_READ_OVER_WRITE_1: return "ARRAYS_READ_OVER_WRITE_1"; - case PfRule::ARRAYS_EXT: return "ARRAYS_EXT"; - case PfRule::ARRAYS_TRUST: return "ARRAYS_TRUST"; - //================================================= Bit-Vector rules - case PfRule::BV_BITBLAST: return "BV_BITBLAST"; - case PfRule::BV_BITBLAST_CONST: return "BV_BITBLAST_CONST"; - case PfRule::BV_BITBLAST_VAR: return "BV_BITBLAST_VAR"; - case PfRule::BV_BITBLAST_EQUAL: return "BV_BITBLAST_EQUAL"; - case PfRule::BV_BITBLAST_ULT: return "BV_BITBLAST_ULT"; - case PfRule::BV_BITBLAST_ULE: return "BV_BITBLAST_ULE"; - case PfRule::BV_BITBLAST_UGT: return "BV_BITBLAST_UGT"; - case PfRule::BV_BITBLAST_UGE: return "BV_BITBLAST_UGE"; - case PfRule::BV_BITBLAST_SLT: return "BV_BITBLAST_SLT"; - case PfRule::BV_BITBLAST_SLE: return "BV_BITBLAST_SLE"; - case PfRule::BV_BITBLAST_SGT: return "BV_BITBLAST_SGT"; - case PfRule::BV_BITBLAST_SGE: return "BV_BITBLAST_SGE"; - case PfRule::BV_BITBLAST_NOT: return "BV_BITBLAST_NOT"; - case PfRule::BV_BITBLAST_CONCAT: return "BV_BITBLAST_CONCAT"; - case PfRule::BV_BITBLAST_AND: return "BV_BITBLAST_AND"; - case PfRule::BV_BITBLAST_OR: return "BV_BITBLAST_OR"; - case PfRule::BV_BITBLAST_XOR: return "BV_BITBLAST_XOR"; - case PfRule::BV_BITBLAST_XNOR: return "BV_BITBLAST_XNOR"; - case PfRule::BV_BITBLAST_NAND: return "BV_BITBLAST_NAND"; - case PfRule::BV_BITBLAST_NOR: return "BV_BITBLAST_NOR"; - case PfRule::BV_BITBLAST_COMP: return "BV_BITBLAST_COMP"; - case PfRule::BV_BITBLAST_MULT: return "BV_BITBLAST_MULT"; - case PfRule::BV_BITBLAST_ADD: return "BV_BITBLAST_ADD"; - case PfRule::BV_BITBLAST_SUB: return "BV_BITBLAST_SUB"; - case PfRule::BV_BITBLAST_NEG: return "BV_BITBLAST_NEG"; - case PfRule::BV_BITBLAST_UDIV: return "BV_BITBLAST_UDIV"; - case PfRule::BV_BITBLAST_UREM: return "BV_BITBLAST_UREM"; - case PfRule::BV_BITBLAST_SDIV: return "BV_BITBLAST_SDIV"; - case PfRule::BV_BITBLAST_SREM: return "BV_BITBLAST_SREM"; - case PfRule::BV_BITBLAST_SMOD: return "BV_BITBLAST_SMOD"; - case PfRule::BV_BITBLAST_SHL: return "BV_BITBLAST_SHL"; - case PfRule::BV_BITBLAST_LSHR: return "BV_BITBLAST_LSHR"; - case PfRule::BV_BITBLAST_ASHR: return "BV_BITBLAST_ASHR"; - case PfRule::BV_BITBLAST_ULTBV: return "BV_BITBLAST_ULTBV"; - case PfRule::BV_BITBLAST_SLTBV: return "BV_BITBLAST_SLTBV"; - case PfRule::BV_BITBLAST_ITE: return "BV_BITBLAST_ITE"; - case PfRule::BV_BITBLAST_EXTRACT: return "BV_BITBLAST_EXTRACT"; - case PfRule::BV_BITBLAST_REPEAT: return "BV_BITBLAST_REPEAT"; - case PfRule::BV_BITBLAST_ZERO_EXTEND: return "BV_BITBLAST_ZERO_EXTEND"; - case PfRule::BV_BITBLAST_SIGN_EXTEND: return "BV_BITBLAST_SIGN_EXTEND"; - case PfRule::BV_BITBLAST_ROTATE_RIGHT: return "BV_BITBLAST_ROTATE_RIGHT"; - case PfRule::BV_BITBLAST_ROTATE_LEFT: return "BV_BITBLAST_ROTATE_LEFT"; - case PfRule::BV_EAGER_ATOM: return "BV_EAGER_ATOM"; - //================================================= Datatype rules - case PfRule::DT_UNIF: return "DT_UNIF"; - case PfRule::DT_INST: return "DT_INST"; - case PfRule::DT_COLLAPSE: return "DT_COLLAPSE"; - case PfRule::DT_SPLIT: return "DT_SPLIT"; - case PfRule::DT_CLASH: return "DT_CLASH"; - case PfRule::DT_TRUST: return "DT_TRUST"; - //================================================= Quantifiers rules - case PfRule::SKOLEM_INTRO: return "SKOLEM_INTRO"; - case PfRule::EXISTS_INTRO: return "EXISTS_INTRO"; - case PfRule::SKOLEMIZE: return "SKOLEMIZE"; - case PfRule::INSTANTIATE: return "INSTANTIATE"; - //================================================= String rules - case PfRule::CONCAT_EQ: return "CONCAT_EQ"; - case PfRule::CONCAT_UNIFY: return "CONCAT_UNIFY"; - case PfRule::CONCAT_CONFLICT: return "CONCAT_CONFLICT"; - case PfRule::CONCAT_SPLIT: return "CONCAT_SPLIT"; - case PfRule::CONCAT_CSPLIT: return "CONCAT_CSPLIT"; - case PfRule::CONCAT_LPROP: return "CONCAT_LPROP"; - case PfRule::CONCAT_CPROP: return "CONCAT_CPROP"; - case PfRule::STRING_DECOMPOSE: return "STRING_DECOMPOSE"; - case PfRule::STRING_LENGTH_POS: return "STRING_LENGTH_POS"; - case PfRule::STRING_LENGTH_NON_EMPTY: return "STRING_LENGTH_NON_EMPTY"; - case PfRule::STRING_REDUCTION: return "STRING_REDUCTION"; - case PfRule::STRING_EAGER_REDUCTION: return "STRING_EAGER_REDUCTION"; - case PfRule::RE_INTER: return "RE_INTER"; - case PfRule::RE_UNFOLD_POS: return "RE_UNFOLD_POS"; - case PfRule::RE_UNFOLD_NEG: return "RE_UNFOLD_NEG"; - case PfRule::RE_UNFOLD_NEG_CONCAT_FIXED: - return "RE_UNFOLD_NEG_CONCAT_FIXED"; - case PfRule::RE_ELIM: return "RE_ELIM"; - case PfRule::STRING_CODE_INJ: return "STRING_CODE_INJ"; - case PfRule::STRING_SEQ_UNIT_INJ: return "STRING_SEQ_UNIT_INJ"; - case PfRule::STRING_TRUST: return "STRING_TRUST"; - //================================================= Arith rules - case PfRule::MACRO_ARITH_SCALE_SUM_UB: - return "ARITH_SCALE_SUM_UPPER_BOUNDS"; - case PfRule::ARITH_SUM_UB: return "ARITH_SUM_UB"; - case PfRule::ARITH_TRICHOTOMY: return "ARITH_TRICHOTOMY"; - case PfRule::INT_TIGHT_LB: return "INT_TIGHT_LB"; - case PfRule::INT_TIGHT_UB: return "INT_TIGHT_UB"; - case PfRule::INT_TRUST: return "INT_TRUST"; - case PfRule::ARITH_MULT_SIGN: return "ARITH_MULT_SIGN"; - case PfRule::ARITH_MULT_POS: return "ARITH_MULT_POS"; - case PfRule::ARITH_MULT_NEG: return "ARITH_MULT_NEG"; - case PfRule::ARITH_MULT_TANGENT: return "ARITH_MULT_TANGENT"; - case PfRule::ARITH_OP_ELIM_AXIOM: return "ARITH_OP_ELIM_AXIOM"; - case PfRule::ARITH_TRANS_PI: return "ARITH_TRANS_PI"; - case PfRule::ARITH_TRANS_EXP_NEG: return "ARITH_TRANS_EXP_NEG"; - case PfRule::ARITH_TRANS_EXP_POSITIVITY: - return "ARITH_TRANS_EXP_POSITIVITY"; - case PfRule::ARITH_TRANS_EXP_SUPER_LIN: return "ARITH_TRANS_EXP_SUPER_LIN"; - case PfRule::ARITH_TRANS_EXP_ZERO: return "ARITH_TRANS_EXP_ZERO"; - case PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_NEG: - return "ARITH_TRANS_EXP_APPROX_ABOVE_NEG"; - case PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_POS: - return "ARITH_TRANS_EXP_APPROX_ABOVE_POS"; - case PfRule::ARITH_TRANS_EXP_APPROX_BELOW: - return "ARITH_TRANS_EXP_APPROX_BELOW"; - case PfRule::ARITH_TRANS_SINE_BOUNDS: return "ARITH_TRANS_SINE_BOUNDS"; - case PfRule::ARITH_TRANS_SINE_SHIFT: return "ARITH_TRANS_SINE_SHIFT"; - case PfRule::ARITH_TRANS_SINE_SYMMETRY: return "ARITH_TRANS_SINE_SYMMETRY"; - case PfRule::ARITH_TRANS_SINE_TANGENT_ZERO: - return "ARITH_TRANS_SINE_TANGENT_ZERO"; - case PfRule::ARITH_TRANS_SINE_TANGENT_PI: - return "ARITH_TRANS_SINE_TANGENT_PI"; - case PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_NEG: - return "ARITH_TRANS_SINE_APPROX_ABOVE_NEG"; - case PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS: - return "ARITH_TRANS_SINE_APPROX_ABOVE_POS"; - case PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG: - return "ARITH_TRANS_SINE_APPROX_BELOW_NEG"; - case PfRule::ARITH_TRANS_SINE_APPROX_BELOW_POS: - return "ARITH_TRANS_SINE_APPROX_BELOW_POS"; - case PfRule::ARITH_NL_CAD_DIRECT: return "ARITH_NL_CAD_DIRECT"; - case PfRule::ARITH_NL_CAD_RECURSIVE: return "ARITH_NL_CAD_RECURSIVE"; - //================================================= Unknown rule - case PfRule::UNKNOWN: return "UNKNOWN"; - default: return "?"; - } -} - -std::ostream& operator<<(std::ostream& out, PfRule id) -{ - out << toString(id); - return out; -} - -size_t PfRuleHashFunction::operator()(PfRule id) const -{ - return static_cast(id); -} - -} // namespace cvc5 diff --git a/src/expr/proof_rule.h b/src/expr/proof_rule.h deleted file mode 100644 index 9771dda31..000000000 --- a/src/expr/proof_rule.h +++ /dev/null @@ -1,1453 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof rule enumeration. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_RULE_H -#define CVC5__EXPR__PROOF_RULE_H - -#include - -namespace cvc5 { - -/** - * An enumeration for proof rules. This enumeration is analogous to Kind for - * Node objects. In the documentation below, P:F denotes a ProofNode that - * proves formula F. - * - * Conceptually, the following proof rules form a calculus whose target - * user is the Node-level theory solvers. This means that the rules below - * are designed to reason about, among other things, common operations on Node - * objects like Rewriter::rewrite or Node::substitute. It is intended to be - * translated or printed in other formats. - * - * The following PfRule values include core rules and those categorized by - * theory, including the theory of equality. - * - * The "core rules" include two distinguished rules which have special status: - * (1) ASSUME, which represents an open leaf in a proof. - * (2) SCOPE, which closes the scope of assumptions. - * The core rules additionally correspond to generic operations that are done - * internally on nodes, e.g. calling Rewriter::rewrite. - * - * Rules with prefix MACRO_ are those that can be defined in terms of other - * rules. These exist for convienience. We provide their definition in the line - * "Macro:". - */ -enum class PfRule : uint32_t -{ - //================================================= Core rules - //======================== Assume and Scope - // ======== Assumption (a leaf) - // Children: none - // Arguments: (F) - // -------------- - // Conclusion: F - // - // This rule has special status, in that an application of assume is an - // open leaf in a proof that is not (yet) justified. An assume leaf is - // analogous to a free variable in a term, where we say "F is a free - // assumption in proof P" if it contains an application of F that is not - // bound by SCOPE (see below). - ASSUME, - // ======== Scope (a binder for assumptions) - // Children: (P:F) - // Arguments: (F1, ..., Fn) - // -------------- - // Conclusion: (=> (and F1 ... Fn) F) or (not (and F1 ... Fn)) if F is false - // - // This rule has a dual purpose with ASSUME. It is a way to close - // assumptions in a proof. We require that F1 ... Fn are free assumptions in - // P and say that F1, ..., Fn are not free in (SCOPE P). In other words, they - // are bound by this application. For example, the proof node: - // (SCOPE (ASSUME F) :args F) - // has the conclusion (=> F F) and has no free assumptions. More generally, a - // proof with no free assumptions always concludes a valid formula. - SCOPE, - - //======================== Builtin theory (common node operations) - // ======== Substitution - // Children: (P1:F1, ..., Pn:Fn) - // Arguments: (t, (ids)?) - // --------------------------------------------------------------- - // Conclusion: (= t t*sigma{ids}(Fn)*...*sigma{ids}(F1)) - // where sigma{ids}(Fi) are substitutions, which notice are applied in - // reverse order. - // Notice that ids is a MethodId identifier, which determines how to convert - // the formulas F1, ..., Fn into substitutions. - SUBS, - // ======== Rewrite - // Children: none - // Arguments: (t, (idr)?) - // ---------------------------------------- - // Conclusion: (= t Rewriter{idr}(t)) - // where idr is a MethodId identifier, which determines the kind of rewriter - // to apply, e.g. Rewriter::rewrite. - REWRITE, - // ======== Evaluate - // Children: none - // Arguments: (t) - // ---------------------------------------- - // Conclusion: (= t Evaluator::evaluate(t)) - // Note this is equivalent to: - // (REWRITE t MethodId::RW_EVALUATE) - EVALUATE, - // ======== Substitution + Rewriting equality introduction - // - // In this rule, we provide a term t and conclude that it is equal to its - // rewritten form under a (proven) substitution. - // - // Children: (P1:F1, ..., Pn:Fn) - // Arguments: (t, (ids (ida (idr)?)?)?) - // --------------------------------------------------------------- - // Conclusion: (= t t') - // where - // t' is - // Rewriter{idr}(t*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) - // - // In other words, from the point of view of Skolem forms, this rule - // transforms t to t' by standard substitution + rewriting. - // - // The arguments ids, ida and idr are optional and specify the identifier of - // the substitution, the substitution application and rewriter respectively to - // be used. For details, see theory/builtin/proof_checker.h. - MACRO_SR_EQ_INTRO, - // ======== Substitution + Rewriting predicate introduction - // - // In this rule, we provide a formula F and conclude it, under the condition - // that it rewrites to true under a proven substitution. - // - // Children: (P1:F1, ..., Pn:Fn) - // Arguments: (F, (ids (ida (idr)?)?)?) - // --------------------------------------------------------------- - // Conclusion: F - // where - // Rewriter{idr}(F*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) == true - // where ids and idr are method identifiers. - // - // More generally, this rule also holds when: - // Rewriter::rewrite(toOriginal(F')) == true - // where F' is the result of the left hand side of the equality above. Here, - // notice that we apply rewriting on the original form of F', meaning that - // this rule may conclude an F whose Skolem form is justified by the - // definition of its (fresh) Skolem variables. For example, this rule may - // justify the conclusion (= k t) where k is the purification Skolem for t, - // e.g. where the original form of k is t. - // - // Furthermore, notice that the rewriting and substitution is applied only - // within the side condition, meaning the rewritten form of the original form - // of F does not escape this rule. - MACRO_SR_PRED_INTRO, - // ======== Substitution + Rewriting predicate elimination - // - // In this rule, if we have proven a formula F, then we may conclude its - // rewritten form under a proven substitution. - // - // Children: (P1:F, P2:F1, ..., P_{n+1}:Fn) - // Arguments: ((ids (ida (idr)?)?)?) - // ---------------------------------------- - // Conclusion: F' - // where - // F' is - // Rewriter{idr}(F*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)). - // where ids and idr are method identifiers. - // - // We rewrite only on the Skolem form of F, similar to MACRO_SR_EQ_INTRO. - MACRO_SR_PRED_ELIM, - // ======== Substitution + Rewriting predicate transform - // - // In this rule, if we have proven a formula F, then we may provide a formula - // G and conclude it if F and G are equivalent after rewriting under a proven - // substitution. - // - // Children: (P1:F, P2:F1, ..., P_{n+1}:Fn) - // Arguments: (G, (ids (ida (idr)?)?)?) - // ---------------------------------------- - // Conclusion: G - // where - // Rewriter{idr}(F*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) == - // Rewriter{idr}(G*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) - // - // More generally, this rule also holds when: - // Rewriter::rewrite(toOriginal(F')) == Rewriter::rewrite(toOriginal(G')) - // where F' and G' are the result of each side of the equation above. Here, - // original forms are used in a similar manner to MACRO_SR_PRED_INTRO above. - MACRO_SR_PRED_TRANSFORM, - //================================================= Processing rules - // ======== Remove Term Formulas Axiom - // Children: none - // Arguments: (t) - // --------------------------------------------------------------- - // Conclusion: RemoveTermFormulas::getAxiomFor(t). - REMOVE_TERM_FORMULA_AXIOM, - - //================================================= Trusted rules - // ======== Theory lemma - // Children: none - // Arguments: (F, tid) - // --------------------------------------------------------------- - // Conclusion: F - // where F is a (T-valid) theory lemma generated by theory with TheoryId tid. - // This is a "coarse-grained" rule that is used as a placeholder if a theory - // did not provide a proof for a lemma or conflict. - THEORY_LEMMA, - // ======== Theory Rewrite - // Children: none - // Arguments: (F, tid, rid) - // ---------------------------------------- - // Conclusion: F - // where F is an equality of the form (= t t') where t' is obtained by - // applying the kind of rewriting given by the method identifier rid, which - // is one of: - // { RW_REWRITE_THEORY_PRE, RW_REWRITE_THEORY_POST, RW_REWRITE_EQ_EXT } - // Notice that the checker for this rule does not replay the rewrite to ensure - // correctness, since theory rewriter methods are not static. For example, - // the quantifiers rewriter involves constructing new bound variables that are - // not guaranteed to be consistent on each call. - THEORY_REWRITE, - // The remaining rules in this section have the signature of a "trusted rule": - // - // Children: none - // Arguments: (F) - // --------------------------------------------------------------- - // Conclusion: F - // - // where F is an equality of the form t = t' where t was replaced by t' - // based on some preprocessing pass, or otherwise F was added as a new - // assertion by some preprocessing pass. - PREPROCESS, - // where F was added as a new assertion by some preprocessing pass. - PREPROCESS_LEMMA, - // where F is an equality of the form t = Theory::ppRewrite(t) for some - // theory. Notice this is a "trusted" rule. - THEORY_PREPROCESS, - // where F was added as a new assertion by theory preprocessing. - THEORY_PREPROCESS_LEMMA, - // where F is an equality of the form t = t' where t was replaced by t' - // based on theory expand definitions. - THEORY_EXPAND_DEF, - // where F is an existential (exists ((x T)) (P x)) used for introducing - // a witness term (witness ((x T)) (P x)). - WITNESS_AXIOM, - // where F is an equality (= t t') that holds by a form of rewriting that - // could not be replayed during proof postprocessing. - TRUST_REWRITE, - // where F is an equality (= t t') that holds by a form of substitution that - // could not be replayed during proof postprocessing. - TRUST_SUBS, - // where F is an equality (= t t') that holds by a form of substitution that - // could not be determined by the TrustSubstitutionMap. - TRUST_SUBS_MAP, - // ========= SAT Refutation for assumption-based unsat cores - // Children: (P1, ..., Pn) - // Arguments: none - // --------------------- - // Conclusion: false - // Note: P1, ..., Pn correspond to the unsat core determined by the SAT - // solver. - SAT_REFUTATION, - - //================================================= Boolean rules - // ======== Resolution - // Children: - // (P1:C1, P2:C2) - // Arguments: (pol, L) - // --------------------- - // Conclusion: C - // where - // - C1 and C2 are nodes viewed as clauses, i.e., either an OR node with - // each children viewed as a literal or a node viewed as a literal. Note - // that an OR node could also be a literal. - // - pol is either true or false, representing the polarity of the pivot on - // the first clause - // - L is the pivot of the resolution, which occurs as is (resp. under a - // NOT) in C1 and negatively (as is) in C2 if pol = true (pol = false). - // C is a clause resulting from collecting all the literals in C1, minus the - // first occurrence of the pivot or its negation, and C2, minus the first - // occurrence of the pivot or its negation, according to the policy above. - // If the resulting clause has a single literal, that literal itself is the - // result; if it has no literals, then the result is false; otherwise it's - // an OR node of the resulting literals. - // - // Note that it may be the case that the pivot does not occur in the - // clauses. In this case the rule is not unsound, but it does not correspond - // to resolution but rather to a weakening of the clause that did not have a - // literal eliminated. - RESOLUTION, - // ======== N-ary Resolution - // Children: (P1:C_1, ..., Pm:C_n) - // Arguments: (pol_1, L_1, ..., pol_{n-1}, L_{n-1}) - // --------------------- - // Conclusion: C - // where - // - let C_1 ... C_n be nodes viewed as clauses, as defined above - // - let "C_1 <>_{L,pol} C_2" represent the resolution of C_1 with C_2 with - // pivot L and polarity pol, as defined above - // - let C_1' = C_1 (from P1), - // - for each i > 1, let C_i' = C_{i-1} <>_{L_{i-1}, pol_{i-1}} C_i' - // The result of the chain resolution is C = C_n' - CHAIN_RESOLUTION, - // ======== Factoring - // Children: (P:C1) - // Arguments: () - // --------------------- - // Conclusion: C2 - // where - // Set representations of C1 and C2 is the same and the number of literals in - // C2 is smaller than that of C1 - FACTORING, - // ======== Reordering - // Children: (P:C1) - // Arguments: (C2) - // --------------------- - // Conclusion: C2 - // where - // Set representations of C1 and C2 are the same and the number of literals - // in C2 is the same of that of C1 - REORDERING, - // ======== N-ary Resolution + Factoring + Reordering - // Children: (P1:C_1, ..., Pm:C_n) - // Arguments: (C, pol_1, L_1, ..., pol_{n-1}, L_{n-1}) - // --------------------- - // Conclusion: C - // where - // - let C_1 ... C_n be nodes viewed as clauses, as defined in RESOLUTION - // - let "C_1 <>_{L,pol} C_2" represent the resolution of C_1 with C_2 with - // pivot L and polarity pol, as defined in RESOLUTION - // - let C_1' be equal, in its set representation, to C_1 (from P1), - // - for each i > 1, let C_i' be equal, it its set representation, to - // C_{i-1} <>_{L_{i-1}, pol_{i-1}} C_i' - // The result of the chain resolution is C, which is equal, in its set - // representation, to C_n' - MACRO_RESOLUTION, - // As above but not checked - MACRO_RESOLUTION_TRUST, - - // ======== Split - // Children: none - // Arguments: (F) - // --------------------- - // Conclusion: (or F (not F)) - SPLIT, - // ======== Equality resolution - // Children: (P1:F1, P2:(= F1 F2)) - // Arguments: none - // --------------------- - // Conclusion: (F2) - // Note this can optionally be seen as a macro for EQUIV_ELIM1+RESOLUTION. - EQ_RESOLVE, - // ======== Modus ponens - // Children: (P1:F1, P2:(=> F1 F2)) - // Arguments: none - // --------------------- - // Conclusion: (F2) - // Note this can optionally be seen as a macro for IMPLIES_ELIM+RESOLUTION. - MODUS_PONENS, - // ======== Double negation elimination - // Children: (P:(not (not F))) - // Arguments: none - // --------------------- - // Conclusion: (F) - NOT_NOT_ELIM, - // ======== Contradiction - // Children: (P1:F P2:(not F)) - // Arguments: () - // --------------------- - // Conclusion: false - CONTRA, - // ======== And elimination - // Children: (P:(and F1 ... Fn)) - // Arguments: (i) - // --------------------- - // Conclusion: (Fi) - AND_ELIM, - // ======== And introduction - // Children: (P1:F1 ... Pn:Fn)) - // Arguments: () - // --------------------- - // Conclusion: (and P1 ... Pn) - AND_INTRO, - // ======== Not Or elimination - // Children: (P:(not (or F1 ... Fn))) - // Arguments: (i) - // --------------------- - // Conclusion: (not Fi) - NOT_OR_ELIM, - // ======== Implication elimination - // Children: (P:(=> F1 F2)) - // Arguments: () - // --------------------- - // Conclusion: (or (not F1) F2) - IMPLIES_ELIM, - // ======== Not Implication elimination version 1 - // Children: (P:(not (=> F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (F1) - NOT_IMPLIES_ELIM1, - // ======== Not Implication elimination version 2 - // Children: (P:(not (=> F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (not F2) - NOT_IMPLIES_ELIM2, - // ======== Equivalence elimination version 1 - // Children: (P:(= F1 F2)) - // Arguments: () - // --------------------- - // Conclusion: (or (not F1) F2) - EQUIV_ELIM1, - // ======== Equivalence elimination version 2 - // Children: (P:(= F1 F2)) - // Arguments: () - // --------------------- - // Conclusion: (or F1 (not F2)) - EQUIV_ELIM2, - // ======== Not Equivalence elimination version 1 - // Children: (P:(not (= F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or F1 F2) - NOT_EQUIV_ELIM1, - // ======== Not Equivalence elimination version 2 - // Children: (P:(not (= F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or (not F1) (not F2)) - NOT_EQUIV_ELIM2, - // ======== XOR elimination version 1 - // Children: (P:(xor F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or F1 F2) - XOR_ELIM1, - // ======== XOR elimination version 2 - // Children: (P:(xor F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or (not F1) (not F2)) - XOR_ELIM2, - // ======== Not XOR elimination version 1 - // Children: (P:(not (xor F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or F1 (not F2)) - NOT_XOR_ELIM1, - // ======== Not XOR elimination version 2 - // Children: (P:(not (xor F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or (not F1) F2) - NOT_XOR_ELIM2, - // ======== ITE elimination version 1 - // Children: (P:(ite C F1 F2)) - // Arguments: () - // --------------------- - // Conclusion: (or (not C) F1) - ITE_ELIM1, - // ======== ITE elimination version 2 - // Children: (P:(ite C F1 F2)) - // Arguments: () - // --------------------- - // Conclusion: (or C F2) - ITE_ELIM2, - // ======== Not ITE elimination version 1 - // Children: (P:(not (ite C F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or (not C) (not F1)) - NOT_ITE_ELIM1, - // ======== Not ITE elimination version 1 - // Children: (P:(not (ite C F1 F2))) - // Arguments: () - // --------------------- - // Conclusion: (or C (not F2)) - NOT_ITE_ELIM2, - - //================================================= De Morgan rules - // ======== Not And - // Children: (P:(not (and F1 ... Fn)) - // Arguments: () - // --------------------- - // Conclusion: (or (not F1) ... (not Fn)) - NOT_AND, - //================================================= CNF rules - // ======== CNF And Pos - // Children: () - // Arguments: ((and F1 ... Fn), i) - // --------------------- - // Conclusion: (or (not (and F1 ... Fn)) Fi) - CNF_AND_POS, - // ======== CNF And Neg - // Children: () - // Arguments: ((and F1 ... Fn)) - // --------------------- - // Conclusion: (or (and F1 ... Fn) (not F1) ... (not Fn)) - CNF_AND_NEG, - // ======== CNF Or Pos - // Children: () - // Arguments: ((or F1 ... Fn)) - // --------------------- - // Conclusion: (or (not (or F1 ... Fn)) F1 ... Fn) - CNF_OR_POS, - // ======== CNF Or Neg - // Children: () - // Arguments: ((or F1 ... Fn), i) - // --------------------- - // Conclusion: (or (or F1 ... Fn) (not Fi)) - CNF_OR_NEG, - // ======== CNF Implies Pos - // Children: () - // Arguments: ((implies F1 F2)) - // --------------------- - // Conclusion: (or (not (implies F1 F2)) (not F1) F2) - CNF_IMPLIES_POS, - // ======== CNF Implies Neg version 1 - // Children: () - // Arguments: ((implies F1 F2)) - // --------------------- - // Conclusion: (or (implies F1 F2) F1) - CNF_IMPLIES_NEG1, - // ======== CNF Implies Neg version 2 - // Children: () - // Arguments: ((implies F1 F2)) - // --------------------- - // Conclusion: (or (implies F1 F2) (not F2)) - CNF_IMPLIES_NEG2, - // ======== CNF Equiv Pos version 1 - // Children: () - // Arguments: ((= F1 F2)) - // --------------------- - // Conclusion: (or (not (= F1 F2)) (not F1) F2) - CNF_EQUIV_POS1, - // ======== CNF Equiv Pos version 2 - // Children: () - // Arguments: ((= F1 F2)) - // --------------------- - // Conclusion: (or (not (= F1 F2)) F1 (not F2)) - CNF_EQUIV_POS2, - // ======== CNF Equiv Neg version 1 - // Children: () - // Arguments: ((= F1 F2)) - // --------------------- - // Conclusion: (or (= F1 F2) F1 F2) - CNF_EQUIV_NEG1, - // ======== CNF Equiv Neg version 2 - // Children: () - // Arguments: ((= F1 F2)) - // --------------------- - // Conclusion: (or (= F1 F2) (not F1) (not F2)) - CNF_EQUIV_NEG2, - // ======== CNF Xor Pos version 1 - // Children: () - // Arguments: ((xor F1 F2)) - // --------------------- - // Conclusion: (or (not (xor F1 F2)) F1 F2) - CNF_XOR_POS1, - // ======== CNF Xor Pos version 2 - // Children: () - // Arguments: ((xor F1 F2)) - // --------------------- - // Conclusion: (or (not (xor F1 F2)) (not F1) (not F2)) - CNF_XOR_POS2, - // ======== CNF Xor Neg version 1 - // Children: () - // Arguments: ((xor F1 F2)) - // --------------------- - // Conclusion: (or (xor F1 F2) (not F1) F2) - CNF_XOR_NEG1, - // ======== CNF Xor Neg version 2 - // Children: () - // Arguments: ((xor F1 F2)) - // --------------------- - // Conclusion: (or (xor F1 F2) F1 (not F2)) - CNF_XOR_NEG2, - // ======== CNF ITE Pos version 1 - // Children: () - // Arguments: ((ite C F1 F2)) - // --------------------- - // Conclusion: (or (not (ite C F1 F2)) (not C) F1) - CNF_ITE_POS1, - // ======== CNF ITE Pos version 2 - // Children: () - // Arguments: ((ite C F1 F2)) - // --------------------- - // Conclusion: (or (not (ite C F1 F2)) C F2) - CNF_ITE_POS2, - // ======== CNF ITE Pos version 3 - // Children: () - // Arguments: ((ite C F1 F2)) - // --------------------- - // Conclusion: (or (not (ite C F1 F2)) F1 F2) - CNF_ITE_POS3, - // ======== CNF ITE Neg version 1 - // Children: () - // Arguments: ((ite C F1 F2)) - // --------------------- - // Conclusion: (or (ite C F1 F2) (not C) (not F1)) - CNF_ITE_NEG1, - // ======== CNF ITE Neg version 2 - // Children: () - // Arguments: ((ite C F1 F2)) - // --------------------- - // Conclusion: (or (ite C F1 F2) C (not F2)) - CNF_ITE_NEG2, - // ======== CNF ITE Neg version 3 - // Children: () - // Arguments: ((ite C F1 F2)) - // --------------------- - // Conclusion: (or (ite C F1 F2) (not F1) (not F2)) - CNF_ITE_NEG3, - - //================================================= Equality rules - // ======== Reflexive - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (= t t) - REFL, - // ======== Symmetric - // Children: (P:(= t1 t2)) or (P:(not (= t1 t2))) - // Arguments: none - // ----------------------- - // Conclusion: (= t2 t1) or (not (= t2 t1)) - SYMM, - // ======== Transitivity - // Children: (P1:(= t1 t2), ..., Pn:(= t{n-1} tn)) - // Arguments: none - // ----------------------- - // Conclusion: (= t1 tn) - TRANS, - // ======== Congruence - // Children: (P1:(= t1 s1), ..., Pn:(= tn sn)) - // Arguments: ( f?) - // --------------------------------------------- - // Conclusion: (= ( f? t1 ... tn) ( f? s1 ... sn)) - // Notice that f must be provided iff is a parameterized kind, e.g. - // APPLY_UF. The actual node for is constructible via - // ProofRuleChecker::mkKindNode. - CONG, - // ======== True intro - // Children: (P:F) - // Arguments: none - // ---------------------------------------- - // Conclusion: (= F true) - TRUE_INTRO, - // ======== True elim - // Children: (P:(= F true)) - // Arguments: none - // ---------------------------------------- - // Conclusion: F - TRUE_ELIM, - // ======== False intro - // Children: (P:(not F)) - // Arguments: none - // ---------------------------------------- - // Conclusion: (= F false) - FALSE_INTRO, - // ======== False elim - // Children: (P:(= F false)) - // Arguments: none - // ---------------------------------------- - // Conclusion: (not F) - FALSE_ELIM, - // ======== HO trust - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (= t TheoryUfRewriter::getHoApplyForApplyUf(t)) - // For example, this rule concludes (f x y) = (HO_APPLY (HO_APPLY f x) y) - HO_APP_ENCODE, - // ======== Congruence - // Children: (P1:(= f g), P2:(= t1 s1), ..., Pn+1:(= tn sn)) - // Arguments: () - // --------------------------------------------- - // Conclusion: (= (f t1 ... tn) (g s1 ... sn)) - // Notice that this rule is only used when the application kinds are APPLY_UF. - HO_CONG, - - //================================================= Array rules - // ======== Read over write - // Children: (P:(not (= i1 i2))) - // Arguments: ((select (store a i2 e) i1)) - // ---------------------------------------- - // Conclusion: (= (select (store a i2 e) i1) (select a i1)) - ARRAYS_READ_OVER_WRITE, - // ======== Read over write, contrapositive - // Children: (P:(not (= (select (store a i2 e) i1) (select a i1))) - // Arguments: none - // ---------------------------------------- - // Conclusion: (= i1 i2) - ARRAYS_READ_OVER_WRITE_CONTRA, - // ======== Read over write 1 - // Children: none - // Arguments: ((select (store a i e) i)) - // ---------------------------------------- - // Conclusion: (= (select (store a i e) i) e) - ARRAYS_READ_OVER_WRITE_1, - // ======== Extensionality - // Children: (P:(not (= a b))) - // Arguments: none - // ---------------------------------------- - // Conclusion: (not (= (select a k) (select b k))) - // where k is arrays::SkolemCache::getExtIndexSkolem((not (= a b))). - ARRAYS_EXT, - // ======== Array Trust - // Children: (P1 ... Pn) - // Arguments: (F) - // --------------------- - // Conclusion: F - ARRAYS_TRUST, - - //================================================= Bit-Vector rules - // Note: bitblast() represents the result of the bit-blasted term as a - // bit-vector consisting of the output bits of the bit-blasted circuit - // representation of the term. Terms are bit-blasted according to the - // strategies defined in - // theory/bv/bitblast/bitblast_strategies_template.h. - // ======== Bitblast - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (= t bitblast(t)) - BV_BITBLAST, - // ======== Bitblast Bit-Vector Constant - // Children: none - // Arguments: (= t bitblast(t)) - // --------------------- - // Conclusion: (= t bitblast(t)) - BV_BITBLAST_CONST, - // ======== Bitblast Bit-Vector Variable - // Children: none - // Arguments: (= t bitblast(t)) - // --------------------- - // Conclusion: (= t bitblast(t)) - BV_BITBLAST_VAR, - // ======== Bitblast Bit-Vector Terms - // TODO cvc4-projects issue #275 - // Children: none - // Arguments: (= (KIND bitblast(child_1) ... bitblast(child_n)) bitblast(t)) - // --------------------- - // Conclusion: (= (KIND bitblast(child_1) ... bitblast(child_n)) bitblast(t)) - BV_BITBLAST_EQUAL, - BV_BITBLAST_ULT, - BV_BITBLAST_ULE, - BV_BITBLAST_UGT, - BV_BITBLAST_UGE, - BV_BITBLAST_SLT, - BV_BITBLAST_SLE, - BV_BITBLAST_SGT, - BV_BITBLAST_SGE, - BV_BITBLAST_NOT, - BV_BITBLAST_CONCAT, - BV_BITBLAST_AND, - BV_BITBLAST_OR, - BV_BITBLAST_XOR, - BV_BITBLAST_XNOR, - BV_BITBLAST_NAND, - BV_BITBLAST_NOR, - BV_BITBLAST_COMP, - BV_BITBLAST_MULT, - BV_BITBLAST_ADD, - BV_BITBLAST_SUB, - BV_BITBLAST_NEG, - BV_BITBLAST_UDIV, - BV_BITBLAST_UREM, - BV_BITBLAST_SDIV, - BV_BITBLAST_SREM, - BV_BITBLAST_SMOD, - BV_BITBLAST_SHL, - BV_BITBLAST_LSHR, - BV_BITBLAST_ASHR, - BV_BITBLAST_ULTBV, - BV_BITBLAST_SLTBV, - BV_BITBLAST_ITE, - BV_BITBLAST_EXTRACT, - BV_BITBLAST_REPEAT, - BV_BITBLAST_ZERO_EXTEND, - BV_BITBLAST_SIGN_EXTEND, - BV_BITBLAST_ROTATE_RIGHT, - BV_BITBLAST_ROTATE_LEFT, - // ======== Eager Atom - // Children: none - // Arguments: (F) - // --------------------- - // Conclusion: (= F F[0]) - // where F is of kind BITVECTOR_EAGER_ATOM - BV_EAGER_ATOM, - - //================================================= Datatype rules - // ======== Unification - // Children: (P:(= (C t1 ... tn) (C s1 ... sn))) - // Arguments: (i) - // ---------------------------------------- - // Conclusion: (= ti si) - // where C is a constructor. - DT_UNIF, - // ======== Instantiate - // Children: none - // Arguments: (t, n) - // ---------------------------------------- - // Conclusion: (= ((_ is C) t) (= t (C (sel_1 t) ... (sel_n t)))) - // where C is the n^th constructor of the type of T, and (_ is C) is the - // discriminator (tester) for C. - DT_INST, - // ======== Collapse - // Children: none - // Arguments: ((sel_i (C_j t_1 ... t_n))) - // ---------------------------------------- - // Conclusion: (= (sel_i (C_j t_1 ... t_n)) r) - // where C_j is a constructor, r is t_i if sel_i is a correctly applied - // selector, or TypeNode::mkGroundTerm() of the proper type otherwise. Notice - // that the use of mkGroundTerm differs from the rewriter which uses - // mkGroundValue in this case. - DT_COLLAPSE, - // ======== Split - // Children: none - // Arguments: (t) - // ---------------------------------------- - // Conclusion: (or ((_ is C1) t) ... ((_ is Cn) t)) - DT_SPLIT, - // ======== Clash - // Children: (P1:((_ is Ci) t), P2: ((_ is Cj) t)) - // Arguments: none - // ---------------------------------------- - // Conclusion: false - // for i != j. - DT_CLASH, - // ======== Datatype Trust - // Children: (P1 ... Pn) - // Arguments: (F) - // --------------------- - // Conclusion: F - DT_TRUST, - - //================================================= Quantifiers rules - // ======== Skolem intro - // Children: none - // Arguments: (k) - // ---------------------------------------- - // Conclusion: (= k t) - // where t is the original form of skolem k. - SKOLEM_INTRO, - // ======== Exists intro - // Children: (P:F[t]) - // Arguments: ((exists ((x T)) F[x])) - // ---------------------------------------- - // Conclusion: (exists ((x T)) F[x]) - // This rule verifies that F[x] indeed matches F[t] with a substitution - // over x. - EXISTS_INTRO, - // ======== Skolemize - // Children: (P:(exists ((x1 T1) ... (xn Tn)) F)) - // Arguments: none - // ---------------------------------------- - // Conclusion: F*sigma - // sigma maps x1 ... xn to their representative skolems obtained by - // SkolemManager::mkSkolemize, returned in the skolems argument of that - // method. Alternatively, can use negated forall as a premise. The witness - // terms for the returned skolems can be obtained by - // SkolemManager::getWitnessForm. - SKOLEMIZE, - // ======== Instantiate - // Children: (P:(forall ((x1 T1) ... (xn Tn)) F)) - // Arguments: (t1 ... tn) - // ---------------------------------------- - // Conclusion: F*sigma - // sigma maps x1 ... xn to t1 ... tn. - INSTANTIATE, - - //================================================= String rules - //======================== Core solver - // ======== Concat eq - // Children: (P1:(= (str.++ t1 ... tn t) (str.++ t1 ... tn s))) - // Arguments: (b), indicating if reverse direction - // --------------------- - // Conclusion: (= t s) - // - // Notice that t or s may be empty, in which case they are implicit in the - // concatenation above. For example, if - // P1 concludes (= x (str.++ x z)), then - // (CONCAT_EQ P1 :args false) concludes (= "" z) - // - // Also note that constants are split, such that if - // P1 concludes (= (str.++ "abc" x) (str.++ "a" y)), then - // (CONCAT_EQ P1 :args false) concludes (= (str.++ "bc" x) y) - // This splitting is done only for constants such that Word::splitConstant - // returns non-null. - CONCAT_EQ, - // ======== Concat unify - // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), - // P2:(= (str.len t1) (str.len s1))) - // Arguments: (b), indicating if reverse direction - // --------------------- - // Conclusion: (= t1 s1) - CONCAT_UNIFY, - // ======== Concat conflict - // Children: (P1:(= (str.++ c1 t) (str.++ c2 s))) - // Arguments: (b), indicating if reverse direction - // --------------------- - // Conclusion: false - // Where c1, c2 are constants such that Word::splitConstant(c1,c2,index,b) - // is null, in other words, neither is a prefix of the other. - CONCAT_CONFLICT, - // ======== Concat split - // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), - // P2:(not (= (str.len t1) (str.len s1)))) - // Arguments: (false) - // --------------------- - // Conclusion: (or (= t1 (str.++ s1 r_t)) (= s1 (str.++ t1 r_s))) - // where - // r_t = (skolem (suf t1 (str.len s1)))), - // r_s = (skolem (suf s1 (str.len t1)))). - // - // or the reverse form of the above: - // - // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), - // P2:(not (= (str.len t2) (str.len s2)))) - // Arguments: (true) - // --------------------- - // Conclusion: (or (= t2 (str.++ r_t s2)) (= s2 (str.++ r_s t2))) - // where - // r_t = (skolem (pre t2 (- (str.len t2) (str.len s2))))), - // r_s = (skolem (pre s2 (- (str.len s2) (str.len t2))))). - // - // Above, (suf x n) is shorthand for (str.substr x n (- (str.len x) n)) and - // (pre x n) is shorthand for (str.substr x 0 n). - CONCAT_SPLIT, - // ======== Concat constant split - // Children: (P1:(= (str.++ t1 t2) (str.++ c s2)), - // P2:(not (= (str.len t1) 0))) - // Arguments: (false) - // --------------------- - // Conclusion: (= t1 (str.++ c r)) - // where - // r = (skolem (suf t1 1)). - // - // or the reverse form of the above: - // - // Children: (P1:(= (str.++ t1 t2) (str.++ s1 c)), - // P2:(not (= (str.len t2) 0))) - // Arguments: (true) - // --------------------- - // Conclusion: (= t2 (str.++ r c)) - // where - // r = (skolem (pre t2 (- (str.len t2) 1))). - CONCAT_CSPLIT, - // ======== Concat length propagate - // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), - // P2:(> (str.len t1) (str.len s1))) - // Arguments: (false) - // --------------------- - // Conclusion: (= t1 (str.++ s1 r_t)) - // where - // r_t = (skolem (suf t1 (str.len s1))) - // - // or the reverse form of the above: - // - // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), - // P2:(> (str.len t2) (str.len s2))) - // Arguments: (false) - // --------------------- - // Conclusion: (= t2 (str.++ r_t s2)) - // where - // r_t = (skolem (pre t2 (- (str.len t2) (str.len s2)))). - CONCAT_LPROP, - // ======== Concat constant propagate - // Children: (P1:(= (str.++ t1 w1 t2) (str.++ w2 s)), - // P2:(not (= (str.len t1) 0))) - // Arguments: (false) - // --------------------- - // Conclusion: (= t1 (str.++ w3 r)) - // where - // w1, w2, w3, w4 are words, - // w3 is (pre w2 p), - // w4 is (suf w2 p), - // p = Word::overlap((suf w2 1), w1), - // r = (skolem (suf t1 (str.len w3))). - // In other words, w4 is the largest suffix of (suf w2 1) that can contain a - // prefix of w1; since t1 is non-empty, w3 must therefore be contained in t1. - // - // or the reverse form of the above: - // - // Children: (P1:(= (str.++ t1 w1 t2) (str.++ s w2)), - // P2:(not (= (str.len t2) 0))) - // Arguments: (true) - // --------------------- - // Conclusion: (= t2 (str.++ r w3)) - // where - // w1, w2, w3, w4 are words, - // w3 is (suf w2 (- (str.len w2) p)), - // w4 is (pre w2 (- (str.len w2) p)), - // p = Word::roverlap((pre w2 (- (str.len w2) 1)), w1), - // r = (skolem (pre t2 (- (str.len t2) (str.len w3)))). - // In other words, w4 is the largest prefix of (pre w2 (- (str.len w2) 1)) - // that can contain a suffix of w1; since t2 is non-empty, w3 must therefore - // be contained in t2. - CONCAT_CPROP, - // ======== String decompose - // Children: (P1: (>= (str.len t) n) - // Arguments: (false) - // --------------------- - // Conclusion: (and (= t (str.++ w1 w2)) (= (str.len w1) n)) - // or - // Children: (P1: (>= (str.len t) n) - // Arguments: (true) - // --------------------- - // Conclusion: (and (= t (str.++ w1 w2)) (= (str.len w2) n)) - // where - // w1 is (skolem (pre t n)) - // w2 is (skolem (suf t n)) - STRING_DECOMPOSE, - // ======== Length positive - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (or (and (= (str.len t) 0) (= t "")) (> (str.len t) 0)) - STRING_LENGTH_POS, - // ======== Length non-empty - // Children: (P1:(not (= t ""))) - // Arguments: none - // --------------------- - // Conclusion: (not (= (str.len t) 0)) - STRING_LENGTH_NON_EMPTY, - //======================== Extended functions - // ======== Reduction - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (and R (= t w)) - // where w = strings::StringsPreprocess::reduce(t, R, ...). - // In other words, R is the reduction predicate for extended term t, and w is - // (skolem t) - // Notice that the free variables of R are w and the free variables of t. - STRING_REDUCTION, - // ======== Eager Reduction - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: R - // where R = strings::TermRegistry::eagerReduce(t). - STRING_EAGER_REDUCTION, - //======================== Regular expressions - // ======== Regular expression intersection - // Children: (P:(str.in.re t R1), P:(str.in.re t R2)) - // Arguments: none - // --------------------- - // Conclusion: (str.in.re t (re.inter R1 R2)). - RE_INTER, - // ======== Regular expression unfold positive - // Children: (P:(str.in.re t R)) - // Arguments: none - // --------------------- - // Conclusion:(RegExpOpr::reduceRegExpPos((str.in.re t R))), - // corresponding to the one-step unfolding of the premise. - RE_UNFOLD_POS, - // ======== Regular expression unfold negative - // Children: (P:(not (str.in.re t R))) - // Arguments: none - // --------------------- - // Conclusion:(RegExpOpr::reduceRegExpNeg((not (str.in.re t R)))), - // corresponding to the one-step unfolding of the premise. - RE_UNFOLD_NEG, - // ======== Regular expression unfold negative concat fixed - // Children: (P:(not (str.in.re t R))) - // Arguments: none - // --------------------- - // Conclusion:(RegExpOpr::reduceRegExpNegConcatFixed((not (str.in.re t - // R)),L,i)) where RegExpOpr::getRegExpConcatFixed((not (str.in.re t R)), i) = - // L. corresponding to the one-step unfolding of the premise, optimized for - // fixed length of component i of the regular expression concatenation R. - RE_UNFOLD_NEG_CONCAT_FIXED, - // ======== Regular expression elimination - // Children: none - // Arguments: (F, b) - // --------------------- - // Conclusion: (= F strings::RegExpElimination::eliminate(F, b)) - // where b is a Boolean indicating whether we are using aggressive - // eliminations. Notice this rule concludes (= F F) if no eliminations - // are performed for F. - RE_ELIM, - //======================== Code points - // Children: none - // Arguments: (t, s) - // --------------------- - // Conclusion:(or (= (str.code t) (- 1)) - // (not (= (str.code t) (str.code s))) - // (not (= t s))) - STRING_CODE_INJ, - //======================== Sequence unit - // Children: (P:(= (seq.unit x) (seq.unit y))) - // Arguments: none - // --------------------- - // Conclusion:(= x y) - // Also applies to the case where (seq.unit y) is a constant sequence - // of length one. - STRING_SEQ_UNIT_INJ, - // ======== String Trust - // Children: none - // Arguments: (Q) - // --------------------- - // Conclusion: (Q) - STRING_TRUST, - - //================================================= Arithmetic rules - // ======== Adding Inequalities - // Note: an ArithLiteral is a term of the form (>< poly const) - // where - // >< is >=, >, ==, <, <=, or not(== ...). - // poly is a polynomial - // const is a rational constant - - // Children: (P1:l1, ..., Pn:ln) - // where each li is an ArithLiteral - // not(= ...) is dis-allowed! - // - // Arguments: (k1, ..., kn), non-zero reals - // --------------------- - // Conclusion: (>< t1 t2) - // where >< is the fusion of the combination of the >< is always one of <, <= - // NB: this implies that lower bounds must have negative ki, - // and upper bounds must have positive ki. - // t1 is the sum of the scaled polynomials (k_1 * poly_1 + ... + k_n * - // poly_n) t2 is the sum of the scaled constants (k_1 * const_1 + ... + k_n - // * const_n) - MACRO_ARITH_SCALE_SUM_UB, - // ======== Sum Upper Bounds - // Children: (P1, ... , Pn) - // where each Pi has form (>< L R) - // where >< is < if any > i c)) - // where i has integer type. - // Arguments: none - // --------------------- - // Conclusion: (>= i leastIntGreaterThan(c)}) - INT_TIGHT_LB, - // ======== Trichotomy of the reals - // Children: (A B) - // Arguments: (C) - // --------------------- - // Conclusion: (C), - // where (not A) (not B) and C - // are (> x c) (< x c) and (= x c) - // in some order - // note that "not" here denotes arithmetic negation, flipping - // >= to <, etc. - ARITH_TRICHOTOMY, - // ======== Arithmetic operator elimination - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: arith::OperatorElim::getAxiomFor(t) - ARITH_OP_ELIM_AXIOM, - // ======== Int Trust - // Children: (P1 ... Pn) - // Arguments: (Q) - // --------------------- - // Conclusion: (Q) - INT_TRUST, - - //======== Multiplication sign inference - // Children: none - // Arguments: (f1, ..., fk, m) - // --------------------- - // Conclusion: (=> (and f1 ... fk) (~ m 0)) - // Where f1, ..., fk are variables compared to zero (less, greater or not - // equal), m is a monomial from these variables, and ~ is the comparison (less - // or greater) that results from the signs of the variables. All variables - // with even exponent in m should be given as not equal to zero while all - // variables with odd exponent in m should be given as less or greater than - // zero. - ARITH_MULT_SIGN, - //======== Multiplication with positive factor - // Children: none - // Arguments: (m, (rel lhs rhs)) - // --------------------- - // Conclusion: (=> (and (> m 0) (rel lhs rhs)) (rel (* m lhs) (* m rhs))) - // Where rel is a relation symbol. - ARITH_MULT_POS, - //======== Multiplication with negative factor - // Children: none - // Arguments: (m, (rel lhs rhs)) - // --------------------- - // Conclusion: (=> (and (< m 0) (rel lhs rhs)) (rel_inv (* m lhs) (* m rhs))) - // Where rel is a relation symbol and rel_inv the inverted relation symbol. - ARITH_MULT_NEG, - //======== Multiplication tangent plane - // Children: none - // Arguments: (t, x, y, a, b, sgn) - // --------------------- - // Conclusion: - // sgn=-1: (= (<= t tplane) (or (and (<= x a) (>= y b)) (and (>= x a) (<= y - // b))) sgn= 1: (= (>= t tplane) (or (and (<= x a) (<= y b)) (and (>= x a) - // (>= y b))) - // Where x,y are real terms (variables or extended terms), t = (* x y) - // (possibly under rewriting), a,b are real constants, and sgn is either -1 - // or 1. tplane is the tangent plane of x*y at (a,b): b*x + a*y - a*b - ARITH_MULT_TANGENT, - - // ================ Lemmas for transcendentals - //======== Assert bounds on PI - // Children: none - // Arguments: (l, u) - // --------------------- - // Conclusion: (and (>= real.pi l) (<= real.pi u)) - // Where l (u) is a valid lower (upper) bound on pi. - ARITH_TRANS_PI, - //======== Exp at negative values - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (= (< t 0) (< (exp t) 1)) - ARITH_TRANS_EXP_NEG, - //======== Exp is always positive - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (> (exp t) 0) - ARITH_TRANS_EXP_POSITIVITY, - //======== Exp grows super-linearly for positive values - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (or (<= t 0) (> exp(t) (+ t 1))) - ARITH_TRANS_EXP_SUPER_LIN, - //======== Exp at zero - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (= (= t 0) (= (exp t) 1)) - ARITH_TRANS_EXP_ZERO, - //======== Exp is approximated from above for negative values - // Children: none - // Arguments: (d, t, l, u) - // --------------------- - // Conclusion: (=> (and (>= t l) (<= t u)) (<= (exp t) (secant exp l u t)) - // Where d is an even positive number, t an arithmetic term and l (u) a lower - // (upper) bound on t. Let p be the d'th taylor polynomial at zero (also - // called the Maclaurin series) of the exponential function. (secant exp l u - // t) denotes the secant of p from (l, exp(l)) to (u, exp(u)) evaluated at t, - // calculated as follows: - // (p(l) - p(u)) / (l - u) * (t - l) + p(l) - // The lemma states that if t is between l and u, then (exp t) is below the - // secant of p from l to u. - ARITH_TRANS_EXP_APPROX_ABOVE_NEG, - //======== Exp is approximated from above for positive values - // Children: none - // Arguments: (d, t, l, u) - // --------------------- - // Conclusion: (=> (and (>= t l) (<= t u)) (<= (exp t) (secant-pos exp l u t)) - // Where d is an even positive number, t an arithmetic term and l (u) a lower - // (upper) bound on t. Let p* be a modification of the d'th taylor polynomial - // at zero (also called the Maclaurin series) of the exponential function as - // follows where p(d-1) is the regular Maclaurin series of degree d-1: - // p* = p(d-1) * (1 + t^n / n!) - // (secant-pos exp l u t) denotes the secant of p from (l, exp(l)) to (u, - // exp(u)) evaluated at t, calculated as follows: - // (p(l) - p(u)) / (l - u) * (t - l) + p(l) - // The lemma states that if t is between l and u, then (exp t) is below the - // secant of p from l to u. - ARITH_TRANS_EXP_APPROX_ABOVE_POS, - //======== Exp is approximated from below - // Children: none - // Arguments: (d, t) - // --------------------- - // Conclusion: (>= (exp t) (maclaurin exp d t)) - // Where d is an odd positive number and (maclaurin exp d t) is the d'th - // taylor polynomial at zero (also called the Maclaurin series) of the - // exponential function evaluated at t. The Maclaurin series for the - // exponential function is the following: - // e^x = \sum_{n=0}^{\infty} x^n / n! - ARITH_TRANS_EXP_APPROX_BELOW, - //======== Sine is always between -1 and 1 - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (and (<= (sin t) 1) (>= (sin t) (- 1))) - ARITH_TRANS_SINE_BOUNDS, - //======== Sine arg shifted to -pi..pi - // Children: none - // Arguments: (x, y, s) - // --------------------- - // Conclusion: (and - // (<= -pi y pi) - // (= (sin y) (sin x)) - // (ite (<= -pi x pi) (= x y) (= x (+ y (* 2 pi s)))) - // ) - // Where x is the argument to sine, y is a new real skolem that is x shifted - // into -pi..pi and s is a new integer skolem that is the number of phases y - // is shifted. - ARITH_TRANS_SINE_SHIFT, - //======== Sine is symmetric with respect to negation of the argument - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (= (- (sin t) (sin (- t))) 0) - ARITH_TRANS_SINE_SYMMETRY, - //======== Sine is bounded by the tangent at zero - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (and - // (=> (> t 0) (< (sin t) t)) - // (=> (< t 0) (> (sin t) t)) - // ) - ARITH_TRANS_SINE_TANGENT_ZERO, - //======== Sine is bounded by the tangents at -pi and pi - // Children: none - // Arguments: (t) - // --------------------- - // Conclusion: (and - // (=> (> t -pi) (> (sin t) (- -pi t))) - // (=> (< t pi) (< (sin t) (- pi t))) - // ) - ARITH_TRANS_SINE_TANGENT_PI, - //======== Sine is approximated from above for negative values - // Children: none - // Arguments: (d, t, lb, ub, l, u) - // --------------------- - // Conclusion: (=> (and (>= t lb) (<= t ub)) (<= (sin t) (secant sin l u t)) - // Where d is an even positive number, t an arithmetic term, lb (ub) a - // symbolic lower (upper) bound on t (possibly containing pi) and l (u) the - // evaluated lower (upper) bound on t. Let p be the d'th taylor polynomial at - // zero (also called the Maclaurin series) of the sine function. (secant sin l - // u t) denotes the secant of p from (l, sin(l)) to (u, sin(u)) evaluated at - // t, calculated as follows: - // (p(l) - p(u)) / (l - u) * (t - l) + p(l) - // The lemma states that if t is between l and u, then (sin t) is below the - // secant of p from l to u. - ARITH_TRANS_SINE_APPROX_ABOVE_NEG, - //======== Sine is approximated from above for positive values - // Children: none - // Arguments: (d, t, c, lb, ub) - // --------------------- - // Conclusion: (=> (and (>= t lb) (<= t ub)) (<= (sin t) (upper sin c)) - // Where d is an even positive number, t an arithmetic term, c an arithmetic - // constant, and lb (ub) a symbolic lower (upper) bound on t (possibly - // containing pi). Let p be the d'th taylor polynomial at zero (also called - // the Maclaurin series) of the sine function. (upper sin c) denotes the upper - // bound on sin(c) given by p and lb,ub are such that sin(t) is the maximum of - // the sine function on (lb,ub). - ARITH_TRANS_SINE_APPROX_ABOVE_POS, - //======== Sine is approximated from below for negative values - // Children: none - // Arguments: (d, t, c, lb, ub) - // --------------------- - // Conclusion: (=> (and (>= t lb) (<= t ub)) (>= (sin t) (lower sin c)) - // Where d is an even positive number, t an arithmetic term, c an arithmetic - // constant, and lb (ub) a symbolic lower (upper) bound on t (possibly - // containing pi). Let p be the d'th taylor polynomial at zero (also called - // the Maclaurin series) of the sine function. (lower sin c) denotes the lower - // bound on sin(c) given by p and lb,ub are such that sin(t) is the minimum of - // the sine function on (lb,ub). - ARITH_TRANS_SINE_APPROX_BELOW_NEG, - //======== Sine is approximated from below for positive values - // Children: none - // Arguments: (d, t, lb, ub, l, u) - // --------------------- - // Conclusion: (=> (and (>= t lb) (<= t ub)) (>= (sin t) (secant sin l u t)) - // Where d is an even positive number, t an arithmetic term, lb (ub) a - // symbolic lower (upper) bound on t (possibly containing pi) and l (u) the - // evaluated lower (upper) bound on t. Let p be the d'th taylor polynomial at - // zero (also called the Maclaurin series) of the sine function. (secant sin l - // u t) denotes the secant of p from (l, sin(l)) to (u, sin(u)) evaluated at - // t, calculated as follows: - // (p(l) - p(u)) / (l - u) * (t - l) + p(l) - // The lemma states that if t is between l and u, then (sin t) is above the - // secant of p from l to u. - ARITH_TRANS_SINE_APPROX_BELOW_POS, - - // ================ CAD Lemmas - // We use IRP for IndexedRootPredicate. - // - // A formula "Interval" describes that a variable (xn is none is given) is - // within a particular interval whose bounds are given as IRPs. It is either - // an open interval or a point interval: - // (IRP k poly) < xn < (IRP k poly) - // xn == (IRP k poly) - // - // A formula "Cell" describes a portion - // of the real space in the following form: - // Interval(x1) and Interval(x2) and ... - // We implicitly assume a Cell to go up to n-1 (and can be empty). - // - // A formula "Covering" is a set of Intervals, implying that xn can be in - // neither of these intervals. To be a covering (of the real line), the union - // of these intervals should be the real numbers. - // ======== CAD direct conflict - // Children (Cell, A) - // --------------------- - // Conclusion: (false) - // A direct interval is generated from an assumption A (in variables x1...xn) - // over a Cell (in variables x1...xn). It derives that A evaluates to false - // over the Cell. In the actual algorithm, it means that xn can not be in the - // topmost interval of the Cell. - ARITH_NL_CAD_DIRECT, - // ======== CAD recursive interval - // Children (Cell, Covering) - // --------------------- - // Conclusion: (false) - // A recursive interval is generated from a Covering (for xn) over a Cell - // (in variables x1...xn-1). It generates the conclusion that no xn exists - // that extends the Cell and satisfies all assumptions. - ARITH_NL_CAD_RECURSIVE, - - //================================================= Unknown rule - UNKNOWN, -}; - -/** - * Converts a proof rule to a string. Note: This function is also used in - * `safe_print()`. Changing this function name or signature will result in - * `safe_print()` printing "" instead of the proper strings for - * the enum values. - * - * @param id The proof rule - * @return The name of the proof rule - */ -const char* toString(PfRule id); - -/** - * Writes a proof rule name to a stream. - * - * @param out The stream to write to - * @param id The proof rule to write to the stream - * @return The stream - */ -std::ostream& operator<<(std::ostream& out, PfRule id); - -/** Hash function for proof rules */ -struct PfRuleHashFunction -{ - size_t operator()(PfRule id) const; -}; /* struct PfRuleHashFunction */ - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_RULE_H */ diff --git a/src/expr/proof_set.h b/src/expr/proof_set.h deleted file mode 100644 index 5ea9c1021..000000000 --- a/src/expr/proof_set.h +++ /dev/null @@ -1,76 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Gereon Kremer, Andrew Reynolds - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof set utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_SET_H -#define CVC5__EXPR__PROOF_SET_H - -#include - -#include "context/cdlist.h" -#include "context/context.h" -#include "expr/proof_node_manager.h" - -namespace cvc5 { - -/** - * A (context-dependent) set of proofs, which is used for memory - * management purposes. - */ -template -class CDProofSet -{ - public: - CDProofSet(ProofNodeManager* pnm, - context::Context* c, - std::string namePrefix = "Proof") - : d_pnm(pnm), d_proofs(c), d_namePrefix(namePrefix) - { - } - /** - * Allocate a new proof. - * - * This returns a fresh proof object that remains alive in the context given - * to this class. Internally, this adds a new proof of type T to a - * context-dependent list of proofs and passes the following arguments to the - * T constructor: - * pnm, args..., name - * where pnm is the proof node manager - * provided to this proof set upon construction, args... are the arguments to - * allocateProof() and name is the namePrefix with an appended index. - */ - template - T* allocateProof(Args&&... args) - { - d_proofs.push_back(std::make_shared( - d_pnm, - std::forward(args)..., - d_namePrefix + "_" + std::to_string(d_proofs.size()))); - return d_proofs.back().get(); - } - - protected: - /** The proof node manager */ - ProofNodeManager* d_pnm; - /** A context-dependent list of lazy proofs. */ - context::CDList> d_proofs; - /** The name prefix of the lazy proofs */ - std::string d_namePrefix; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__LAZY_PROOF_SET_H */ diff --git a/src/expr/proof_step_buffer.cpp b/src/expr/proof_step_buffer.cpp deleted file mode 100644 index 84cfc040c..000000000 --- a/src/expr/proof_step_buffer.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of proof step and proof step buffer utilities. - */ - -#include "expr/proof_step_buffer.h" - -#include "expr/proof_checker.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -ProofStep::ProofStep() : d_rule(PfRule::UNKNOWN) {} -ProofStep::ProofStep(PfRule r, - const std::vector& children, - const std::vector& args) - : d_rule(r), d_children(children), d_args(args) -{ -} -std::ostream& operator<<(std::ostream& out, ProofStep step) -{ - out << "(step " << step.d_rule; - for (const Node& c : step.d_children) - { - out << " " << c; - } - if (!step.d_args.empty()) - { - out << " :args"; - for (const Node& a : step.d_args) - { - out << " " << a; - } - } - out << ")"; - return out; -} - -ProofStepBuffer::ProofStepBuffer(ProofChecker* pc) : d_checker(pc) {} - -Node ProofStepBuffer::tryStep(PfRule id, - const std::vector& children, - const std::vector& args, - Node expected) -{ - if (d_checker == nullptr) - { - Assert(false) << "ProofStepBuffer::ProofStepBuffer: no proof checker."; - return Node::null(); - } - Node res = - d_checker->checkDebug(id, children, args, expected, "pf-step-buffer"); - if (!res.isNull()) - { - // add proof step - d_steps.push_back( - std::pair(res, ProofStep(id, children, args))); - } - return res; -} - -void ProofStepBuffer::addStep(PfRule id, - const std::vector& children, - const std::vector& args, - Node expected) -{ - d_steps.push_back( - std::pair(expected, ProofStep(id, children, args))); -} - -void ProofStepBuffer::addSteps(ProofStepBuffer& psb) -{ - const std::vector>& steps = psb.getSteps(); - for (const std::pair& step : steps) - { - addStep(step.second.d_rule, - step.second.d_children, - step.second.d_args, - step.first); - } -} - -void ProofStepBuffer::popStep() -{ - Assert(!d_steps.empty()); - if (!d_steps.empty()) - { - d_steps.pop_back(); - } -} - -size_t ProofStepBuffer::getNumSteps() const { return d_steps.size(); } - -const std::vector>& ProofStepBuffer::getSteps() const -{ - return d_steps; -} - -void ProofStepBuffer::clear() { d_steps.clear(); } - -} // namespace cvc5 diff --git a/src/expr/proof_step_buffer.h b/src/expr/proof_step_buffer.h deleted file mode 100644 index b9350cf45..000000000 --- a/src/expr/proof_step_buffer.h +++ /dev/null @@ -1,98 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Proof step and proof step buffer utilities. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__PROOF_STEP_BUFFER_H -#define CVC5__EXPR__PROOF_STEP_BUFFER_H - -#include - -#include "expr/node.h" -#include "expr/proof_rule.h" - -namespace cvc5 { - -class ProofChecker; - -/** - * Information for constructing a step in a CDProof. Notice that the conclusion - * of the proof step is intentionally not included in this data structure. - * Instead, it is intended that conclusions may be associated with proof steps - * based on e.g. the result of proof checking. - */ -class ProofStep -{ - public: - ProofStep(); - ProofStep(PfRule r, - const std::vector& children, - const std::vector& args); - /** The proof rule */ - PfRule d_rule; - /** The proof children */ - std::vector d_children; - /** The proof arguments */ - std::vector d_args; -}; -std::ostream& operator<<(std::ostream& out, ProofStep step); - -/** - * Class used to speculatively try and buffer a set of proof steps before - * sending them to a proof object. - */ -class ProofStepBuffer -{ - public: - ProofStepBuffer(ProofChecker* pc = nullptr); - ~ProofStepBuffer() {} - /** - * Returns the conclusion of the proof step, as determined by the proof - * checker of the given proof. If this is non-null, then the given step - * is added to the buffer maintained by this class. - * - * If expected is non-null, then this method returns null if the result of - * checking is not equal to expected. - */ - Node tryStep(PfRule id, - const std::vector& children, - const std::vector& args, - Node expected = Node::null()); - /** Same as above, without checking */ - void addStep(PfRule id, - const std::vector& children, - const std::vector& args, - Node expected); - /** Multi-step version */ - void addSteps(ProofStepBuffer& psb); - /** pop step */ - void popStep(); - /** Get num steps */ - size_t getNumSteps() const; - /** Get steps */ - const std::vector>& getSteps() const; - /** Clear */ - void clear(); - - private: - /** The proof checker*/ - ProofChecker* d_checker; - /** the queued proof steps */ - std::vector> d_steps; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__PROOF_STEP_BUFFER_H */ diff --git a/src/expr/tconv_seq_proof_generator.cpp b/src/expr/tconv_seq_proof_generator.cpp deleted file mode 100644 index 00e961628..000000000 --- a/src/expr/tconv_seq_proof_generator.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Term conversion sequence proof generator utility. - */ - -#include "expr/tconv_seq_proof_generator.h" - -#include - -#include "expr/proof_node_manager.h" - -namespace cvc5 { - -TConvSeqProofGenerator::TConvSeqProofGenerator( - ProofNodeManager* pnm, - const std::vector& ts, - context::Context* c, - std::string name) - : d_pnm(pnm), d_converted(c), d_name(name) -{ - d_tconvs.insert(d_tconvs.end(), ts.begin(), ts.end()); - AlwaysAssert(!d_tconvs.empty()) - << "TConvSeqProofGenerator::TConvSeqProofGenerator: expecting non-empty " - "sequence"; -} - -TConvSeqProofGenerator::~TConvSeqProofGenerator() {} - -void TConvSeqProofGenerator::registerConvertedTerm(Node t, Node s, size_t index) -{ - if (t == s) - { - // no need - return; - } - std::pair key = std::pair(t, index); - d_converted[key] = s; -} - -std::shared_ptr TConvSeqProofGenerator::getProofFor(Node f) -{ - Trace("tconv-seq-pf-gen") - << "TConvSeqProofGenerator::getProofFor: " << identify() << ": " << f - << std::endl; - return getSubsequenceProofFor(f, 0, d_tconvs.size() - 1); -} - -std::shared_ptr TConvSeqProofGenerator::getSubsequenceProofFor( - Node f, size_t start, size_t end) -{ - Assert(end < d_tconvs.size()); - if (f.getKind() != kind::EQUAL) - { - std::stringstream serr; - serr << "TConvSeqProofGenerator::getProofFor: " << identify() - << ": fail, non-equality " << f; - Unhandled() << serr.str(); - Trace("tconv-seq-pf-gen") << serr.str() << std::endl; - return nullptr; - } - // start with the left hand side of the equality - Node curr = f[0]; - // proofs forming transitivity chain - std::vector> transChildren; - std::pair currKey; - NodeIndexNodeMap::iterator itc; - // convert the term in sequence - for (size_t i = start; i <= end; i++) - { - currKey = std::pair(curr, i); - itc = d_converted.find(currKey); - // if we provided a conversion at this index via registerConvertedTerm - if (itc != d_converted.end()) - { - Node next = (*itc).second; - Trace("tconv-seq-pf-gen") << "...convert to " << next << std::endl; - Node eq = curr.eqNode(next); - std::shared_ptr pf = d_tconvs[i]->getProofFor(eq); - transChildren.push_back(pf); - curr = next; - } - } - // should end up with the right hand side of the equality - if (curr != f[1]) - { - // unexpected - std::stringstream serr; - serr << "TConvSeqProofGenerator::getProofFor: " << identify() - << ": failed, mismatch (see -t tconv-seq-pf-gen-debug for details)" - << std::endl; - serr << " source: " << f[0] << std::endl; - serr << "expected after conversions: " << f[1] << std::endl; - serr << " actual after conversions: " << curr << std::endl; - - if (Trace.isOn("tconv-seq-pf-gen-debug")) - { - Trace("tconv-pf-gen-debug") - << "Printing conversion steps..." << std::endl; - serr << "Conversions: " << std::endl; - for (NodeIndexNodeMap::const_iterator it = d_converted.begin(); - it != d_converted.end(); - ++it) - { - serr << "(" << (*it).first.first << ", " << (*it).first.second - << ") -> " << (*it).second << std::endl; - } - } - Unhandled() << serr.str(); - return nullptr; - } - // otherwise, make transitivity - return d_pnm->mkTrans(transChildren, f); -} - -theory::TrustNode TConvSeqProofGenerator::mkTrustRewriteSequence( - const std::vector& cterms) -{ - Assert(cterms.size() == d_tconvs.size() + 1); - if (cterms[0] == cterms[cterms.size() - 1]) - { - return theory::TrustNode::null(); - } - bool useThis = false; - ProofGenerator* pg = nullptr; - for (size_t i = 0, nconvs = d_tconvs.size(); i < nconvs; i++) - { - if (cterms[i] == cterms[i + 1]) - { - continue; - } - else if (pg == nullptr) - { - // Maybe the i^th generator can explain it alone, which must be the case - // if there is only one position in the sequence where the term changes. - // We may overwrite pg with this class if another step is encountered in - // this loop. - pg = d_tconvs[i]; - } - else - { - // need more than a single generator, use this class - useThis = true; - break; - } - } - if (useThis) - { - pg = this; - // if more than two steps, we must register each conversion step - for (size_t i = 0, nconvs = d_tconvs.size(); i < nconvs; i++) - { - registerConvertedTerm(cterms[i], cterms[i + 1], i); - } - } - Assert(pg != nullptr); - return theory::TrustNode::mkTrustRewrite( - cterms[0], cterms[cterms.size() - 1], pg); -} - -std::string TConvSeqProofGenerator::identify() const { return d_name; } - -} // namespace cvc5 diff --git a/src/expr/tconv_seq_proof_generator.h b/src/expr/tconv_seq_proof_generator.h deleted file mode 100644 index bc067f60a..000000000 --- a/src/expr/tconv_seq_proof_generator.h +++ /dev/null @@ -1,121 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Term conversion sequence proof generator utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__TCONV_SEQ_PROOF_GENERATOR_H -#define CVC5__EXPR__TCONV_SEQ_PROOF_GENERATOR_H - -#include "context/cdhashmap.h" -#include "expr/node.h" -#include "expr/proof_generator.h" -#include "theory/trust_node.h" - -namespace cvc5 { - -class ProofNodeManager; - -/** - * The term conversion sequence proof generator. This is used for maintaining - * a fixed sequence of proof generators that provide proofs for rewrites - * (equalities). We call these the "component generators" of this sequence, - * which are typically TConvProofGenerator. - */ -class TConvSeqProofGenerator : public ProofGenerator -{ - public: - /** - * @param pnm The proof node manager for constructing ProofNode objects. - * @param ts The list of component term conversion generators that are - * applied in sequence - * @param c The context that this class depends on. If none is provided, - * this class is context-independent. - * @param name The name of this generator (for debugging). - */ - TConvSeqProofGenerator(ProofNodeManager* pnm, - const std::vector& ts, - context::Context* c = nullptr, - std::string name = "TConvSeqProofGenerator"); - ~TConvSeqProofGenerator(); - /** - * Indicate that the index^th proof generator converts term t to s. This - * should be called for a unique s for each (t, index). It must be the - * case that d_tconv[index] can provide a proof for t = s in the remainder - * of the context maintained by this class. - */ - void registerConvertedTerm(Node t, Node s, size_t index); - /** - * Get the proof for formula f. It should be the case that f is of the form - * t_0 = t_n, where it must be the case that t_n is obtained by the following: - * For each i=0, ... n, let t_{i+1} be the term such that - * registerConvertedTerm(t_i, t_{i+1}, i) - * was called. Otherwise t_{i+1} = t_i. - * In other words, t_n is obtained by converting t_0, in order, based on the - * calls to registerConvertedTerm. - * - * @param f The equality fact to get the proof for. - * @return The proof for f. - */ - std::shared_ptr getProofFor(Node f) override; - /** - * Get subsequence proof for f, with given start and end steps (inclusive). - */ - std::shared_ptr getSubsequenceProofFor(Node f, - size_t start, - size_t end); - /** Identify this generator (for debugging, etc..) */ - std::string identify() const override; - - /** - * Make trust node from a sequence of converted terms. The number of - * terms in cterms should be 1 + the number of component proof generators - * maintained by this class. This selects a proof generator that is capable - * of proving cterms[0] = cterms[cterms.size()-1], which is either this - * generator, or one of the component proof generators, if only one step - * rewrote. In the former case, all steps are registered to this class. - * Using a component generator is an optimization that saves having to - * save the conversion steps or use this class. For example, if we have 2 - * term conversion components, and call this method on: - * { a, b, c } - * then this method calls: - * registerConvertedTerm( a, b, 0 ) - * registerConvertedTerm( b, c, 1 ) - * and returns a trust node proving (= a c) with this class as the proof - * generator. On the other hand, if we call this method on: - * { d, d, e } - * then we return a trust node proving (= d e) with the 2nd component proof - * generator, as it alone is capable of proving this equality. - */ - theory::TrustNode mkTrustRewriteSequence(const std::vector& cterms); - - protected: - using NodeIndexPairHashFunction = - PairHashFunction>; - typedef context:: - CDHashMap, Node, NodeIndexPairHashFunction> - NodeIndexNodeMap; - /** The proof node manager */ - ProofNodeManager* d_pnm; - /** The term conversion generators */ - std::vector d_tconvs; - /** the set of converted terms */ - NodeIndexNodeMap d_converted; - /** Name identifier */ - std::string d_name; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__TCONV_SEQ_PROOF_GENERATOR_H */ diff --git a/src/expr/term_conversion_proof_generator.cpp b/src/expr/term_conversion_proof_generator.cpp deleted file mode 100644 index 0e0ed3165..000000000 --- a/src/expr/term_conversion_proof_generator.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of term conversion proof generator utility. - */ - -#include "expr/term_conversion_proof_generator.h" - -#include - -#include "expr/proof_checker.h" -#include "expr/proof_node.h" -#include "expr/proof_node_algorithm.h" -#include "expr/term_context.h" -#include "expr/term_context_stack.h" - -using namespace cvc5::kind; - -namespace cvc5 { - -std::ostream& operator<<(std::ostream& out, TConvPolicy tcpol) -{ - switch (tcpol) - { - case TConvPolicy::FIXPOINT: out << "FIXPOINT"; break; - case TConvPolicy::ONCE: out << "ONCE"; break; - default: out << "TConvPolicy:unknown"; break; - } - return out; -} - -std::ostream& operator<<(std::ostream& out, TConvCachePolicy tcpol) -{ - switch (tcpol) - { - case TConvCachePolicy::STATIC: out << "STATIC"; break; - case TConvCachePolicy::DYNAMIC: out << "DYNAMIC"; break; - case TConvCachePolicy::NEVER: out << "NEVER"; break; - default: out << "TConvCachePolicy:unknown"; break; - } - return out; -} - -TConvProofGenerator::TConvProofGenerator(ProofNodeManager* pnm, - context::Context* c, - TConvPolicy pol, - TConvCachePolicy cpol, - std::string name, - TermContext* tccb, - bool rewriteOps) - : d_proof(pnm, nullptr, c, name + "::LazyCDProof"), - d_preRewriteMap(c ? c : &d_context), - d_postRewriteMap(c ? c : &d_context), - d_policy(pol), - d_cpolicy(cpol), - d_name(name), - d_tcontext(tccb), - d_rewriteOps(rewriteOps) -{ -} - -TConvProofGenerator::~TConvProofGenerator() {} - -void TConvProofGenerator::addRewriteStep(Node t, - Node s, - ProofGenerator* pg, - bool isPre, - PfRule trustId, - bool isClosed, - uint32_t tctx) -{ - Node eq = registerRewriteStep(t, s, tctx, isPre); - if (!eq.isNull()) - { - d_proof.addLazyStep(eq, pg, trustId, isClosed); - } -} - -void TConvProofGenerator::addRewriteStep( - Node t, Node s, ProofStep ps, bool isPre, uint32_t tctx) -{ - Node eq = registerRewriteStep(t, s, tctx, isPre); - if (!eq.isNull()) - { - d_proof.addStep(eq, ps); - } -} - -void TConvProofGenerator::addRewriteStep(Node t, - Node s, - PfRule id, - const std::vector& children, - const std::vector& args, - bool isPre, - uint32_t tctx) -{ - Node eq = registerRewriteStep(t, s, tctx, isPre); - if (!eq.isNull()) - { - d_proof.addStep(eq, id, children, args); - } -} - -bool TConvProofGenerator::hasRewriteStep(Node t, - uint32_t tctx, - bool isPre) const -{ - return !getRewriteStep(t, tctx, isPre).isNull(); -} - -Node TConvProofGenerator::getRewriteStep(Node t, - uint32_t tctx, - bool isPre) const -{ - Node thash = t; - if (d_tcontext != nullptr) - { - thash = TCtxNode::computeNodeHash(t, tctx); - } - return getRewriteStepInternal(thash, isPre); -} - -Node TConvProofGenerator::registerRewriteStep(Node t, - Node s, - uint32_t tctx, - bool isPre) -{ - Assert(!t.isNull()); - Assert(!s.isNull()); - - if (t == s) - { - return Node::null(); - } - Node thash = t; - if (d_tcontext != nullptr) - { - thash = TCtxNode::computeNodeHash(t, tctx); - } - else - { - // don't use term context ids if not using term context - Assert(tctx == 0); - } - // should not rewrite term to two different things - if (!getRewriteStepInternal(thash, isPre).isNull()) - { - Assert(getRewriteStepInternal(thash, isPre) == s) - << identify() << " rewriting " << t << " to both " << s << " and " - << getRewriteStepInternal(thash, isPre); - return Node::null(); - } - NodeNodeMap& rm = isPre ? d_preRewriteMap : d_postRewriteMap; - rm[thash] = s; - if (d_cpolicy == TConvCachePolicy::DYNAMIC) - { - // clear the cache - d_cache.clear(); - } - return t.eqNode(s); -} - -std::shared_ptr TConvProofGenerator::getProofFor(Node f) -{ - Trace("tconv-pf-gen") << "TConvProofGenerator::getProofFor: " << identify() - << ": " << f << std::endl; - if (f.getKind() != EQUAL) - { - std::stringstream serr; - serr << "TConvProofGenerator::getProofFor: " << identify() - << ": fail, non-equality " << f; - Unhandled() << serr.str(); - Trace("tconv-pf-gen") << serr.str() << std::endl; - return nullptr; - } - // we use the existing proofs - LazyCDProof lpf( - d_proof.getManager(), &d_proof, nullptr, d_name + "::LazyCDProof"); - if (f[0] == f[1]) - { - // assertion failure in debug - Assert(false) << "TConvProofGenerator::getProofFor: " << identify() - << ": don't ask for trivial proofs"; - lpf.addStep(f, PfRule::REFL, {}, {f[0]}); - } - else - { - Node conc = getProofForRewriting(f[0], lpf, d_tcontext); - if (conc != f) - { - bool debugTraceEnabled = Trace.isOn("tconv-pf-gen-debug"); - Assert(conc.getKind() == EQUAL && conc[0] == f[0]); - std::stringstream serr; - serr << "TConvProofGenerator::getProofFor: " << toStringDebug() - << ": failed, mismatch"; - if (!debugTraceEnabled) - { - serr << " (see -t tconv-pf-gen-debug for details)"; - } - serr << std::endl; - serr << " source: " << f[0] << std::endl; - serr << " requested conclusion: " << f[1] << std::endl; - serr << "conclusion from generator: " << conc[1] << std::endl; - - if (debugTraceEnabled) - { - Trace("tconv-pf-gen-debug") << "Printing rewrite steps..." << std::endl; - for (size_t r = 0; r < 2; r++) - { - const NodeNodeMap& rm = r == 0 ? d_preRewriteMap : d_postRewriteMap; - serr << "Rewrite steps (" << (r == 0 ? "pre" : "post") - << "):" << std::endl; - for (NodeNodeMap::const_iterator it = rm.begin(); it != rm.end(); - ++it) - { - serr << (*it).first << " -> " << (*it).second << std::endl; - } - } - } - Unhandled() << serr.str(); - return nullptr; - } - } - std::shared_ptr pfn = lpf.getProofFor(f); - Trace("tconv-pf-gen") << "... success" << std::endl; - Assert (pfn!=nullptr); - Trace("tconv-pf-gen-debug") << "... proof is " << *pfn << std::endl; - return pfn; -} - -std::shared_ptr TConvProofGenerator::getProofForRewriting(Node n) -{ - LazyCDProof lpf( - d_proof.getManager(), &d_proof, nullptr, d_name + "::LazyCDProofRew"); - Node conc = getProofForRewriting(n, lpf, d_tcontext); - if (conc[1] == n) - { - // assertion failure in debug - Assert(false) << "TConvProofGenerator::getProofForRewriting: " << identify() - << ": don't ask for trivial proofs"; - lpf.addStep(conc, PfRule::REFL, {}, {n}); - } - std::shared_ptr pfn = lpf.getProofFor(conc); - Assert(pfn != nullptr); - Trace("tconv-pf-gen-debug") << "... proof is " << *pfn << std::endl; - return pfn; -} - -Node TConvProofGenerator::getProofForRewriting(Node t, - LazyCDProof& pf, - TermContext* tctx) -{ - NodeManager* nm = NodeManager::currentNM(); - // Invariant: if visited[hash(t)] = s or rewritten[hash(t)] = s and t,s are - // distinct, then pf is able to generate a proof of t=s. We must - // Node in the domains of the maps below due to hashing creating new (SEXPR) - // nodes. - - // the final rewritten form of terms - std::unordered_map visited; - // the rewritten form of terms we have processed so far - std::unordered_map rewritten; - std::unordered_map::iterator it; - std::unordered_map::iterator itr; - std::map >::iterator itc; - Trace("tconv-pf-gen-rewrite") - << "TConvProofGenerator::getProofForRewriting: " << toStringDebug() - << std::endl; - Trace("tconv-pf-gen-rewrite") << "Input: " << t << std::endl; - // if provided, we use term context for cache - std::shared_ptr visitctx; - // otherwise, visit is used if we don't have a term context - std::vector visit; - Node tinitialHash; - if (tctx != nullptr) - { - visitctx = std::make_shared(tctx); - visitctx->pushInitial(t); - tinitialHash = TCtxNode::computeNodeHash(t, tctx->initialValue()); - } - else - { - visit.push_back(t); - tinitialHash = t; - } - Node cur; - uint32_t curCVal = 0; - Node curHash; - do - { - // pop the top element - if (tctx != nullptr) - { - std::pair curPair = visitctx->getCurrent(); - cur = curPair.first; - curCVal = curPair.second; - curHash = TCtxNode::computeNodeHash(cur, curCVal); - visitctx->pop(); - } - else - { - cur = visit.back(); - curHash = cur; - visit.pop_back(); - } - Trace("tconv-pf-gen-rewrite") << "* visit : " << curHash << std::endl; - // has the proof for cur been cached? - itc = d_cache.find(curHash); - if (itc != d_cache.end()) - { - Node res = itc->second->getResult(); - Assert(res.getKind() == EQUAL); - Assert(!res[1].isNull()); - visited[curHash] = res[1]; - pf.addProof(itc->second); - continue; - } - it = visited.find(curHash); - if (it == visited.end()) - { - Trace("tconv-pf-gen-rewrite") << "- previsit" << std::endl; - visited[curHash] = Node::null(); - // did we rewrite the current node (at pre-rewrite)? - Node rcur = getRewriteStepInternal(curHash, true); - if (!rcur.isNull()) - { - Trace("tconv-pf-gen-rewrite") - << "*** " << curHash << " prerewrites to " << rcur << std::endl; - // d_proof has a proof of cur = rcur. Hence there is nothing - // to do here, as pf will reference d_proof to get its proof. - if (d_policy == TConvPolicy::FIXPOINT) - { - // It may be the case that rcur also rewrites, thus we cannot assign - // the final rewritten form for cur yet. Instead we revisit cur after - // finishing visiting rcur. - rewritten[curHash] = rcur; - if (tctx != nullptr) - { - visitctx->push(cur, curCVal); - visitctx->push(rcur, curCVal); - } - else - { - visit.push_back(cur); - visit.push_back(rcur); - } - } - else - { - Assert(d_policy == TConvPolicy::ONCE); - Trace("tconv-pf-gen-rewrite") << "-> (once, prewrite) " << curHash - << " = " << rcur << std::endl; - // not rewriting again, rcur is final - Assert(!rcur.isNull()); - visited[curHash] = rcur; - doCache(curHash, cur, rcur, pf); - } - } - else if (tctx != nullptr) - { - visitctx->push(cur, curCVal); - // visit operator if apply uf - if (d_rewriteOps && cur.getKind() == APPLY_UF) - { - visitctx->pushOp(cur, curCVal); - } - visitctx->pushChildren(cur, curCVal); - } - else - { - visit.push_back(cur); - // visit operator if apply uf - if (d_rewriteOps && cur.getKind() == APPLY_UF) - { - visit.push_back(cur.getOperator()); - } - visit.insert(visit.end(), cur.begin(), cur.end()); - } - } - else if (it->second.isNull()) - { - itr = rewritten.find(curHash); - if (itr != rewritten.end()) - { - // only can generate partially rewritten nodes when rewrite again is - // true. - Assert(d_policy != TConvPolicy::ONCE); - // if it was rewritten, check the status of the rewritten node, - // which should be finished now - Node rcur = itr->second; - Trace("tconv-pf-gen-rewrite") - << "- postvisit, previously rewritten to " << rcur << std::endl; - Node rcurHash = rcur; - if (tctx != nullptr) - { - rcurHash = TCtxNode::computeNodeHash(rcur, curCVal); - } - Assert(cur != rcur); - // the final rewritten form of cur is the final form of rcur - Node rcurFinal = visited[rcurHash]; - Assert(!rcurFinal.isNull()); - if (rcurFinal != rcur) - { - // must connect via TRANS - std::vector pfChildren; - pfChildren.push_back(cur.eqNode(rcur)); - pfChildren.push_back(rcur.eqNode(rcurFinal)); - Node result = cur.eqNode(rcurFinal); - pf.addStep(result, PfRule::TRANS, pfChildren, {}); - } - Trace("tconv-pf-gen-rewrite") - << "-> (rewritten postrewrite) " << curHash << " = " << rcurFinal - << std::endl; - visited[curHash] = rcurFinal; - doCache(curHash, cur, rcurFinal, pf); - } - else - { - Trace("tconv-pf-gen-rewrite") << "- postvisit" << std::endl; - Node ret = cur; - Node retHash = curHash; - bool childChanged = false; - std::vector children; - Kind ck = cur.getKind(); - if (d_rewriteOps && ck == APPLY_UF) - { - // the operator of APPLY_UF is visited - Node cop = cur.getOperator(); - if (tctx != nullptr) - { - uint32_t coval = tctx->computeValueOp(cur, curCVal); - Node coHash = TCtxNode::computeNodeHash(cop, coval); - it = visited.find(coHash); - } - else - { - it = visited.find(cop); - } - Assert(it != visited.end()); - Assert(!it->second.isNull()); - childChanged = childChanged || cop != it->second; - children.push_back(it->second); - } - else if (cur.getMetaKind() == metakind::PARAMETERIZED) - { - // all other parametrized operators are unchanged - children.push_back(cur.getOperator()); - } - // get the results of the children - if (tctx != nullptr) - { - for (size_t i = 0, nchild = cur.getNumChildren(); i < nchild; i++) - { - Node cn = cur[i]; - uint32_t cnval = tctx->computeValue(cur, curCVal, i); - Node cnHash = TCtxNode::computeNodeHash(cn, cnval); - it = visited.find(cnHash); - Assert(it != visited.end()); - Assert(!it->second.isNull()); - childChanged = childChanged || cn != it->second; - children.push_back(it->second); - } - } - else - { - // can use simple loop if not term-context-sensitive - for (const Node& cn : cur) - { - it = visited.find(cn); - Assert(it != visited.end()); - Assert(!it->second.isNull()); - childChanged = childChanged || cn != it->second; - children.push_back(it->second); - } - } - if (childChanged) - { - ret = nm->mkNode(ck, children); - rewritten[curHash] = ret; - // congruence to show (cur = ret) - PfRule congRule = PfRule::CONG; - std::vector pfChildren; - std::vector pfArgs; - pfArgs.push_back(ProofRuleChecker::mkKindNode(ck)); - if (ck == APPLY_UF && children[0] != cur.getOperator()) - { - // use HO_CONG if the operator changed - congRule = PfRule::HO_CONG; - pfChildren.push_back(cur.getOperator().eqNode(children[0])); - } - else if (kind::metaKindOf(ck) == kind::metakind::PARAMETERIZED) - { - pfArgs.push_back(cur.getOperator()); - } - for (size_t i = 0, size = cur.getNumChildren(); i < size; i++) - { - if (cur[i] == ret[i]) - { - // ensure REFL proof for unchanged children - pf.addStep(cur[i].eqNode(cur[i]), PfRule::REFL, {}, {cur[i]}); - } - pfChildren.push_back(cur[i].eqNode(ret[i])); - } - Node result = cur.eqNode(ret); - pf.addStep(result, congRule, pfChildren, pfArgs); - // must update the hash - retHash = ret; - if (tctx != nullptr) - { - retHash = TCtxNode::computeNodeHash(ret, curCVal); - } - } - else if (tctx != nullptr) - { - // now we need the hash - retHash = TCtxNode::computeNodeHash(cur, curCVal); - } - // did we rewrite ret (at post-rewrite)? - Node rret = getRewriteStepInternal(retHash, false); - if (!rret.isNull() && d_policy == TConvPolicy::FIXPOINT) - { - Trace("tconv-pf-gen-rewrite") - << "*** " << retHash << " postrewrites to " << rret << std::endl; - // d_proof should have a proof of ret = rret, hence nothing to do - // here, for the same reasons as above. It also may be the case that - // rret rewrites, hence we must revisit ret. - rewritten[retHash] = rret; - if (tctx != nullptr) - { - if (cur != ret) - { - visitctx->push(cur, curCVal); - } - visitctx->push(ret, curCVal); - visitctx->push(rret, curCVal); - } - else - { - if (cur != ret) - { - visit.push_back(cur); - } - visit.push_back(ret); - visit.push_back(rret); - } - } - else - { - // If we changed due to congruence, and then rewrote, then we - // require a trans step to connect here - if (!rret.isNull() && childChanged) - { - std::vector pfChildren; - pfChildren.push_back(cur.eqNode(ret)); - pfChildren.push_back(ret.eqNode(rret)); - Node result = cur.eqNode(rret); - pf.addStep(result, PfRule::TRANS, pfChildren, {}); - } - // take its rewrite if it rewrote and we have ONCE rewriting policy - ret = rret.isNull() ? ret : rret; - Trace("tconv-pf-gen-rewrite") - << "-> (postrewrite) " << curHash << " = " << ret << std::endl; - // it is final - Assert(!ret.isNull()); - visited[curHash] = ret; - doCache(curHash, cur, ret, pf); - } - } - } - else - { - Trace("tconv-pf-gen-rewrite") << "- already visited" << std::endl; - } - } while (!(tctx != nullptr ? visitctx->empty() : visit.empty())); - Assert(visited.find(tinitialHash) != visited.end()); - Assert(!visited.find(tinitialHash)->second.isNull()); - Trace("tconv-pf-gen-rewrite") - << "...finished, return " << visited[tinitialHash] << std::endl; - // return the conclusion of the overall proof - return t.eqNode(visited[tinitialHash]); -} - -void TConvProofGenerator::doCache(Node curHash, - Node cur, - Node r, - LazyCDProof& pf) -{ - if (d_cpolicy != TConvCachePolicy::NEVER) - { - Node eq = cur.eqNode(r); - d_cache[curHash] = pf.getProofFor(eq); - } -} - -Node TConvProofGenerator::getRewriteStepInternal(Node t, bool isPre) const -{ - const NodeNodeMap& rm = isPre ? d_preRewriteMap : d_postRewriteMap; - NodeNodeMap::const_iterator it = rm.find(t); - if (it == rm.end()) - { - return Node::null(); - } - return (*it).second; -} -std::string TConvProofGenerator::identify() const { return d_name; } - -std::string TConvProofGenerator::toStringDebug() const -{ - std::stringstream ss; - ss << identify() << " (policy=" << d_policy << ", cache policy=" << d_cpolicy - << (d_tcontext != nullptr ? ", term-context-sensitive" : "") << ")"; - return ss.str(); -} - -} // namespace cvc5 diff --git a/src/expr/term_conversion_proof_generator.h b/src/expr/term_conversion_proof_generator.h deleted file mode 100644 index 70e606db4..000000000 --- a/src/expr/term_conversion_proof_generator.h +++ /dev/null @@ -1,256 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Term conversion proof generator utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__EXPR__TERM_CONVERSION_PROOF_GENERATOR_H -#define CVC5__EXPR__TERM_CONVERSION_PROOF_GENERATOR_H - -#include "context/cdhashmap.h" -#include "expr/lazy_proof.h" -#include "expr/proof_generator.h" - -namespace cvc5 { - -class ProofNodeManager; -class TermContext; - -/** A policy for how rewrite steps are applied in TConvProofGenerator */ -enum class TConvPolicy : uint32_t -{ - // steps are applied to fix-point, common use case is PfRule::REWRITE - FIXPOINT, - // steps are applied once at pre-rewrite, common use case is PfRule::SUBS - ONCE, -}; -/** Writes a term conversion policy name to a stream. */ -std::ostream& operator<<(std::ostream& out, TConvPolicy tcpol); - -/** A policy for how proofs are cached in TConvProofGenerator */ -enum class TConvCachePolicy : uint32_t -{ - // proofs are statically cached - STATIC, - // proofs are dynamically cached, cleared when a new rewrite is added - DYNAMIC, - // proofs are never cached - NEVER, -}; -/** Writes a term conversion cache policy name to a stream. */ -std::ostream& operator<<(std::ostream& out, TConvCachePolicy tcpol); - -/** - * The term conversion proof generator. - * - * This class is used for proofs of t = t', where t' is obtained from t by - * applying (context-free) small step rewrites on subterms of t. Its main - * interface functions are: - * (1) addRewriteStep(t,s,) which notifies this class that t - * rewrites to s, where justification is either a proof generator or proof - * step, - * (2) getProofFor(f) where f is any equality that can be justified by the - * rewrite steps given above. - * - * For example, say we make the following calls: - * addRewriteStep(a,b,P1) - * addRewriteStep(f(a),c,P2) - * addRewriteStep(c,d,P3) - * where P1 and P2 are proof steps. Afterwards, this class may justify any - * equality t = s where s is obtained by applying the rewrites a->b, f(a)->c, - * c->d, based on the strategy outlined below [***]. For example, the call to: - * getProofFor(g(f(a),h(a),f(e)) = g(d,h(b),f(e))) - * will return the proof: - * CONG( - * TRANS(P2,P3), ; f(a)=d - * CONG(P1 :args h), ; h(a)=h(b) - * REFL(:args f(e)) ; f(e)=f(e) - * :args g) - * - * [***] This class traverses the left hand side of a given equality-to-prove - * (the term g(f(a),h(a),e) in the above example) and "replays" the rewrite - * steps to obtain its rewritten form. To do so, it applies any available - * rewrite step at pre-rewrite (pre-order traversal) and post-rewrite - * (post-order traversal) based on whether the user specified pre-rewrite or a - * post-rewrite during addRewriteStep. - * - * This class may additionally be used for term-context-sensitive rewrite - * systems. An example is the term formula removal pass which rewrites - * terms dependending on whether they occur in a "term position", for details - * see RtfTermContext in expr/term_context.h. To use this class in a way - * that takes into account term contexts, the user of the term conversion - * proof generator should: - * (1) Provide a term context callback to the constructor of this class (tccb), - * (2) Register rewrite steps that indicate the term context identifier of - * the rewrite, which is a uint32_t. - * - * For example, RtfTermContext uses hash value 2 to indicate we are in a "term - * position". Say the user of this class calls: - * addRewriteStep( (and A B), BOOLEAN_TERM_VARIABLE_1, pg, true, 2) - * This indicates that (and A B) should rewrite to BOOLEAN_TERM_VARIABLE_1 if - * (and A B) occurs in a term position, where pg is a proof generator that can - * provide a closed proof of: - * (= (and A B) BOOLEAN_TERM_VARIABLE_1) - * Subsequently, this class may respond to a call to getProofFor on: - * (= - * (or (and A B) (P (and A B))) - * (or (and A B) (P BOOLEAN_TERM_VARIABLE_1))) - * where P is a predicate Bool -> Bool. The proof returned by this class - * involves congruence and pg's proof of the equivalence above. In particular, - * assuming its proof of the equivalence is P1, this proof is: - * (CONG{=} (CONG{or} (REFL (and A B)) (CONG{P} P1))) - * Notice the callback provided to this class ensures that the rewrite is - * replayed in the expected way, e.g. the occurrence of (and A B) that is not - * in term position is not rewritten. - */ -class TConvProofGenerator : public ProofGenerator -{ - public: - /** - * Constructor, which notice does fixpoint rewriting (since this is the - * most common use case) and never caches. - * - * @param pnm The proof node manager for constructing ProofNode objects. - * @param c The context that this class depends on. If none is provided, - * this class is context-independent. - * @param tpol The policy for applying rewrite steps of this class. For - * details, see d_policy. - * @param cpol The caching policy for this generator. - * @param name The name of this generator (for debugging). - * @param tccb The term context callback that this class depends on. If this - * is non-null, then this class stores a term-context-sensitive rewrite - * system. The rewrite steps should be given term context identifiers. - */ - TConvProofGenerator(ProofNodeManager* pnm, - context::Context* c = nullptr, - TConvPolicy pol = TConvPolicy::FIXPOINT, - TConvCachePolicy cpol = TConvCachePolicy::NEVER, - std::string name = "TConvProofGenerator", - TermContext* tccb = nullptr, - bool rewriteOps = false); - ~TConvProofGenerator(); - /** - * Add rewrite step t --> s based on proof generator. - * - * @param isPre Whether the rewrite is applied at prerewrite (pre-order - * traversal). - * @param trustId If a null proof generator is provided, we add a step to - * the proof that has trustId as the rule and expected as the sole argument. - * @param isClosed whether to expect that pg can provide a closed proof for - * this fact. - * @param tctx The term context identifier for the rewrite step. This - * value should correspond to one generated by the term context callback - * class provided in the argument tccb provided to the constructor of this - * class. - */ - void addRewriteStep(Node t, - Node s, - ProofGenerator* pg, - bool isPre = false, - PfRule trustId = PfRule::ASSUME, - bool isClosed = false, - uint32_t tctx = 0); - /** Same as above, for a single step */ - void addRewriteStep( - Node t, Node s, ProofStep ps, bool isPre = false, uint32_t tctx = 0); - /** Same as above, with explicit arguments */ - void addRewriteStep(Node t, - Node s, - PfRule id, - const std::vector& children, - const std::vector& args, - bool isPre = false, - uint32_t tctx = 0); - /** Has rewrite step for term t */ - bool hasRewriteStep(Node t, uint32_t tctx = 0, bool isPre = false) const; - /** - * Get rewrite step for term t, returns the s provided in a call to - * addRewriteStep if one exists, or null otherwise. - */ - Node getRewriteStep(Node t, uint32_t tctx = 0, bool isPre = false) const; - /** - * Get the proof for formula f. It should be the case that f is of the form - * t = t', where t' is the result of rewriting t based on the rewrite steps - * registered to this class. - * - * @param f The equality fact to get the proof for. - * @return The proof for f. - */ - std::shared_ptr getProofFor(Node f) override; - /** Identify this generator (for debugging, etc..) */ - std::string identify() const override; - /** - * Get the proof for how term n would rewrite. This is in contrast to the - * above method where the user provides an equality (= n n'). The motivation - * for this method is when it may be expensive to compute n', and hence it - * is preferred that the proof checker computes the rewritten form of - * n, instead of verifying that n has rewritten form n'. - */ - std::shared_ptr getProofForRewriting(Node n); - - protected: - typedef context::CDHashMap NodeNodeMap; - /** A dummy context used by this class if none is provided */ - context::Context d_context; - /** The (lazy) context dependent proof object. */ - LazyCDProof d_proof; - /** map to rewritten forms */ - NodeNodeMap d_preRewriteMap; - NodeNodeMap d_postRewriteMap; - /** - * Policy for how rewrites are applied to terms. As a simple example, say we - * have registered the rewrite steps: - * addRewriteStep( a, f(c), p1 ) - * addRewriteStep( c, d, p2 ) - * Then getProofForRewriting(f(a,c),pf) returns a proof of: - * f(a,c) = f(f(d),d) if d_policy is FIXPOINT, - * f(a,c) = f(f(c),d) if d_policy is ONCE. - */ - TConvPolicy d_policy; - /** The cache policy */ - TConvCachePolicy d_cpolicy; - /** Name identifier */ - std::string d_name; - /** The cache for terms */ - std::map > d_cache; - /** An (optional) term context object */ - TermContext* d_tcontext; - /** - * Whether we rewrite operators. If this flag is true, then the main - * traversal algorithm of this proof generator traverses operators of - * APPLY_UF and uses HO_CONG to justify rewriting of subterms when necessary. - */ - bool d_rewriteOps; - /** Get rewrite step for (hash value of) term. */ - Node getRewriteStepInternal(Node thash, bool isPre) const; - /** - * Adds a proof of t = t' to the proof pf where t' is the result of rewriting - * t based on the rewrite steps registered to this class. This method then - * returns the proved equality t = t'. - */ - Node getProofForRewriting(Node t, LazyCDProof& pf, TermContext* tc = nullptr); - /** - * Register rewrite step, returns the equality t=s if t is distinct from s - * and a rewrite step has not already been registered for t. - */ - Node registerRewriteStep(Node t, Node s, uint32_t tctx, bool isPre); - /** cache that r is the rewritten form of cur, pf can provide a proof */ - void doCache(Node curHash, Node cur, Node r, LazyCDProof& pf); - /** get debug information on this generator */ - std::string toStringDebug() const; -}; - -} // namespace cvc5 - -#endif /* CVC5__EXPR__TERM_CONVERSION_PROOF_GENERATOR_H */ diff --git a/src/preprocessing/assertion_pipeline.cpp b/src/preprocessing/assertion_pipeline.cpp index 2f3a49ac2..5542cfcf3 100644 --- a/src/preprocessing/assertion_pipeline.cpp +++ b/src/preprocessing/assertion_pipeline.cpp @@ -18,7 +18,7 @@ #include "expr/node_manager.h" #include "options/smt_options.h" -#include "expr/lazy_proof.h" +#include "proof/lazy_proof.h" #include "smt/preprocess_proof_generator.h" #include "theory/builtin/proof_checker.h" #include "theory/rewriter.h" diff --git a/src/preprocessing/assertion_pipeline.h b/src/preprocessing/assertion_pipeline.h index bb8e594d7..af88d5164 100644 --- a/src/preprocessing/assertion_pipeline.h +++ b/src/preprocessing/assertion_pipeline.h @@ -22,7 +22,7 @@ #include #include "expr/node.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/preprocessing/passes/non_clausal_simp.h b/src/preprocessing/passes/non_clausal_simp.h index 7f6106e3a..de16cc49a 100644 --- a/src/preprocessing/passes/non_clausal_simp.h +++ b/src/preprocessing/passes/non_clausal_simp.h @@ -21,7 +21,7 @@ #include "context/cdlist.h" #include "expr/node.h" #include "preprocessing/preprocessing_pass.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/preprocessing/passes/theory_rewrite_eq.h b/src/preprocessing/passes/theory_rewrite_eq.h index 729252dd6..2312c38ed 100644 --- a/src/preprocessing/passes/theory_rewrite_eq.h +++ b/src/preprocessing/passes/theory_rewrite_eq.h @@ -20,7 +20,7 @@ #include "expr/node.h" #include "preprocessing/preprocessing_pass.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { namespace preprocessing { diff --git a/src/proof/buffered_proof_generator.cpp b/src/proof/buffered_proof_generator.cpp new file mode 100644 index 000000000..d6f54fb34 --- /dev/null +++ b/src/proof/buffered_proof_generator.cpp @@ -0,0 +1,103 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of a proof generator for buffered proof steps. + */ + +#include "proof/buffered_proof_generator.h" + +#include "proof/proof.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { + +BufferedProofGenerator::BufferedProofGenerator(context::Context* c, + ProofNodeManager* pnm) + : ProofGenerator(), d_facts(c), d_pnm(pnm) +{ +} + +bool BufferedProofGenerator::addStep(Node fact, + ProofStep ps, + CDPOverwrite opolicy) +{ + // check duplicates if we are not always overwriting + if (opolicy != CDPOverwrite::ALWAYS) + { + if (d_facts.find(fact) != d_facts.end()) + { + // duplicate + return false; + } + Node symFact = CDProof::getSymmFact(fact); + if (!symFact.isNull()) + { + if (d_facts.find(symFact) != d_facts.end()) + { + // duplicate due to symmetry + return false; + } + } + } + // note that this replaces the value fact is mapped to if there is already one + d_facts.insert(fact, std::make_shared(ps)); + return true; +} + +std::shared_ptr BufferedProofGenerator::getProofFor(Node fact) +{ + Trace("pfee-fact-gen") << "BufferedProofGenerator::getProofFor: " << fact + << std::endl; + NodeProofStepMap::iterator it = d_facts.find(fact); + if (it == d_facts.end()) + { + Node symFact = CDProof::getSymmFact(fact); + if (symFact.isNull()) + { + Trace("pfee-fact-gen") << "...cannot find step" << std::endl; + Assert(false); + return nullptr; + } + it = d_facts.find(symFact); + if (it == d_facts.end()) + { + Assert(false); + Trace("pfee-fact-gen") << "...cannot find step (no sym)" << std::endl; + return nullptr; + } + } + Trace("pfee-fact-gen") << "...return via step " << *(*it).second << std::endl; + CDProof cdp(d_pnm); + cdp.addStep(fact, *(*it).second); + return cdp.getProofFor(fact); +} + +bool BufferedProofGenerator::hasProofFor(Node f) +{ + NodeProofStepMap::iterator it = d_facts.find(f); + if (it == d_facts.end()) + { + Node symFact = CDProof::getSymmFact(f); + if (symFact.isNull()) + { + return false; + } + it = d_facts.find(symFact); + if (it == d_facts.end()) + { + return false; + } + } + return true; +} + +} // namespace cvc5 diff --git a/src/proof/buffered_proof_generator.h b/src/proof/buffered_proof_generator.h new file mode 100644 index 000000000..9d13faff4 --- /dev/null +++ b/src/proof/buffered_proof_generator.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Top contributors (to current version): + * Haniel Barbosa, Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * A proof generator for buffered proof steps. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__BUFFERED_PROOF_GENERATOR_H +#define CVC5__PROOF__BUFFERED_PROOF_GENERATOR_H + +#include "context/cdhashmap.h" +#include "proof/proof_generator.h" + +namespace cvc5 { + +class ProofNodeManager; +class ProofStep; + +/** + * The proof generator for buffered steps. This class is a context-dependent + * mapping from formulas to proof steps. It does not generate ProofNodes until + * it is asked to provide a proof for a given fact. + */ +class BufferedProofGenerator : public ProofGenerator +{ + typedef context::CDHashMap> NodeProofStepMap; + + public: + BufferedProofGenerator(context::Context* c, ProofNodeManager* pnm); + ~BufferedProofGenerator() {} + /** add step + * Unless the overwrite policy is ALWAYS it does not replace previously + * registered steps (modulo (dis)equality symmetry). + */ + bool addStep(Node fact, + ProofStep ps, + CDPOverwrite opolicy = CDPOverwrite::NEVER); + /** Get proof for. It is robust to (dis)equality symmetry. */ + std::shared_ptr getProofFor(Node f) override; + /** Whether a step has been registered for f. */ + bool hasProofFor(Node f) override; + /** identify */ + std::string identify() const override { return "BufferedProofGenerator"; } + + private: + /** maps expected to ProofStep */ + NodeProofStepMap d_facts; + /** the proof node manager */ + ProofNodeManager* d_pnm; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__BUFFERED_PROOF_GENERATOR_H */ diff --git a/src/proof/conv_proof_generator.cpp b/src/proof/conv_proof_generator.cpp new file mode 100644 index 000000000..3635f3dea --- /dev/null +++ b/src/proof/conv_proof_generator.cpp @@ -0,0 +1,624 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of term conversion proof generator utility. + */ + +#include "proof/conv_proof_generator.h" + +#include + +#include "expr/term_context.h" +#include "expr/term_context_stack.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" +#include "proof/proof_node_algorithm.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +std::ostream& operator<<(std::ostream& out, TConvPolicy tcpol) +{ + switch (tcpol) + { + case TConvPolicy::FIXPOINT: out << "FIXPOINT"; break; + case TConvPolicy::ONCE: out << "ONCE"; break; + default: out << "TConvPolicy:unknown"; break; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, TConvCachePolicy tcpol) +{ + switch (tcpol) + { + case TConvCachePolicy::STATIC: out << "STATIC"; break; + case TConvCachePolicy::DYNAMIC: out << "DYNAMIC"; break; + case TConvCachePolicy::NEVER: out << "NEVER"; break; + default: out << "TConvCachePolicy:unknown"; break; + } + return out; +} + +TConvProofGenerator::TConvProofGenerator(ProofNodeManager* pnm, + context::Context* c, + TConvPolicy pol, + TConvCachePolicy cpol, + std::string name, + TermContext* tccb, + bool rewriteOps) + : d_proof(pnm, nullptr, c, name + "::LazyCDProof"), + d_preRewriteMap(c ? c : &d_context), + d_postRewriteMap(c ? c : &d_context), + d_policy(pol), + d_cpolicy(cpol), + d_name(name), + d_tcontext(tccb), + d_rewriteOps(rewriteOps) +{ +} + +TConvProofGenerator::~TConvProofGenerator() {} + +void TConvProofGenerator::addRewriteStep(Node t, + Node s, + ProofGenerator* pg, + bool isPre, + PfRule trustId, + bool isClosed, + uint32_t tctx) +{ + Node eq = registerRewriteStep(t, s, tctx, isPre); + if (!eq.isNull()) + { + d_proof.addLazyStep(eq, pg, trustId, isClosed); + } +} + +void TConvProofGenerator::addRewriteStep( + Node t, Node s, ProofStep ps, bool isPre, uint32_t tctx) +{ + Node eq = registerRewriteStep(t, s, tctx, isPre); + if (!eq.isNull()) + { + d_proof.addStep(eq, ps); + } +} + +void TConvProofGenerator::addRewriteStep(Node t, + Node s, + PfRule id, + const std::vector& children, + const std::vector& args, + bool isPre, + uint32_t tctx) +{ + Node eq = registerRewriteStep(t, s, tctx, isPre); + if (!eq.isNull()) + { + d_proof.addStep(eq, id, children, args); + } +} + +bool TConvProofGenerator::hasRewriteStep(Node t, + uint32_t tctx, + bool isPre) const +{ + return !getRewriteStep(t, tctx, isPre).isNull(); +} + +Node TConvProofGenerator::getRewriteStep(Node t, + uint32_t tctx, + bool isPre) const +{ + Node thash = t; + if (d_tcontext != nullptr) + { + thash = TCtxNode::computeNodeHash(t, tctx); + } + return getRewriteStepInternal(thash, isPre); +} + +Node TConvProofGenerator::registerRewriteStep(Node t, + Node s, + uint32_t tctx, + bool isPre) +{ + Assert(!t.isNull()); + Assert(!s.isNull()); + + if (t == s) + { + return Node::null(); + } + Node thash = t; + if (d_tcontext != nullptr) + { + thash = TCtxNode::computeNodeHash(t, tctx); + } + else + { + // don't use term context ids if not using term context + Assert(tctx == 0); + } + // should not rewrite term to two different things + if (!getRewriteStepInternal(thash, isPre).isNull()) + { + Assert(getRewriteStepInternal(thash, isPre) == s) + << identify() << " rewriting " << t << " to both " << s << " and " + << getRewriteStepInternal(thash, isPre); + return Node::null(); + } + NodeNodeMap& rm = isPre ? d_preRewriteMap : d_postRewriteMap; + rm[thash] = s; + if (d_cpolicy == TConvCachePolicy::DYNAMIC) + { + // clear the cache + d_cache.clear(); + } + return t.eqNode(s); +} + +std::shared_ptr TConvProofGenerator::getProofFor(Node f) +{ + Trace("tconv-pf-gen") << "TConvProofGenerator::getProofFor: " << identify() + << ": " << f << std::endl; + if (f.getKind() != EQUAL) + { + std::stringstream serr; + serr << "TConvProofGenerator::getProofFor: " << identify() + << ": fail, non-equality " << f; + Unhandled() << serr.str(); + Trace("tconv-pf-gen") << serr.str() << std::endl; + return nullptr; + } + // we use the existing proofs + LazyCDProof lpf( + d_proof.getManager(), &d_proof, nullptr, d_name + "::LazyCDProof"); + if (f[0] == f[1]) + { + // assertion failure in debug + Assert(false) << "TConvProofGenerator::getProofFor: " << identify() + << ": don't ask for trivial proofs"; + lpf.addStep(f, PfRule::REFL, {}, {f[0]}); + } + else + { + Node conc = getProofForRewriting(f[0], lpf, d_tcontext); + if (conc != f) + { + bool debugTraceEnabled = Trace.isOn("tconv-pf-gen-debug"); + Assert(conc.getKind() == EQUAL && conc[0] == f[0]); + std::stringstream serr; + serr << "TConvProofGenerator::getProofFor: " << toStringDebug() + << ": failed, mismatch"; + if (!debugTraceEnabled) + { + serr << " (see -t tconv-pf-gen-debug for details)"; + } + serr << std::endl; + serr << " source: " << f[0] << std::endl; + serr << " requested conclusion: " << f[1] << std::endl; + serr << "conclusion from generator: " << conc[1] << std::endl; + + if (debugTraceEnabled) + { + Trace("tconv-pf-gen-debug") << "Printing rewrite steps..." << std::endl; + for (size_t r = 0; r < 2; r++) + { + const NodeNodeMap& rm = r == 0 ? d_preRewriteMap : d_postRewriteMap; + serr << "Rewrite steps (" << (r == 0 ? "pre" : "post") + << "):" << std::endl; + for (NodeNodeMap::const_iterator it = rm.begin(); it != rm.end(); + ++it) + { + serr << (*it).first << " -> " << (*it).second << std::endl; + } + } + } + Unhandled() << serr.str(); + return nullptr; + } + } + std::shared_ptr pfn = lpf.getProofFor(f); + Trace("tconv-pf-gen") << "... success" << std::endl; + Assert(pfn != nullptr); + Trace("tconv-pf-gen-debug") << "... proof is " << *pfn << std::endl; + return pfn; +} + +std::shared_ptr TConvProofGenerator::getProofForRewriting(Node n) +{ + LazyCDProof lpf( + d_proof.getManager(), &d_proof, nullptr, d_name + "::LazyCDProofRew"); + Node conc = getProofForRewriting(n, lpf, d_tcontext); + if (conc[1] == n) + { + // assertion failure in debug + Assert(false) << "TConvProofGenerator::getProofForRewriting: " << identify() + << ": don't ask for trivial proofs"; + lpf.addStep(conc, PfRule::REFL, {}, {n}); + } + std::shared_ptr pfn = lpf.getProofFor(conc); + Assert(pfn != nullptr); + Trace("tconv-pf-gen-debug") << "... proof is " << *pfn << std::endl; + return pfn; +} + +Node TConvProofGenerator::getProofForRewriting(Node t, + LazyCDProof& pf, + TermContext* tctx) +{ + NodeManager* nm = NodeManager::currentNM(); + // Invariant: if visited[hash(t)] = s or rewritten[hash(t)] = s and t,s are + // distinct, then pf is able to generate a proof of t=s. We must + // Node in the domains of the maps below due to hashing creating new (SEXPR) + // nodes. + + // the final rewritten form of terms + std::unordered_map visited; + // the rewritten form of terms we have processed so far + std::unordered_map rewritten; + std::unordered_map::iterator it; + std::unordered_map::iterator itr; + std::map >::iterator itc; + Trace("tconv-pf-gen-rewrite") + << "TConvProofGenerator::getProofForRewriting: " << toStringDebug() + << std::endl; + Trace("tconv-pf-gen-rewrite") << "Input: " << t << std::endl; + // if provided, we use term context for cache + std::shared_ptr visitctx; + // otherwise, visit is used if we don't have a term context + std::vector visit; + Node tinitialHash; + if (tctx != nullptr) + { + visitctx = std::make_shared(tctx); + visitctx->pushInitial(t); + tinitialHash = TCtxNode::computeNodeHash(t, tctx->initialValue()); + } + else + { + visit.push_back(t); + tinitialHash = t; + } + Node cur; + uint32_t curCVal = 0; + Node curHash; + do + { + // pop the top element + if (tctx != nullptr) + { + std::pair curPair = visitctx->getCurrent(); + cur = curPair.first; + curCVal = curPair.second; + curHash = TCtxNode::computeNodeHash(cur, curCVal); + visitctx->pop(); + } + else + { + cur = visit.back(); + curHash = cur; + visit.pop_back(); + } + Trace("tconv-pf-gen-rewrite") << "* visit : " << curHash << std::endl; + // has the proof for cur been cached? + itc = d_cache.find(curHash); + if (itc != d_cache.end()) + { + Node res = itc->second->getResult(); + Assert(res.getKind() == EQUAL); + Assert(!res[1].isNull()); + visited[curHash] = res[1]; + pf.addProof(itc->second); + continue; + } + it = visited.find(curHash); + if (it == visited.end()) + { + Trace("tconv-pf-gen-rewrite") << "- previsit" << std::endl; + visited[curHash] = Node::null(); + // did we rewrite the current node (at pre-rewrite)? + Node rcur = getRewriteStepInternal(curHash, true); + if (!rcur.isNull()) + { + Trace("tconv-pf-gen-rewrite") + << "*** " << curHash << " prerewrites to " << rcur << std::endl; + // d_proof has a proof of cur = rcur. Hence there is nothing + // to do here, as pf will reference d_proof to get its proof. + if (d_policy == TConvPolicy::FIXPOINT) + { + // It may be the case that rcur also rewrites, thus we cannot assign + // the final rewritten form for cur yet. Instead we revisit cur after + // finishing visiting rcur. + rewritten[curHash] = rcur; + if (tctx != nullptr) + { + visitctx->push(cur, curCVal); + visitctx->push(rcur, curCVal); + } + else + { + visit.push_back(cur); + visit.push_back(rcur); + } + } + else + { + Assert(d_policy == TConvPolicy::ONCE); + Trace("tconv-pf-gen-rewrite") << "-> (once, prewrite) " << curHash + << " = " << rcur << std::endl; + // not rewriting again, rcur is final + Assert(!rcur.isNull()); + visited[curHash] = rcur; + doCache(curHash, cur, rcur, pf); + } + } + else if (tctx != nullptr) + { + visitctx->push(cur, curCVal); + // visit operator if apply uf + if (d_rewriteOps && cur.getKind() == APPLY_UF) + { + visitctx->pushOp(cur, curCVal); + } + visitctx->pushChildren(cur, curCVal); + } + else + { + visit.push_back(cur); + // visit operator if apply uf + if (d_rewriteOps && cur.getKind() == APPLY_UF) + { + visit.push_back(cur.getOperator()); + } + visit.insert(visit.end(), cur.begin(), cur.end()); + } + } + else if (it->second.isNull()) + { + itr = rewritten.find(curHash); + if (itr != rewritten.end()) + { + // only can generate partially rewritten nodes when rewrite again is + // true. + Assert(d_policy != TConvPolicy::ONCE); + // if it was rewritten, check the status of the rewritten node, + // which should be finished now + Node rcur = itr->second; + Trace("tconv-pf-gen-rewrite") + << "- postvisit, previously rewritten to " << rcur << std::endl; + Node rcurHash = rcur; + if (tctx != nullptr) + { + rcurHash = TCtxNode::computeNodeHash(rcur, curCVal); + } + Assert(cur != rcur); + // the final rewritten form of cur is the final form of rcur + Node rcurFinal = visited[rcurHash]; + Assert(!rcurFinal.isNull()); + if (rcurFinal != rcur) + { + // must connect via TRANS + std::vector pfChildren; + pfChildren.push_back(cur.eqNode(rcur)); + pfChildren.push_back(rcur.eqNode(rcurFinal)); + Node result = cur.eqNode(rcurFinal); + pf.addStep(result, PfRule::TRANS, pfChildren, {}); + } + Trace("tconv-pf-gen-rewrite") + << "-> (rewritten postrewrite) " << curHash << " = " << rcurFinal + << std::endl; + visited[curHash] = rcurFinal; + doCache(curHash, cur, rcurFinal, pf); + } + else + { + Trace("tconv-pf-gen-rewrite") << "- postvisit" << std::endl; + Node ret = cur; + Node retHash = curHash; + bool childChanged = false; + std::vector children; + Kind ck = cur.getKind(); + if (d_rewriteOps && ck == APPLY_UF) + { + // the operator of APPLY_UF is visited + Node cop = cur.getOperator(); + if (tctx != nullptr) + { + uint32_t coval = tctx->computeValueOp(cur, curCVal); + Node coHash = TCtxNode::computeNodeHash(cop, coval); + it = visited.find(coHash); + } + else + { + it = visited.find(cop); + } + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cop != it->second; + children.push_back(it->second); + } + else if (cur.getMetaKind() == metakind::PARAMETERIZED) + { + // all other parametrized operators are unchanged + children.push_back(cur.getOperator()); + } + // get the results of the children + if (tctx != nullptr) + { + for (size_t i = 0, nchild = cur.getNumChildren(); i < nchild; i++) + { + Node cn = cur[i]; + uint32_t cnval = tctx->computeValue(cur, curCVal, i); + Node cnHash = TCtxNode::computeNodeHash(cn, cnval); + it = visited.find(cnHash); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cn != it->second; + children.push_back(it->second); + } + } + else + { + // can use simple loop if not term-context-sensitive + for (const Node& cn : cur) + { + it = visited.find(cn); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cn != it->second; + children.push_back(it->second); + } + } + if (childChanged) + { + ret = nm->mkNode(ck, children); + rewritten[curHash] = ret; + // congruence to show (cur = ret) + PfRule congRule = PfRule::CONG; + std::vector pfChildren; + std::vector pfArgs; + pfArgs.push_back(ProofRuleChecker::mkKindNode(ck)); + if (ck == APPLY_UF && children[0] != cur.getOperator()) + { + // use HO_CONG if the operator changed + congRule = PfRule::HO_CONG; + pfChildren.push_back(cur.getOperator().eqNode(children[0])); + } + else if (kind::metaKindOf(ck) == kind::metakind::PARAMETERIZED) + { + pfArgs.push_back(cur.getOperator()); + } + for (size_t i = 0, size = cur.getNumChildren(); i < size; i++) + { + if (cur[i] == ret[i]) + { + // ensure REFL proof for unchanged children + pf.addStep(cur[i].eqNode(cur[i]), PfRule::REFL, {}, {cur[i]}); + } + pfChildren.push_back(cur[i].eqNode(ret[i])); + } + Node result = cur.eqNode(ret); + pf.addStep(result, congRule, pfChildren, pfArgs); + // must update the hash + retHash = ret; + if (tctx != nullptr) + { + retHash = TCtxNode::computeNodeHash(ret, curCVal); + } + } + else if (tctx != nullptr) + { + // now we need the hash + retHash = TCtxNode::computeNodeHash(cur, curCVal); + } + // did we rewrite ret (at post-rewrite)? + Node rret = getRewriteStepInternal(retHash, false); + if (!rret.isNull() && d_policy == TConvPolicy::FIXPOINT) + { + Trace("tconv-pf-gen-rewrite") + << "*** " << retHash << " postrewrites to " << rret << std::endl; + // d_proof should have a proof of ret = rret, hence nothing to do + // here, for the same reasons as above. It also may be the case that + // rret rewrites, hence we must revisit ret. + rewritten[retHash] = rret; + if (tctx != nullptr) + { + if (cur != ret) + { + visitctx->push(cur, curCVal); + } + visitctx->push(ret, curCVal); + visitctx->push(rret, curCVal); + } + else + { + if (cur != ret) + { + visit.push_back(cur); + } + visit.push_back(ret); + visit.push_back(rret); + } + } + else + { + // If we changed due to congruence, and then rewrote, then we + // require a trans step to connect here + if (!rret.isNull() && childChanged) + { + std::vector pfChildren; + pfChildren.push_back(cur.eqNode(ret)); + pfChildren.push_back(ret.eqNode(rret)); + Node result = cur.eqNode(rret); + pf.addStep(result, PfRule::TRANS, pfChildren, {}); + } + // take its rewrite if it rewrote and we have ONCE rewriting policy + ret = rret.isNull() ? ret : rret; + Trace("tconv-pf-gen-rewrite") + << "-> (postrewrite) " << curHash << " = " << ret << std::endl; + // it is final + Assert(!ret.isNull()); + visited[curHash] = ret; + doCache(curHash, cur, ret, pf); + } + } + } + else + { + Trace("tconv-pf-gen-rewrite") << "- already visited" << std::endl; + } + } while (!(tctx != nullptr ? visitctx->empty() : visit.empty())); + Assert(visited.find(tinitialHash) != visited.end()); + Assert(!visited.find(tinitialHash)->second.isNull()); + Trace("tconv-pf-gen-rewrite") + << "...finished, return " << visited[tinitialHash] << std::endl; + // return the conclusion of the overall proof + return t.eqNode(visited[tinitialHash]); +} + +void TConvProofGenerator::doCache(Node curHash, + Node cur, + Node r, + LazyCDProof& pf) +{ + if (d_cpolicy != TConvCachePolicy::NEVER) + { + Node eq = cur.eqNode(r); + d_cache[curHash] = pf.getProofFor(eq); + } +} + +Node TConvProofGenerator::getRewriteStepInternal(Node t, bool isPre) const +{ + const NodeNodeMap& rm = isPre ? d_preRewriteMap : d_postRewriteMap; + NodeNodeMap::const_iterator it = rm.find(t); + if (it == rm.end()) + { + return Node::null(); + } + return (*it).second; +} +std::string TConvProofGenerator::identify() const { return d_name; } + +std::string TConvProofGenerator::toStringDebug() const +{ + std::stringstream ss; + ss << identify() << " (policy=" << d_policy << ", cache policy=" << d_cpolicy + << (d_tcontext != nullptr ? ", term-context-sensitive" : "") << ")"; + return ss.str(); +} + +} // namespace cvc5 diff --git a/src/proof/conv_proof_generator.h b/src/proof/conv_proof_generator.h new file mode 100644 index 000000000..f23a661ae --- /dev/null +++ b/src/proof/conv_proof_generator.h @@ -0,0 +1,256 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Term conversion proof generator utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__CONV_PROOF_GENERATOR_H +#define CVC5__PROOF__CONV_PROOF_GENERATOR_H + +#include "context/cdhashmap.h" +#include "proof/lazy_proof.h" +#include "proof/proof_generator.h" + +namespace cvc5 { + +class ProofNodeManager; +class TermContext; + +/** A policy for how rewrite steps are applied in TConvProofGenerator */ +enum class TConvPolicy : uint32_t +{ + // steps are applied to fix-point, common use case is PfRule::REWRITE + FIXPOINT, + // steps are applied once at pre-rewrite, common use case is PfRule::SUBS + ONCE, +}; +/** Writes a term conversion policy name to a stream. */ +std::ostream& operator<<(std::ostream& out, TConvPolicy tcpol); + +/** A policy for how proofs are cached in TConvProofGenerator */ +enum class TConvCachePolicy : uint32_t +{ + // proofs are statically cached + STATIC, + // proofs are dynamically cached, cleared when a new rewrite is added + DYNAMIC, + // proofs are never cached + NEVER, +}; +/** Writes a term conversion cache policy name to a stream. */ +std::ostream& operator<<(std::ostream& out, TConvCachePolicy tcpol); + +/** + * The term conversion proof generator. + * + * This class is used for proofs of t = t', where t' is obtained from t by + * applying (context-free) small step rewrites on subterms of t. Its main + * interface functions are: + * (1) addRewriteStep(t,s,) which notifies this class that t + * rewrites to s, where justification is either a proof generator or proof + * step, + * (2) getProofFor(f) where f is any equality that can be justified by the + * rewrite steps given above. + * + * For example, say we make the following calls: + * addRewriteStep(a,b,P1) + * addRewriteStep(f(a),c,P2) + * addRewriteStep(c,d,P3) + * where P1 and P2 are proof steps. Afterwards, this class may justify any + * equality t = s where s is obtained by applying the rewrites a->b, f(a)->c, + * c->d, based on the strategy outlined below [***]. For example, the call to: + * getProofFor(g(f(a),h(a),f(e)) = g(d,h(b),f(e))) + * will return the proof: + * CONG( + * TRANS(P2,P3), ; f(a)=d + * CONG(P1 :args h), ; h(a)=h(b) + * REFL(:args f(e)) ; f(e)=f(e) + * :args g) + * + * [***] This class traverses the left hand side of a given equality-to-prove + * (the term g(f(a),h(a),e) in the above example) and "replays" the rewrite + * steps to obtain its rewritten form. To do so, it applies any available + * rewrite step at pre-rewrite (pre-order traversal) and post-rewrite + * (post-order traversal) based on whether the user specified pre-rewrite or a + * post-rewrite during addRewriteStep. + * + * This class may additionally be used for term-context-sensitive rewrite + * systems. An example is the term formula removal pass which rewrites + * terms dependending on whether they occur in a "term position", for details + * see RtfTermContext in expr/term_context.h. To use this class in a way + * that takes into account term contexts, the user of the term conversion + * proof generator should: + * (1) Provide a term context callback to the constructor of this class (tccb), + * (2) Register rewrite steps that indicate the term context identifier of + * the rewrite, which is a uint32_t. + * + * For example, RtfTermContext uses hash value 2 to indicate we are in a "term + * position". Say the user of this class calls: + * addRewriteStep( (and A B), BOOLEAN_TERM_VARIABLE_1, pg, true, 2) + * This indicates that (and A B) should rewrite to BOOLEAN_TERM_VARIABLE_1 if + * (and A B) occurs in a term position, where pg is a proof generator that can + * provide a closed proof of: + * (= (and A B) BOOLEAN_TERM_VARIABLE_1) + * Subsequently, this class may respond to a call to getProofFor on: + * (= + * (or (and A B) (P (and A B))) + * (or (and A B) (P BOOLEAN_TERM_VARIABLE_1))) + * where P is a predicate Bool -> Bool. The proof returned by this class + * involves congruence and pg's proof of the equivalence above. In particular, + * assuming its proof of the equivalence is P1, this proof is: + * (CONG{=} (CONG{or} (REFL (and A B)) (CONG{P} P1))) + * Notice the callback provided to this class ensures that the rewrite is + * replayed in the expected way, e.g. the occurrence of (and A B) that is not + * in term position is not rewritten. + */ +class TConvProofGenerator : public ProofGenerator +{ + public: + /** + * Constructor, which notice does fixpoint rewriting (since this is the + * most common use case) and never caches. + * + * @param pnm The proof node manager for constructing ProofNode objects. + * @param c The context that this class depends on. If none is provided, + * this class is context-independent. + * @param tpol The policy for applying rewrite steps of this class. For + * details, see d_policy. + * @param cpol The caching policy for this generator. + * @param name The name of this generator (for debugging). + * @param tccb The term context callback that this class depends on. If this + * is non-null, then this class stores a term-context-sensitive rewrite + * system. The rewrite steps should be given term context identifiers. + */ + TConvProofGenerator(ProofNodeManager* pnm, + context::Context* c = nullptr, + TConvPolicy pol = TConvPolicy::FIXPOINT, + TConvCachePolicy cpol = TConvCachePolicy::NEVER, + std::string name = "TConvProofGenerator", + TermContext* tccb = nullptr, + bool rewriteOps = false); + ~TConvProofGenerator(); + /** + * Add rewrite step t --> s based on proof generator. + * + * @param isPre Whether the rewrite is applied at prerewrite (pre-order + * traversal). + * @param trustId If a null proof generator is provided, we add a step to + * the proof that has trustId as the rule and expected as the sole argument. + * @param isClosed whether to expect that pg can provide a closed proof for + * this fact. + * @param tctx The term context identifier for the rewrite step. This + * value should correspond to one generated by the term context callback + * class provided in the argument tccb provided to the constructor of this + * class. + */ + void addRewriteStep(Node t, + Node s, + ProofGenerator* pg, + bool isPre = false, + PfRule trustId = PfRule::ASSUME, + bool isClosed = false, + uint32_t tctx = 0); + /** Same as above, for a single step */ + void addRewriteStep( + Node t, Node s, ProofStep ps, bool isPre = false, uint32_t tctx = 0); + /** Same as above, with explicit arguments */ + void addRewriteStep(Node t, + Node s, + PfRule id, + const std::vector& children, + const std::vector& args, + bool isPre = false, + uint32_t tctx = 0); + /** Has rewrite step for term t */ + bool hasRewriteStep(Node t, uint32_t tctx = 0, bool isPre = false) const; + /** + * Get rewrite step for term t, returns the s provided in a call to + * addRewriteStep if one exists, or null otherwise. + */ + Node getRewriteStep(Node t, uint32_t tctx = 0, bool isPre = false) const; + /** + * Get the proof for formula f. It should be the case that f is of the form + * t = t', where t' is the result of rewriting t based on the rewrite steps + * registered to this class. + * + * @param f The equality fact to get the proof for. + * @return The proof for f. + */ + std::shared_ptr getProofFor(Node f) override; + /** Identify this generator (for debugging, etc..) */ + std::string identify() const override; + /** + * Get the proof for how term n would rewrite. This is in contrast to the + * above method where the user provides an equality (= n n'). The motivation + * for this method is when it may be expensive to compute n', and hence it + * is preferred that the proof checker computes the rewritten form of + * n, instead of verifying that n has rewritten form n'. + */ + std::shared_ptr getProofForRewriting(Node n); + + protected: + typedef context::CDHashMap NodeNodeMap; + /** A dummy context used by this class if none is provided */ + context::Context d_context; + /** The (lazy) context dependent proof object. */ + LazyCDProof d_proof; + /** map to rewritten forms */ + NodeNodeMap d_preRewriteMap; + NodeNodeMap d_postRewriteMap; + /** + * Policy for how rewrites are applied to terms. As a simple example, say we + * have registered the rewrite steps: + * addRewriteStep( a, f(c), p1 ) + * addRewriteStep( c, d, p2 ) + * Then getProofForRewriting(f(a,c),pf) returns a proof of: + * f(a,c) = f(f(d),d) if d_policy is FIXPOINT, + * f(a,c) = f(f(c),d) if d_policy is ONCE. + */ + TConvPolicy d_policy; + /** The cache policy */ + TConvCachePolicy d_cpolicy; + /** Name identifier */ + std::string d_name; + /** The cache for terms */ + std::map > d_cache; + /** An (optional) term context object */ + TermContext* d_tcontext; + /** + * Whether we rewrite operators. If this flag is true, then the main + * traversal algorithm of this proof generator traverses operators of + * APPLY_UF and uses HO_CONG to justify rewriting of subterms when necessary. + */ + bool d_rewriteOps; + /** Get rewrite step for (hash value of) term. */ + Node getRewriteStepInternal(Node thash, bool isPre) const; + /** + * Adds a proof of t = t' to the proof pf where t' is the result of rewriting + * t based on the rewrite steps registered to this class. This method then + * returns the proved equality t = t'. + */ + Node getProofForRewriting(Node t, LazyCDProof& pf, TermContext* tc = nullptr); + /** + * Register rewrite step, returns the equality t=s if t is distinct from s + * and a rewrite step has not already been registered for t. + */ + Node registerRewriteStep(Node t, Node s, uint32_t tctx, bool isPre); + /** cache that r is the rewritten form of cur, pf can provide a proof */ + void doCache(Node curHash, Node cur, Node r, LazyCDProof& pf); + /** get debug information on this generator */ + std::string toStringDebug() const; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__CONV_PROOF_GENERATOR_H */ diff --git a/src/proof/conv_seq_proof_generator.cpp b/src/proof/conv_seq_proof_generator.cpp new file mode 100644 index 000000000..65a7e462b --- /dev/null +++ b/src/proof/conv_seq_proof_generator.cpp @@ -0,0 +1,172 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Term conversion sequence proof generator utility. + */ + +#include "proof/conv_seq_proof_generator.h" + +#include + +#include "proof/proof_node_manager.h" + +namespace cvc5 { + +TConvSeqProofGenerator::TConvSeqProofGenerator( + ProofNodeManager* pnm, + const std::vector& ts, + context::Context* c, + std::string name) + : d_pnm(pnm), d_converted(c), d_name(name) +{ + d_tconvs.insert(d_tconvs.end(), ts.begin(), ts.end()); + AlwaysAssert(!d_tconvs.empty()) + << "TConvSeqProofGenerator::TConvSeqProofGenerator: expecting non-empty " + "sequence"; +} + +TConvSeqProofGenerator::~TConvSeqProofGenerator() {} + +void TConvSeqProofGenerator::registerConvertedTerm(Node t, Node s, size_t index) +{ + if (t == s) + { + // no need + return; + } + std::pair key = std::pair(t, index); + d_converted[key] = s; +} + +std::shared_ptr TConvSeqProofGenerator::getProofFor(Node f) +{ + Trace("tconv-seq-pf-gen") + << "TConvSeqProofGenerator::getProofFor: " << identify() << ": " << f + << std::endl; + return getSubsequenceProofFor(f, 0, d_tconvs.size() - 1); +} + +std::shared_ptr TConvSeqProofGenerator::getSubsequenceProofFor( + Node f, size_t start, size_t end) +{ + Assert(end < d_tconvs.size()); + if (f.getKind() != kind::EQUAL) + { + std::stringstream serr; + serr << "TConvSeqProofGenerator::getProofFor: " << identify() + << ": fail, non-equality " << f; + Unhandled() << serr.str(); + Trace("tconv-seq-pf-gen") << serr.str() << std::endl; + return nullptr; + } + // start with the left hand side of the equality + Node curr = f[0]; + // proofs forming transitivity chain + std::vector> transChildren; + std::pair currKey; + NodeIndexNodeMap::iterator itc; + // convert the term in sequence + for (size_t i = start; i <= end; i++) + { + currKey = std::pair(curr, i); + itc = d_converted.find(currKey); + // if we provided a conversion at this index via registerConvertedTerm + if (itc != d_converted.end()) + { + Node next = (*itc).second; + Trace("tconv-seq-pf-gen") << "...convert to " << next << std::endl; + Node eq = curr.eqNode(next); + std::shared_ptr pf = d_tconvs[i]->getProofFor(eq); + transChildren.push_back(pf); + curr = next; + } + } + // should end up with the right hand side of the equality + if (curr != f[1]) + { + // unexpected + std::stringstream serr; + serr << "TConvSeqProofGenerator::getProofFor: " << identify() + << ": failed, mismatch (see -t tconv-seq-pf-gen-debug for details)" + << std::endl; + serr << " source: " << f[0] << std::endl; + serr << "expected after conversions: " << f[1] << std::endl; + serr << " actual after conversions: " << curr << std::endl; + + if (Trace.isOn("tconv-seq-pf-gen-debug")) + { + Trace("tconv-pf-gen-debug") + << "Printing conversion steps..." << std::endl; + serr << "Conversions: " << std::endl; + for (NodeIndexNodeMap::const_iterator it = d_converted.begin(); + it != d_converted.end(); + ++it) + { + serr << "(" << (*it).first.first << ", " << (*it).first.second + << ") -> " << (*it).second << std::endl; + } + } + Unhandled() << serr.str(); + return nullptr; + } + // otherwise, make transitivity + return d_pnm->mkTrans(transChildren, f); +} + +theory::TrustNode TConvSeqProofGenerator::mkTrustRewriteSequence( + const std::vector& cterms) +{ + Assert(cterms.size() == d_tconvs.size() + 1); + if (cterms[0] == cterms[cterms.size() - 1]) + { + return theory::TrustNode::null(); + } + bool useThis = false; + ProofGenerator* pg = nullptr; + for (size_t i = 0, nconvs = d_tconvs.size(); i < nconvs; i++) + { + if (cterms[i] == cterms[i + 1]) + { + continue; + } + else if (pg == nullptr) + { + // Maybe the i^th generator can explain it alone, which must be the case + // if there is only one position in the sequence where the term changes. + // We may overwrite pg with this class if another step is encountered in + // this loop. + pg = d_tconvs[i]; + } + else + { + // need more than a single generator, use this class + useThis = true; + break; + } + } + if (useThis) + { + pg = this; + // if more than two steps, we must register each conversion step + for (size_t i = 0, nconvs = d_tconvs.size(); i < nconvs; i++) + { + registerConvertedTerm(cterms[i], cterms[i + 1], i); + } + } + Assert(pg != nullptr); + return theory::TrustNode::mkTrustRewrite( + cterms[0], cterms[cterms.size() - 1], pg); +} + +std::string TConvSeqProofGenerator::identify() const { return d_name; } + +} // namespace cvc5 diff --git a/src/proof/conv_seq_proof_generator.h b/src/proof/conv_seq_proof_generator.h new file mode 100644 index 000000000..8d4417134 --- /dev/null +++ b/src/proof/conv_seq_proof_generator.h @@ -0,0 +1,121 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Term conversion sequence proof generator utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__CONV_SEQ_PROOF_GENERATOR_H +#define CVC5__PROOF__CONV_SEQ_PROOF_GENERATOR_H + +#include "context/cdhashmap.h" +#include "expr/node.h" +#include "proof/proof_generator.h" +#include "proof/trust_node.h" + +namespace cvc5 { + +class ProofNodeManager; + +/** + * The term conversion sequence proof generator. This is used for maintaining + * a fixed sequence of proof generators that provide proofs for rewrites + * (equalities). We call these the "component generators" of this sequence, + * which are typically TConvProofGenerator. + */ +class TConvSeqProofGenerator : public ProofGenerator +{ + public: + /** + * @param pnm The proof node manager for constructing ProofNode objects. + * @param ts The list of component term conversion generators that are + * applied in sequence + * @param c The context that this class depends on. If none is provided, + * this class is context-independent. + * @param name The name of this generator (for debugging). + */ + TConvSeqProofGenerator(ProofNodeManager* pnm, + const std::vector& ts, + context::Context* c = nullptr, + std::string name = "TConvSeqProofGenerator"); + ~TConvSeqProofGenerator(); + /** + * Indicate that the index^th proof generator converts term t to s. This + * should be called for a unique s for each (t, index). It must be the + * case that d_tconv[index] can provide a proof for t = s in the remainder + * of the context maintained by this class. + */ + void registerConvertedTerm(Node t, Node s, size_t index); + /** + * Get the proof for formula f. It should be the case that f is of the form + * t_0 = t_n, where it must be the case that t_n is obtained by the following: + * For each i=0, ... n, let t_{i+1} be the term such that + * registerConvertedTerm(t_i, t_{i+1}, i) + * was called. Otherwise t_{i+1} = t_i. + * In other words, t_n is obtained by converting t_0, in order, based on the + * calls to registerConvertedTerm. + * + * @param f The equality fact to get the proof for. + * @return The proof for f. + */ + std::shared_ptr getProofFor(Node f) override; + /** + * Get subsequence proof for f, with given start and end steps (inclusive). + */ + std::shared_ptr getSubsequenceProofFor(Node f, + size_t start, + size_t end); + /** Identify this generator (for debugging, etc..) */ + std::string identify() const override; + + /** + * Make trust node from a sequence of converted terms. The number of + * terms in cterms should be 1 + the number of component proof generators + * maintained by this class. This selects a proof generator that is capable + * of proving cterms[0] = cterms[cterms.size()-1], which is either this + * generator, or one of the component proof generators, if only one step + * rewrote. In the former case, all steps are registered to this class. + * Using a component generator is an optimization that saves having to + * save the conversion steps or use this class. For example, if we have 2 + * term conversion components, and call this method on: + * { a, b, c } + * then this method calls: + * registerConvertedTerm( a, b, 0 ) + * registerConvertedTerm( b, c, 1 ) + * and returns a trust node proving (= a c) with this class as the proof + * generator. On the other hand, if we call this method on: + * { d, d, e } + * then we return a trust node proving (= d e) with the 2nd component proof + * generator, as it alone is capable of proving this equality. + */ + theory::TrustNode mkTrustRewriteSequence(const std::vector& cterms); + + protected: + using NodeIndexPairHashFunction = + PairHashFunction>; + typedef context:: + CDHashMap, Node, NodeIndexPairHashFunction> + NodeIndexNodeMap; + /** The proof node manager */ + ProofNodeManager* d_pnm; + /** The term conversion generators */ + std::vector d_tconvs; + /** the set of converted terms */ + NodeIndexNodeMap d_converted; + /** Name identifier */ + std::string d_name; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__CONV_SEQ_PROOF_GENERATOR_H */ diff --git a/src/proof/dot/dot_printer.cpp b/src/proof/dot/dot_printer.cpp index 4ba409d6d..ca85aadd3 100644 --- a/src/proof/dot/dot_printer.cpp +++ b/src/proof/dot/dot_printer.cpp @@ -17,9 +17,9 @@ #include -#include "expr/proof_checker.h" -#include "expr/proof_node_manager.h" #include "printer/smt2/smt2_printer.h" +#include "proof/proof_checker.h" +#include "proof/proof_node_manager.h" #include "theory/builtin/proof_checker.h" namespace cvc5 { diff --git a/src/proof/dot/dot_printer.h b/src/proof/dot/dot_printer.h index 6e6785080..0027d145a 100644 --- a/src/proof/dot/dot_printer.h +++ b/src/proof/dot/dot_printer.h @@ -20,7 +20,7 @@ #include -#include "expr/proof_node.h" +#include "proof/proof_node.h" namespace cvc5 { namespace proof { diff --git a/src/proof/eager_proof_generator.cpp b/src/proof/eager_proof_generator.cpp new file mode 100644 index 000000000..34ff4fa75 --- /dev/null +++ b/src/proof/eager_proof_generator.cpp @@ -0,0 +1,159 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Alex Ozdemir + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of the abstract proof generator class. + */ + +#include "proof/eager_proof_generator.h" + +#include "proof/proof.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { +namespace theory { + +EagerProofGenerator::EagerProofGenerator(ProofNodeManager* pnm, + context::Context* c, + std::string name) + : d_pnm(pnm), d_name(name), d_proofs(c == nullptr ? &d_context : c) +{ +} + +void EagerProofGenerator::setProofFor(Node f, std::shared_ptr pf) +{ + // pf should prove f + Assert(pf->getResult() == f) + << "EagerProofGenerator::setProofFor: unexpected result" << std::endl + << "Expected: " << f << std::endl + << "Actual: " << pf->getResult() << std::endl; + d_proofs[f] = pf; +} +void EagerProofGenerator::setProofForConflict(Node conf, + std::shared_ptr pf) +{ + // Normalize based on key + Node ckey = TrustNode::getConflictProven(conf); + setProofFor(ckey, pf); +} + +void EagerProofGenerator::setProofForLemma(Node lem, + std::shared_ptr pf) +{ + // Normalize based on key + Node lkey = TrustNode::getLemmaProven(lem); + setProofFor(lkey, pf); +} + +void EagerProofGenerator::setProofForPropExp(TNode lit, + Node exp, + std::shared_ptr pf) +{ + // Normalize based on key + Node pekey = TrustNode::getPropExpProven(lit, exp); + setProofFor(pekey, pf); +} + +std::shared_ptr EagerProofGenerator::getProofFor(Node f) +{ + NodeProofNodeMap::iterator it = d_proofs.find(f); + if (it == d_proofs.end()) + { + return nullptr; + } + return (*it).second; +} + +bool EagerProofGenerator::hasProofFor(Node f) +{ + return d_proofs.find(f) != d_proofs.end(); +} + +TrustNode EagerProofGenerator::mkTrustNode(Node n, + std::shared_ptr pf, + bool isConflict) +{ + if (pf == nullptr) + { + return TrustNode::null(); + } + if (isConflict) + { + // this shouldnt modify the key + setProofForConflict(n, pf); + // we can now return the trust node + return TrustNode::mkTrustConflict(n, this); + } + // this shouldnt modify the key + setProofForLemma(n, pf); + // we can now return the trust node + return TrustNode::mkTrustLemma(n, this); +} + +TrustNode EagerProofGenerator::mkTrustNode(Node conc, + PfRule id, + const std::vector& exp, + const std::vector& args, + bool isConflict) +{ + // if no children, its easy + if (exp.empty()) + { + std::shared_ptr pf = d_pnm->mkNode(id, {}, args, conc); + return mkTrustNode(conc, pf, isConflict); + } + // otherwise, we use CDProof + SCOPE + CDProof cdp(d_pnm); + cdp.addStep(conc, id, exp, args); + std::shared_ptr pf = cdp.getProofFor(conc); + // We use mkNode instead of mkScope, since there is no reason to check + // whether the free assumptions of pf are in exp, since they are by the + // construction above. + std::shared_ptr pfs = d_pnm->mkNode(PfRule::SCOPE, {pf}, exp); + return mkTrustNode(pfs->getResult(), pfs, isConflict); +} + +TrustNode EagerProofGenerator::mkTrustedRewrite(Node a, + Node b, + std::shared_ptr pf) +{ + if (pf == nullptr) + { + return TrustNode::null(); + } + Node eq = a.eqNode(b); + setProofFor(eq, pf); + return TrustNode::mkTrustRewrite(a, b, this); +} + +TrustNode EagerProofGenerator::mkTrustedPropagation( + Node n, Node exp, std::shared_ptr pf) +{ + if (pf == nullptr) + { + return TrustNode::null(); + } + setProofForPropExp(n, exp, pf); + return TrustNode::mkTrustPropExp(n, exp, this); +} + +TrustNode EagerProofGenerator::mkTrustNodeSplit(Node f) +{ + // make the lemma + Node lem = f.orNode(f.notNode()); + return mkTrustNode(lem, PfRule::SPLIT, {}, {f}, false); +} + +std::string EagerProofGenerator::identify() const { return d_name; } + +} // namespace theory +} // namespace cvc5 diff --git a/src/proof/eager_proof_generator.h b/src/proof/eager_proof_generator.h new file mode 100644 index 000000000..ada48d893 --- /dev/null +++ b/src/proof/eager_proof_generator.h @@ -0,0 +1,197 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Alex Ozdemir, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * The eager proof generator class. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__EAGER_PROOF_GENERATOR_H +#define CVC5__PROOF__EAGER_PROOF_GENERATOR_H + +#include "context/cdhashmap.h" +#include "expr/node.h" +#include "proof/proof_generator.h" +#include "proof/proof_rule.h" +#include "proof/trust_node.h" + +namespace cvc5 { + +class ProofNode; +class ProofNodeManager; + +namespace theory { + +/** + * An eager proof generator, with explicit proof caching. + * + * The intended use of this class is to store proofs for lemmas and conflicts + * at the time they are sent out on the ProofOutputChannel. This means that the + * getProofForConflict and getProofForLemma methods are lookups in a + * (user-context depedent) map, the field d_proofs below. + * + * In detail, the method setProofForConflict(conf, pf) should be called prior to + * calling ProofOutputChannel(TrustNode(conf,X)), where X is this generator. + * Similarly for setProofForLemma. + * + * The intended usage of this class in combination with OutputChannel is + * the following: + * //----------------------------------------------------------- + * class MyEagerProofGenerator : public EagerProofGenerator + * { + * public: + * TrustNode getProvenConflictByMethodX(...) + * { + * // construct a conflict + * Node conf = [construct conflict]; + * // construct a proof for conf + * std::shared_ptr pf = [construct the proof for conf]; + * // wrap the conflict in a trust node + * return mkTrustNode(conf,pf); + * } + * }; + * // [1] Make objects given user context u and output channel out. + * + * MyEagerProofGenerator epg(u); + * OutputChannel out; + * + * // [2] Assume epg realizes there is a conflict. We have it store the proof + * // internally and return the conflict node paired with epg. + * + * TrustNode pconf = epg.getProvenConflictByMethodX(...); + * + * // [3] Send the conflict on the output channel. + * + * out.trustedConflict(pconf); + * + * // [4] The trust node has information about what is proven and who can + * // prove it, where this association is valid in the remainder of the user + * // context. + * + * Node conf = pconf.getProven(); + * ProofGenerator * pg = pconf.getGenerator(); + * std::shared_ptr pf = pg->getProofForConflict(conf); + * //----------------------------------------------------------- + * In other words, the proof generator epg is responsible for creating and + * storing the proof internally, and the proof output channel is responsible for + * maintaining the map that epg is who to ask for the proof of the conflict. + */ +class EagerProofGenerator : public ProofGenerator +{ + typedef context::CDHashMap> NodeProofNodeMap; + + public: + EagerProofGenerator(ProofNodeManager* pnm, + context::Context* c = nullptr, + std::string name = "EagerProofGenerator"); + ~EagerProofGenerator() {} + /** Get the proof for formula f. */ + std::shared_ptr getProofFor(Node f) override; + /** Can we give the proof for formula f? */ + bool hasProofFor(Node f) override; + /** + * Set proof for fact f, called when pf is a proof of f. + * + * @param f The fact proven by pf, + * @param pf The proof to store in this class. + */ + void setProofFor(Node f, std::shared_ptr pf); + /** + * Make trust node: wrap n in a trust node with this generator, and have it + * store the proof pf to lemma or conflict n. + * + * @param n The proven node, + * @param pf The proof of n, + * @param isConflict Whether the returned trust node is a conflict (otherwise + * it is a lemma), + * @return The trust node corresponding to the fact that this generator has + * a proof of n. + */ + TrustNode mkTrustNode(Node n, + std::shared_ptr pf, + bool isConflict = false); + /** + * Make trust node from a single step proof. This is a convenience function + * that avoids the need to explictly construct ProofNode by the caller. + * + * @param conc The conclusion of the rule, + * @param id The rule of the proof concluding conc + * @param exp The explanation (premises) to the proof concluding conc, + * @param args The arguments to the proof concluding conc, + * @param isConflict Whether the returned trust node is a conflict (otherwise + * it is a lemma), + * @return The trust node corresponding to the fact that this generator has + * a proof of (exp => conc), or of conc if exp is empty. + */ + TrustNode mkTrustNode(Node conc, + PfRule id, + const std::vector& exp, + const std::vector& args, + bool isConflict = false); + /** + * Make trust node: wrap `exp => n` in a trust node with this generator, and + * have it store the proof `pf` too. + * + * @param n The implication + * @param exp A conjunction of literals that imply it + * @param pf The proof of exp => n, + * @return The trust node corresponding to the fact that this generator has + * a proof of exp => n. + */ + TrustNode mkTrustedPropagation(Node n, + Node exp, + std::shared_ptr pf); + /** + * Make trust node: `a = b` as a Rewrite trust node + * + * @param a the original + * @param b what is rewrites to + * @param pf The proof of a = b, + * @return The trust node corresponding to the fact that this generator has + * a proof of a = b + */ + TrustNode mkTrustedRewrite(Node a, Node b, std::shared_ptr pf); + //--------------------------------------- common proofs + /** + * This returns the trust node corresponding to the splitting lemma + * (or f (not f)) and this generator. The method registers its proof in the + * map maintained by this class. + */ + TrustNode mkTrustNodeSplit(Node f); + //--------------------------------------- end common proofs + /** identify */ + std::string identify() const override; + + protected: + /** Set that pf is the proof for conflict conf */ + void setProofForConflict(Node conf, std::shared_ptr pf); + /** Set that pf is the proof for lemma lem */ + void setProofForLemma(Node lem, std::shared_ptr pf); + /** Set that pf is the proof for explained propagation */ + void setProofForPropExp(TNode lit, Node exp, std::shared_ptr pf); + /** The proof node manager */ + ProofNodeManager* d_pnm; + /** Name identifier */ + std::string d_name; + /** A dummy context used by this class if none is provided */ + context::Context d_context; + /** + * A user-context-dependent map from lemmas and conflicts to proofs provided + * by calls to setProofForConflict and setProofForLemma above. + */ + NodeProofNodeMap d_proofs; +}; + +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_GENERATOR_H */ diff --git a/src/proof/lazy_proof.cpp b/src/proof/lazy_proof.cpp new file mode 100644 index 000000000..d7b62a8dc --- /dev/null +++ b/src/proof/lazy_proof.cpp @@ -0,0 +1,231 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of lazy proof utility. + */ + +#include "proof/lazy_proof.h" + +#include "proof/proof_ensure_closed.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +LazyCDProof::LazyCDProof(ProofNodeManager* pnm, + ProofGenerator* dpg, + context::Context* c, + std::string name) + : CDProof(pnm, c, name), d_gens(c ? c : &d_context), d_defaultGen(dpg) +{ +} + +LazyCDProof::~LazyCDProof() {} + +std::shared_ptr LazyCDProof::getProofFor(Node fact) +{ + Trace("lazy-cdproof") << "LazyCDProof::mkLazyProof " << fact << std::endl; + // make the proof, which should always be non-null, since we construct an + // assumption in the worst case. + std::shared_ptr opf = CDProof::getProofFor(fact); + Assert(opf != nullptr); + if (!hasGenerators()) + { + Trace("lazy-cdproof") << "...no generators, finished" << std::endl; + // optimization: no generators, we are done + return opf; + } + // otherwise, we traverse the proof opf and fill in the ASSUME leafs that + // have generators + std::unordered_set visited; + std::unordered_set::iterator it; + std::vector visit; + ProofNode* cur; + visit.push_back(opf.get()); + do + { + cur = visit.back(); + visit.pop_back(); + it = visited.find(cur); + + if (it == visited.end()) + { + visited.insert(cur); + Node cfact = cur->getResult(); + if (getProof(cfact).get() != cur) + { + // We don't own this proof, skip it. This is to ensure that this method + // is idempotent, since it may be the case that a previous call to + // getProofFor connected a proof from a proof generator as a child of + // a ProofNode in the range of the map in CDProof. Thus, this ensures + // we don't touch such proofs. + Trace("lazy-cdproof") << "...skip unowned proof" << std::endl; + } + else if (cur->getRule() == PfRule::ASSUME) + { + bool isSym = false; + ProofGenerator* pg = getGeneratorFor(cfact, isSym); + if (pg != nullptr) + { + Trace("lazy-cdproof") + << "LazyCDProof: Call generator " << pg->identify() + << " for assumption " << cfact << std::endl; + Node cfactGen = isSym ? CDProof::getSymmFact(cfact) : cfact; + Assert(!cfactGen.isNull()); + // Do not use the addProofTo interface, instead use the update node + // interface, since this ensures that we don't take ownership for + // the current proof. Instead, it is only linked, and ignored on + // future calls to getProofFor due to the check above. + std::shared_ptr pgc = pg->getProofFor(cfactGen); + // If the proof was null, then the update is not performed. This is + // not considered an error, since this behavior is equivalent to + // if pg had provided the proof (ASSUME cfactGen). Ensuring the + // proper behavior wrt closed proofs should be done outside this + // method. + if (pgc != nullptr) + { + Trace("lazy-cdproof-gen") + << "LazyCDProof: stored proof: " << *pgc.get() << std::endl; + + if (isSym) + { + d_manager->updateNode(cur, PfRule::SYMM, {pgc}, {}); + } + else + { + d_manager->updateNode(cur, pgc.get()); + } + Trace("lazy-cdproof") << "LazyCDProof: Successfully added fact for " + << cfactGen << std::endl; + } + } + else + { + Trace("lazy-cdproof") << "LazyCDProof: " << identify() + << " : No generator for " << cfact << std::endl; + } + // Notice that we do not traverse the proofs that have been generated + // lazily by the proof generators here. In other words, we assume that + // the proofs from provided proof generators are final and need + // no further modification by this class. + } + else + { + const std::vector>& cc = cur->getChildren(); + for (const std::shared_ptr& cp : cc) + { + visit.push_back(cp.get()); + } + } + } + } while (!visit.empty()); + // we have now updated the ASSUME leafs of opf, return it + Trace("lazy-cdproof") << "...finished" << std::endl; + Assert(opf->getResult() == fact); + return opf; +} + +void LazyCDProof::addLazyStep(Node expected, + ProofGenerator* pg, + PfRule idNull, + bool isClosed, + const char* ctx, + bool forceOverwrite) +{ + if (pg == nullptr) + { + // null generator, should have given a proof rule + if (idNull == PfRule::ASSUME) + { + Unreachable() << "LazyCDProof::addLazyStep: " << identify() + << ": failed to provide proof generator for " << expected; + return; + } + Trace("lazy-cdproof") << "LazyCDProof::addLazyStep: " << expected + << " set (trusted) step " << idNull << "\n"; + addStep(expected, idNull, {}, {expected}); + return; + } + Trace("lazy-cdproof") << "LazyCDProof::addLazyStep: " << expected + << " set to generator " << pg->identify() << "\n"; + if (!forceOverwrite) + { + NodeProofGeneratorMap::const_iterator it = d_gens.find(expected); + if (it != d_gens.end()) + { + // don't overwrite something that is already there + return; + } + } + // just store now + d_gens.insert(expected, pg); + // debug checking + if (isClosed) + { + Trace("lazy-cdproof-debug") << "Checking closed..." << std::endl; + pfgEnsureClosed(expected, pg, "lazy-cdproof-debug", ctx); + } +} + +ProofGenerator* LazyCDProof::getGeneratorFor(Node fact, bool& isSym) +{ + isSym = false; + NodeProofGeneratorMap::const_iterator it = d_gens.find(fact); + if (it != d_gens.end()) + { + return (*it).second; + } + Node factSym = CDProof::getSymmFact(fact); + // could be symmetry + if (factSym.isNull()) + { + // can't be symmetry, return the default generator + return d_defaultGen; + } + it = d_gens.find(factSym); + if (it != d_gens.end()) + { + isSym = true; + return (*it).second; + } + // return the default generator + return d_defaultGen; +} + +bool LazyCDProof::hasGenerators() const +{ + return !d_gens.empty() || d_defaultGen != nullptr; +} + +bool LazyCDProof::hasGenerator(Node fact) const +{ + if (d_defaultGen != nullptr) + { + return true; + } + NodeProofGeneratorMap::const_iterator it = d_gens.find(fact); + if (it != d_gens.end()) + { + return true; + } + // maybe there is a symmetric fact? + Node factSym = CDProof::getSymmFact(fact); + if (!factSym.isNull()) + { + it = d_gens.find(factSym); + } + return it != d_gens.end(); +} + +} // namespace cvc5 diff --git a/src/proof/lazy_proof.h b/src/proof/lazy_proof.h new file mode 100644 index 000000000..b566e408e --- /dev/null +++ b/src/proof/lazy_proof.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Lazy proof utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__LAZY_PROOF_H +#define CVC5__PROOF__LAZY_PROOF_H + +#include "proof/proof.h" + +namespace cvc5 { + +class ProofGenerator; +class ProofNodeManager; + +/** + * A (context-dependent) lazy proof. This class is an extension of CDProof + * that additionally maps facts to proof generators in a context-dependent + * manner. It extends CDProof with an additional method, addLazyStep for adding + * steps to a proof via a given proof generator. + */ +class LazyCDProof : public CDProof +{ + public: + /** Constructor + * + * @param pnm The proof node manager for constructing ProofNode objects. + * @param dpg The (optional) default proof generator, which is called + * for facts that have no explicitly provided generator. + * @param c The context that this class depends on. If none is provided, + * this class is context-independent. + */ + LazyCDProof(ProofNodeManager* pnm, + ProofGenerator* dpg = nullptr, + context::Context* c = nullptr, + std::string name = "LazyCDProof"); + ~LazyCDProof(); + /** + * Get lazy proof for fact, or nullptr if it does not exist. This may + * additionally call proof generators to generate proofs for ASSUME nodes that + * don't yet have a concrete proof. + */ + std::shared_ptr getProofFor(Node fact) override; + /** Add step by generator + * + * This method stores that expected can be proven by proof generator pg if + * it is required to do so. This mapping is maintained in the remainder of + * the current context (according to the context c provided to this class). + * + * It is important to note that pg is asked to provide a proof for expected + * only when no other call for the fact expected is provided via the addStep + * method of this class. In particular, pg is asked to prove expected when it + * appears as the conclusion of an ASSUME leaf within CDProof::getProofFor. + * + * @param expected The fact that can be proven. + * @param pg The generator that can proof expected. + * @param trustId If a null proof generator is provided, we add a step to + * the proof that has trustId as the rule and expected as the sole argument. + * We do this only if trustId is not PfRule::ASSUME. This is primarily used + * for identifying the kind of hole when a proof generator is not given. + * @param isClosed Whether to expect that pg can provide a closed proof for + * this fact. + * @param ctx The context we are in (for debugging). + * @param forceOverwrite If this flag is true, then this call overwrites + * an existing proof generator provided for expected, if one was provided + * via a previous call to addLazyStep in the current context. + */ + void addLazyStep(Node expected, + ProofGenerator* pg, + PfRule trustId = PfRule::ASSUME, + bool isClosed = false, + const char* ctx = "LazyCDProof::addLazyStep", + bool forceOverwrite = false); + /** + * Does this have any proof generators? This method always returns true + * if the default is non-null. + */ + bool hasGenerators() const; + /** Does the given fact have an explicitly provided generator? */ + bool hasGenerator(Node fact) const; + + protected: + typedef context::CDHashMap NodeProofGeneratorMap; + /** Maps facts that can be proven to generators */ + NodeProofGeneratorMap d_gens; + /** The default proof generator */ + ProofGenerator* d_defaultGen; + /** + * Get generator for fact, or nullptr if it doesnt exist. This method is + * robust to symmetry of (dis)equality. It updates isSym to true if a + * proof generator for the symmetric form of fact was provided. + */ + ProofGenerator* getGeneratorFor(Node fact, bool& isSym); +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__LAZY_PROOF_H */ diff --git a/src/proof/lazy_proof_chain.cpp b/src/proof/lazy_proof_chain.cpp new file mode 100644 index 000000000..4c1b19ffe --- /dev/null +++ b/src/proof/lazy_proof_chain.cpp @@ -0,0 +1,327 @@ +/****************************************************************************** + * Top contributors (to current version): + * Haniel Barbosa, Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of lazy proof utility. + */ + +#include "proof/lazy_proof_chain.h" + +#include "options/proof_options.h" +#include "proof/proof.h" +#include "proof/proof_ensure_closed.h" +#include "proof/proof_node.h" +#include "proof/proof_node_algorithm.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { + +LazyCDProofChain::LazyCDProofChain(ProofNodeManager* pnm, + bool cyclic, + context::Context* c, + ProofGenerator* defGen, + bool defRec) + : d_manager(pnm), + d_cyclic(cyclic), + d_defRec(defRec), + d_context(), + d_gens(c ? c : &d_context), + d_defGen(defGen) +{ +} + +LazyCDProofChain::~LazyCDProofChain() {} + +const std::map> LazyCDProofChain::getLinks() + const +{ + std::map> links; + for (const std::pair& link : d_gens) + { + Assert(link.second); + std::shared_ptr pfn = link.second->getProofFor(link.first); + Assert(pfn); + links[link.first] = pfn; + } + return links; +} + +std::shared_ptr LazyCDProofChain::getProofFor(Node fact) +{ + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor " << fact << "\n"; + // which facts have had proofs retrieved for. This is maintained to avoid + // cycles. It also saves the proof node of the fact + std::unordered_map> toConnect; + // leaves of proof node links in the chain, i.e. assumptions, yet to be + // expanded + std::unordered_map>> + assumptionsToExpand; + // invariant of the loop below, the first iteration notwithstanding: + // visit = domain(assumptionsToExpand) \ domain(toConnect) + std::vector visit{fact}; + std::unordered_map visited; + Node cur; + do + { + cur = visit.back(); + visit.pop_back(); + auto itVisited = visited.find(cur); + // pre-traversal + if (itVisited == visited.end()) + { + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: check " << cur << "\n"; + Assert(toConnect.find(cur) == toConnect.end()); + bool rec = true; + ProofGenerator* pg = getGeneratorForInternal(cur, rec); + if (!pg) + { + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: nothing to do\n"; + // nothing to do for this fact, it'll be a leaf in the final proof + // node, don't post-traverse. + visited[cur] = true; + continue; + } + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: Call generator " << pg->identify() + << " for chain link " << cur << "\n"; + std::shared_ptr curPfn = pg->getProofFor(cur); + if (curPfn == nullptr) + { + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: No proof found, skip\n"; + visited[cur] = true; + continue; + } + // map node whose proof node must be expanded to the respective poof node + toConnect[cur] = curPfn; + if (!rec) + { + // we don't want to recursively connect this proof + visited[cur] = true; + continue; + } + Trace("lazy-cdproofchain-debug") + << "LazyCDProofChain::getProofFor: stored proof: " << *curPfn.get() + << "\n"; + // retrieve free assumptions and their respective proof nodes + std::map>> famap; + expr::getFreeAssumptionsMap(curPfn, famap); + if (Trace.isOn("lazy-cdproofchain")) + { + unsigned alreadyToVisit = 0; + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: " << famap.size() + << " free assumptions:\n"; + for (auto fap : famap) + { + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: - " << fap.first << "\n"; + alreadyToVisit += + std::find(visit.begin(), visit.end(), fap.first) != visit.end() + ? 1 + : 0; + } + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: " << alreadyToVisit + << " already to visit\n"; + } + // mark for post-traversal if we are controlling cycles + if (d_cyclic) + { + Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: marking " + << cur << " for cycle check\n"; + visit.push_back(cur); + visited[cur] = false; + } + else + { + visited[cur] = true; + } + // enqueue free assumptions to process + for (const std::pair>>& + fap : famap) + { + // check cycles + if (d_cyclic) + { + // cycles are assumptions being *currently* expanded and seen again, + // i.e. in toConnect and not yet post-visited + auto itToConnect = toConnect.find(fap.first); + if (itToConnect != toConnect.end() && !visited[fap.first]) + { + // Since we have a cycle with an assumption, this fact will be an + // assumption in the final proof node produced by this + // method. Thus we erase it as something to be connected, which + // will keep it as an assumption. + Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: " + "removing cyclic assumption " + << fap.first << " from expansion\n"; + continue; + } + } + // We always add assumptions to visit so that their last seen occurrence + // is expanded (rather than the first seen occurrence, if we were not + // adding assumptions, say, in assumptionsToExpand). This is so because + // in the case where we are checking cycles this is necessary (and + // harmless when we are not). For example the cycle + // + // a2 + // ... + // ---- + // a1 + // ... + // -------- + // a0 a1 a2 + // ... + // --------------- + // n + // + // in which a2 has a1 as an assumption, which has a2 an assumption, + // would not be captured if we did not revisit a1, which is an + // assumption of n and this already occur in assumptionsToExpand when + // it shows up again as an assumption of a2. + visit.push_back(fap.first); + // add assumption proof nodes to be updated + assumptionsToExpand[fap.first].insert( + assumptionsToExpand[fap.first].end(), + fap.second.begin(), + fap.second.end()); + } + if (d_cyclic) + { + Trace("lazy-cdproofchain") << push; + Trace("lazy-cdproofchain-debug") << push; + } + } + else if (!itVisited->second) + { + visited[cur] = true; + Trace("lazy-cdproofchain") << pop; + Trace("lazy-cdproofchain-debug") << pop; + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: post-visited " << cur << "\n"; + } + else + { + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: already fully processed " << cur + << "\n"; + } + } while (!visit.empty()); + // expand all assumptions marked to be connected + for (const std::pair>& npfn : + toConnect) + { + auto it = assumptionsToExpand.find(npfn.first); + if (it == assumptionsToExpand.end()) + { + Assert(npfn.first == fact); + continue; + } + Assert(npfn.second); + Trace("lazy-cdproofchain") + << "LazyCDProofChain::getProofFor: expand assumption " << npfn.first + << "\n"; + Trace("lazy-cdproofchain-debug") + << "LazyCDProofChain::getProofFor: ...expand to " << *npfn.second.get() + << "\n"; + // update each assumption proof node + for (std::shared_ptr pfn : it->second) + { + d_manager->updateNode(pfn.get(), npfn.second.get()); + } + } + // final proof of fact + auto it = toConnect.find(fact); + if (it == toConnect.end()) + { + return nullptr; + } + return it->second; +} + +void LazyCDProofChain::addLazyStep(Node expected, ProofGenerator* pg) +{ + Assert(pg != nullptr); + Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected + << " set to generator " << pg->identify() << "\n"; + // note this will replace the generator for expected, if any + d_gens.insert(expected, pg); +} + +void LazyCDProofChain::addLazyStep(Node expected, + ProofGenerator* pg, + const std::vector& assumptions, + const char* ctx) +{ + Assert(pg != nullptr); + Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected + << " set to generator " << pg->identify() << "\n"; + // note this will rewrite the generator for expected, if any + d_gens.insert(expected, pg); + // check if chain is closed if options::proofEagerChecking() is on + if (options::proofEagerChecking()) + { + Trace("lazy-cdproofchain") + << "LazyCDProofChain::addLazyStep: Checking closed proof...\n"; + std::shared_ptr pfn = pg->getProofFor(expected); + std::vector allowedLeaves{assumptions.begin(), assumptions.end()}; + // add all current links in the chain + for (const std::pair& link : d_gens) + { + allowedLeaves.push_back(link.first); + } + if (Trace.isOn("lazy-cdproofchain")) + { + Trace("lazy-cdproofchain") << "Checking relative to leaves...\n"; + for (const Node& n : allowedLeaves) + { + Trace("lazy-cdproofchain") << " - " << n << "\n"; + } + Trace("lazy-cdproofchain") << "\n"; + } + pfnEnsureClosedWrt(pfn.get(), allowedLeaves, "lazy-cdproofchain", ctx); + } +} + +bool LazyCDProofChain::hasGenerator(Node fact) const +{ + return d_gens.find(fact) != d_gens.end(); +} + +ProofGenerator* LazyCDProofChain::getGeneratorFor(Node fact) +{ + bool rec = true; + return getGeneratorForInternal(fact, rec); +} + +ProofGenerator* LazyCDProofChain::getGeneratorForInternal(Node fact, bool& rec) +{ + auto it = d_gens.find(fact); + if (it != d_gens.end()) + { + return (*it).second; + } + // otherwise, if no explicit generators, we use the default one + if (d_defGen != nullptr) + { + rec = d_defRec; + return d_defGen; + } + return nullptr; +} + +std::string LazyCDProofChain::identify() const { return "LazyCDProofChain"; } + +} // namespace cvc5 diff --git a/src/proof/lazy_proof_chain.h b/src/proof/lazy_proof_chain.h new file mode 100644 index 000000000..4315ee87a --- /dev/null +++ b/src/proof/lazy_proof_chain.h @@ -0,0 +1,154 @@ +/****************************************************************************** + * Top contributors (to current version): + * Haniel Barbosa, Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Lazy proof chain utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__LAZY_PROOF_CHAIN_H +#define CVC5__PROOF__LAZY_PROOF_CHAIN_H + +#include + +#include "context/cdhashmap.h" +#include "proof/proof_generator.h" + +namespace cvc5 { + +class ProofNodeManager; + +/** + * A (context-dependent) lazy generator for proof chains. This class is an + * extension of ProofGenerator that additionally that maps facts to proof + * generators in a context-dependent manner. The map is built with the addition + * of lazy steps mapping facts to proof generators. More importantly, its + * getProofFor method expands the proof generators registered to this class by + * connecting, for the proof generated to one fact, assumptions to the proofs + * generated for those assumptinos that are registered in the chain. + */ +class LazyCDProofChain : public ProofGenerator +{ + public: + /** Constructor + * + * @param pnm The proof node manager for constructing ProofNode objects. + * @param cyclic Whether this instance is robust to cycles in the chain. + * @param c The context that this class depends on. If none is provided, + * this class is context-independent. + * @param defGen The default generator to be used if no generator exists + * for a step. + * @param defRec Whether this instance expands proofs from defGen recursively. + */ + LazyCDProofChain(ProofNodeManager* pnm, + bool cyclic = true, + context::Context* c = nullptr, + ProofGenerator* defGen = nullptr, + bool defRec = true); + ~LazyCDProofChain(); + /** + * Get lazy proof for fact, or nullptr if it does not exist, by connecting the + * proof nodes generated for each intermediate relevant fact registered in the + * chain (i.e., in the domain of d_gens). + * + * For example, if d_gens consists of the following pairs + * + * --- (A, PG1) + * --- (B, PG2) + * --- (C, PG3) + * + * and getProofFor(A) is called, with PG1 generating a proof with assumptions + * B and D, then B is expanded, with its assumption proof node being updated + * to the expanded proof node, while D is not. Assuming PG2 provides a proof + * with assumptions C and E, then C is expanded and E is not. Suppose PG3 + * gives a closed proof, thus the call getProofFor(A) produces a proof node + * + * A : ( ... B : ( ... C : (...), ... ASSUME( E ) ), ... ASSUME( D ) ) + * + * Note that the expansions are done directly on the proof nodes produced by + * the generators. + * + * If this instance has been set to be robust to cyclic proofs (i.e., d_cyclic + * is true), then the construction of the proof chain checks that there are no + * cycles, i.e., a given fact would have itself as an assumption when + * connecting the chain links. If such a cycle were to be detected then the + * fact will be marked as an assumption and not expanded in the final proof + * node. The method does not fail. + */ + std::shared_ptr getProofFor(Node fact) override; + /** Add step by generator + * + * This method stores that expected can be proven by proof generator pg if + * it is required to do so. This mapping is maintained in the remainder of + * the current context (according to the context c provided to this class). + * + * Moreover the lazy steps of this class are expected to fulfill the + * requirement that pg.getProofFor(expected) generates a proof node closed + * with relation to + * (1) a fixed set F1, ..., Fn, + * (2) formulas in the current domain of d_gens. + * + * This is so that we only add links to the chain that depend on a fixed set + * of assumptions or in other links. + * + * @param expected The fact that can be proven. + * @param pg The generator that can proof expected. + * @param assumptions The fixed set of assumptions with relation to which the + * chain, now augmented with expect, must be closed. + * @param ctx The context we are in (for debugging). + */ + void addLazyStep(Node expected, + ProofGenerator* pg, + const std::vector& assumptions, + const char* ctx = "LazyCDProofChain::addLazyStep"); + + /** As above but does not do the closedness check. */ + void addLazyStep(Node expected, ProofGenerator* pg); + + /** Does the given fact have an explicitly provided generator? */ + bool hasGenerator(Node fact) const; + + /** + * Get generator for fact, or nullptr if it doesnt exist. + */ + ProofGenerator* getGeneratorFor(Node fact); + + /** identify */ + std::string identify() const override; + + /** Retrieve, for each fact in d_gens, it mapped to the proof node generated + * by its generator in d_gens. */ + const std::map> getLinks() const; + + private: + /** + * Get generator for fact, or nullptr if it doesnt exist. Updates rec to + * true if we should recurse on its proof. + */ + ProofGenerator* getGeneratorForInternal(Node fact, bool& rec); + /** The proof manager, used for allocating new ProofNode objects */ + ProofNodeManager* d_manager; + /** Whether this instance is robust to cycles in the chain. */ + bool d_cyclic; + /** Whether we expand recursively (for the default generator) */ + bool d_defRec; + /** A dummy context used by this class if none is provided */ + context::Context d_context; + /** Maps facts that can be proven to generators */ + context::CDHashMap d_gens; + /** The default proof generator (if one exists) */ + ProofGenerator* d_defGen; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__LAZY_PROOF_CHAIN_H */ diff --git a/src/proof/lazy_tree_proof_generator.cpp b/src/proof/lazy_tree_proof_generator.cpp new file mode 100644 index 000000000..a50b9efd4 --- /dev/null +++ b/src/proof/lazy_tree_proof_generator.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** + * Top contributors (to current version): + * Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of the lazy tree proof generator class. + */ + +#include "proof/lazy_tree_proof_generator.h" + +#include + +#include "expr/node.h" +#include "proof/proof_generator.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { +namespace theory { + +LazyTreeProofGenerator::LazyTreeProofGenerator(ProofNodeManager* pnm, + const std::string& name) + : d_pnm(pnm), d_name(name) +{ + d_stack.emplace_back(&d_proof); +} +void LazyTreeProofGenerator::openChild() +{ + detail::TreeProofNode& pn = getCurrent(); + pn.d_children.emplace_back(); + d_stack.emplace_back(&pn.d_children.back()); +} +void LazyTreeProofGenerator::closeChild() +{ + Assert(getCurrent().d_rule != PfRule::UNKNOWN); + d_stack.pop_back(); +} +detail::TreeProofNode& LazyTreeProofGenerator::getCurrent() +{ + Assert(!d_stack.empty()) << "Proof construction has already been finished."; + return *d_stack.back(); +} +void LazyTreeProofGenerator::setCurrent(PfRule rule, + const std::vector& premise, + std::vector args, + Node proven) +{ + detail::TreeProofNode& pn = getCurrent(); + pn.d_rule = rule; + pn.d_premise = premise; + pn.d_args = args; + pn.d_proven = proven; +} +std::shared_ptr LazyTreeProofGenerator::getProof() const +{ + // Check cache + if (d_cached) return d_cached; + Assert(d_stack.empty()) << "Proof construction has not yet been finished."; + std::vector> scope; + d_cached = getProof(scope, d_proof); + return d_cached; +} + +std::shared_ptr LazyTreeProofGenerator::getProofFor(Node f) +{ + Assert(hasProofFor(f)); + return getProof(); +} + +bool LazyTreeProofGenerator::hasProofFor(Node f) +{ + return f == getProof()->getResult(); +} + +std::shared_ptr LazyTreeProofGenerator::getProof( + std::vector>& scope, + const detail::TreeProofNode& pn) const +{ + // Store scope size to reset scope afterwards + std::size_t before = scope.size(); + std::vector> children; + if (pn.d_rule == PfRule::SCOPE) + { + // Extend scope for all but the root node + if (&pn != &d_proof) + { + for (const auto& a : pn.d_args) + { + scope.emplace_back(d_pnm->mkAssume(a)); + } + } + } + else + { + // Initialize the children with the scope + children = scope; + } + for (auto& c : pn.d_children) + { + // Recurse into tree + children.emplace_back(getProof(scope, c)); + } + for (const auto& p : pn.d_premise) + { + // Add premises as assumptions + children.emplace_back(d_pnm->mkAssume(p)); + } + // Reset scope + scope.resize(before); + return d_pnm->mkNode(pn.d_rule, children, pn.d_args); +} + +void LazyTreeProofGenerator::print(std::ostream& os, + const std::string& prefix, + const detail::TreeProofNode& pn) const +{ + os << prefix << pn.d_rule << ": "; + container_to_stream(os, pn.d_premise); + os << " ==> " << pn.d_proven << std::endl; + if (!pn.d_args.empty()) + { + os << prefix << ":args "; + container_to_stream(os, pn.d_args); + std::cout << std::endl; + } + for (const auto& c : pn.d_children) + { + print(os, prefix + '\t', c); + } +} + +std::ostream& operator<<(std::ostream& os, const LazyTreeProofGenerator& ltpg) +{ + ltpg.print(os, "", ltpg.d_proof); + return os; +} + +} // namespace theory +} // namespace cvc5 diff --git a/src/proof/lazy_tree_proof_generator.h b/src/proof/lazy_tree_proof_generator.h new file mode 100644 index 000000000..8b8d344e9 --- /dev/null +++ b/src/proof/lazy_tree_proof_generator.h @@ -0,0 +1,223 @@ +/****************************************************************************** + * Top contributors (to current version): + * Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * The lazy tree proof generator class. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__LAZY_TREE_PROOF_GENERATOR_H +#define CVC5__PROOF__LAZY_TREE_PROOF_GENERATOR_H + +#include + +#include "expr/node.h" +#include "proof/proof_generator.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { +namespace theory { +namespace detail { +/** + * A single node in the proof tree created by the LazyTreeProofGenerator. + * A node directly represents a ProofNode that is eventually constructed from + * it. The Nodes of the additional field d_premise are added to d_children as + * new assumptions via ASSUME. + */ +struct TreeProofNode +{ + /** The proof rule */ + PfRule d_rule = PfRule::UNKNOWN; + /** Assumptions used as premise for this proof step */ + std::vector d_premise; + /** Arguments for this proof step */ + std::vector d_args; + /** Conclusion of this proof step */ + Node d_proven; + /** Children of this proof step */ + std::vector d_children; +}; +} // namespace detail + +/** + * This class supports the lazy creation of a tree-shaped proof. + * The core idea is that the lazy creation is necessary because proof rules + * depend on assumptions that are only known after the whole subtree has been + * finished. + * + * We indend to argue about proofs that roughly go as follows: + * - we assume a set of assumptions + * - we do a case split + * - for every case, we derive false, possibly by nested case splits + * + * Every case is represented by a SCOPE whose child derives false. When + * composing the final proof, we explicitly extend the premise of every proof + * step with the scope (the currently selected case) that this proof step lives + * in. When doing this, we ignore the outermost scope (which assumes the + * assertion set) to allow for having conflicts that are only a subset of the + * assertion set. + * + * Consider the example x*x<1 and x>2 and the intended proof + * (SCOPE + * (ARITH_NL_CAD_SPLIT + * (SCOPE + * (ARITH_NL_CAD_DIRECT (x<=2 and x>2) ==> false) + * :args [x<=2] + * ) + * (SCOPE + * (ARITH_NL_CAD_DIRECT (x>=1 and x*x<1) ==> false) + * :args [x>=1] + * ) + * ) + * :args [ x*x<1, x>2 ] + * ) + * We obtain this proof in a way that (in general) gives us the assumptions + * for the scopes (x<=2, x>=1) only when the scope children have been fully + * computed. Thus, we store the proof in an intermediate form and add the scope + * arguments when we learn them. Once we have completed the proof, we can easily + * transform it into proper ProofNodes. + * + * This class is stateful in the sense that the interface (in particular + * openChild() and closeChild()) navigates the proof tree (and creating it + * on-the-fly). Initially, and after reset() has been called, the proof consists + * of one empty proof node (with proof rule UNKNOWN). It stores the current + * position in the proof tree internally to make the interface easier to use. + * + * THIS MAKES THIS CLASS INHERENTLY NOT THREAD-SAFE! + * + * To construct the above proof, use this class roughly as follows: + * setCurrent(SCOPE, {}, assertions, false); + * openChild(); + * // First nested scope + * openChild(); + * setCurrent(SCOPE, {}, {}, false); + * openChild(); + * setCurrent(CAD_DIRECT, {x>2}, {}, false); + * closeChild(); + * getCurrent().args = {x<=2}; + * closeChild(); + * // Second nested scope + * openChild(); + * setCurrent(SCOPE, {}, {}, false); + * openChild(); + * setCurrent(CAD_DIRECT, {x*x<1}, {}, false); + * closeChild(); + * getCurrent().args = {x>=1}; + * closeChild(); + * // Finish split + * setCurrent(CAD_SPLIT, {}, {}, false); + * closeChild(); + * closeChild(); + * + * To explicitly finish proof construction, we need to call closeChild() one + * additional time. + */ +class LazyTreeProofGenerator : public ProofGenerator +{ + public: + friend std::ostream& operator<<(std::ostream& os, + const LazyTreeProofGenerator& ltpg); + + LazyTreeProofGenerator(ProofNodeManager* pnm, + const std::string& name = "LazyTreeProofGenerator"); + + std::string identify() const override { return d_name; } + /** Create a new child and make it the current node */ + void openChild(); + /** + * Finish the current node and return to its parent + * Checks that the current node has a proof rule different from UNKNOWN. + * When called on the root node, this finishes the proof construction: we can + * no longer call getCurrent(), setCurrent() openChild() and closeChild(), but + * instead can call getProof() now. + */ + void closeChild(); + /** + * Return a reference to the current node + */ + detail::TreeProofNode& getCurrent(); + /** Set the current node / proof step */ + void setCurrent(PfRule rule, + const std::vector& premise, + std::vector args, + Node proven); + /** Construct the proof as a ProofNode */ + std::shared_ptr getProof() const; + /** Return the constructed proof. Checks that we have proven f */ + std::shared_ptr getProofFor(Node f) override; + /** Checks whether we have proven f */ + bool hasProofFor(Node f) override; + + /** + * Removes children from the current node based on the given predicate. + * It can be used for cases where facts (and their proofs) are eagerly + * generated and then later pruned, for example to produce smaller conflicts. + * The predicate is given as a Callable f that is called for every child with + * the id of the child and the child itself. + * f should return true if the child should be kept, fals if the child should + * be removed. + * @param f a Callable bool(std::size_t, const detail::TreeProofNode&) + */ + template + void pruneChildren(F&& f) + { + auto& children = getCurrent().d_children; + std::size_t cur = 0; + std::size_t pos = 0; + for (std::size_t size = children.size(); cur < size; ++cur) + { + if (f(cur, children[pos])) + { + if (cur != pos) + { + children[pos] = std::move(children[cur]); + } + ++pos; + } + } + children.resize(pos); + } + + private: + /** recursive proof construction used by getProof() */ + std::shared_ptr getProof( + std::vector>& scope, + const detail::TreeProofNode& pn) const; + + /** recursive debug printing used by operator<<() */ + void print(std::ostream& os, + const std::string& prefix, + const detail::TreeProofNode& pn) const; + + /** The ProofNodeManager used for constructing ProofNodes */ + ProofNodeManager* d_pnm; + /** The trace to the current node */ + std::vector d_stack; + /** The root node of the proof tree */ + detail::TreeProofNode d_proof; + /** Caches the result of getProof() */ + mutable std::shared_ptr d_cached; + /** Name of this proof generator */ + std::string d_name; +}; + +/** + * Prints the current state of a LazyTreeProofGenerator. Can also be used on a + * partially constructed proof. It is intended for debugging only and attempts + * to pretty-print itself using indentation. + */ +std::ostream& operator<<(std::ostream& os, const LazyTreeProofGenerator& ltpg); + +} // namespace theory +} // namespace cvc5 + +#endif diff --git a/src/proof/proof.cpp b/src/proof/proof.cpp new file mode 100644 index 000000000..a100be990 --- /dev/null +++ b/src/proof/proof.cpp @@ -0,0 +1,459 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof. + */ + +#include "proof/proof.h" + +#include "proof/proof_checker.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +CDProof::CDProof(ProofNodeManager* pnm, + context::Context* c, + std::string name, + bool autoSymm) + : d_manager(pnm), + d_context(), + d_nodes(c ? c : &d_context), + d_name(name), + d_autoSymm(autoSymm) +{ +} + +CDProof::~CDProof() {} + +std::shared_ptr CDProof::getProofFor(Node fact) +{ + std::shared_ptr pf = getProofSymm(fact); + if (pf != nullptr) + { + return pf; + } + // add as assumption + std::vector pargs = {fact}; + std::vector> passume; + std::shared_ptr pfa = + d_manager->mkNode(PfRule::ASSUME, passume, pargs, fact); + d_nodes.insert(fact, pfa); + return pfa; +} + +std::shared_ptr CDProof::getProof(Node fact) const +{ + NodeProofNodeMap::iterator it = d_nodes.find(fact); + if (it != d_nodes.end()) + { + return (*it).second; + } + return nullptr; +} + +std::shared_ptr CDProof::getProofSymm(Node fact) +{ + Trace("cdproof") << "CDProof::getProofSymm: " << fact << std::endl; + std::shared_ptr pf = getProof(fact); + if (pf != nullptr && !isAssumption(pf.get())) + { + Trace("cdproof") << "...existing non-assume " << pf->getRule() << std::endl; + return pf; + } + else if (!d_autoSymm) + { + Trace("cdproof") << "...not auto considering symmetry" << std::endl; + return pf; + } + Node symFact = getSymmFact(fact); + if (symFact.isNull()) + { + Trace("cdproof") << "...no possible symm" << std::endl; + // no symmetry possible, return original proof (possibly assumption) + return pf; + } + // See if a proof exists for the opposite direction, if so, add the step. + // Notice that SYMM is also disallowed. + std::shared_ptr pfs = getProof(symFact); + if (pfs != nullptr) + { + // The symmetric fact exists, and the current one either does not, or is + // an assumption. We make a new proof that applies SYMM to pfs. + std::vector> pschild; + pschild.push_back(pfs); + std::vector args; + if (pf == nullptr) + { + Trace("cdproof") << "...fresh make symm" << std::endl; + std::shared_ptr psym = + d_manager->mkNode(PfRule::SYMM, pschild, args, fact); + Assert(psym != nullptr); + d_nodes.insert(fact, psym); + return psym; + } + else if (!isAssumption(pfs.get())) + { + // if its not an assumption, make the connection + Trace("cdproof") << "...update symm" << std::endl; + // update pf + bool sret = d_manager->updateNode(pf.get(), PfRule::SYMM, pschild, args); + AlwaysAssert(sret); + } + } + else + { + Trace("cdproof") << "...no symm, return " + << (pf == nullptr ? "null" : "non-null") << std::endl; + } + // return original proof (possibly assumption) + return pf; +} + +bool CDProof::addStep(Node expected, + PfRule id, + const std::vector& children, + const std::vector& args, + bool ensureChildren, + CDPOverwrite opolicy) +{ + Trace("cdproof") << "CDProof::addStep: " << identify() << " : " << id << " " + << expected << ", ensureChildren = " << ensureChildren + << ", overwrite policy = " << opolicy << std::endl; + Trace("cdproof-debug") << "CDProof::addStep: " << identify() + << " : children: " << children << "\n"; + Trace("cdproof-debug") << "CDProof::addStep: " << identify() + << " : args: " << args << "\n"; + // We must always provide expected to this method + Assert(!expected.isNull()); + + std::shared_ptr pprev = getProofSymm(expected); + if (pprev != nullptr) + { + if (!shouldOverwrite(pprev.get(), id, opolicy)) + { + // we should not overwrite the current step + Trace("cdproof") << "...success, no overwrite" << std::endl; + return true; + } + Trace("cdproof") << "existing proof " << pprev->getRule() + << ", overwrite..." << std::endl; + // we will overwrite the existing proof node by updating its contents below + } + // collect the child proofs, for each premise + std::vector> pchildren; + for (const Node& c : children) + { + Trace("cdproof") << "- get child " << c << std::endl; + std::shared_ptr pc = getProofSymm(c); + if (pc == nullptr) + { + if (ensureChildren) + { + // failed to get a proof for a child, fail + Trace("cdproof") << "...fail, no child" << std::endl; + return false; + } + Trace("cdproof") << "--- add assume" << std::endl; + // otherwise, we initialize it as an assumption + std::vector pcargs = {c}; + std::vector> pcassume; + pc = d_manager->mkNode(PfRule::ASSUME, pcassume, pcargs, c); + // assumptions never fail to check + Assert(pc != nullptr); + d_nodes.insert(c, pc); + } + pchildren.push_back(pc); + } + + // the user may have provided SYMM of an assumption + if (id == PfRule::SYMM) + { + Assert(pchildren.size() == 1); + if (isAssumption(pchildren[0].get())) + { + // the step we are constructing is a (symmetric fact of an) assumption, so + // there is no use adding it to the proof. + return true; + } + } + + bool ret = true; + // create or update it + std::shared_ptr pthis; + if (pprev == nullptr) + { + Trace("cdproof") << " new node " << expected << "..." << std::endl; + pthis = d_manager->mkNode(id, pchildren, args, expected); + if (pthis == nullptr) + { + // failed to construct the node, perhaps due to a proof checking failure + Trace("cdproof") << "...fail, proof checking" << std::endl; + return false; + } + d_nodes.insert(expected, pthis); + } + else + { + Trace("cdproof") << " update node " << expected << "..." << std::endl; + // update its value + pthis = pprev; + // We return the value of updateNode here. This means this method may return + // false if this call failed, regardless of whether we already have a proof + // step for expected. + ret = d_manager->updateNode(pthis.get(), id, pchildren, args); + } + if (ret) + { + // the result of the proof node should be expected + Assert(pthis->getResult() == expected); + + // notify new proof + notifyNewProof(expected); + } + + Trace("cdproof") << "...return " << ret << std::endl; + return ret; +} + +void CDProof::notifyNewProof(Node expected) +{ + if (!d_autoSymm) + { + return; + } + // ensure SYMM proof is also linked to an existing proof, if it is an + // assumption. + Node symExpected = getSymmFact(expected); + if (!symExpected.isNull()) + { + Trace("cdproof") << " check connect symmetry " << symExpected << std::endl; + // if it exists, we may need to update it + std::shared_ptr pfs = getProof(symExpected); + if (pfs != nullptr) + { + Trace("cdproof") << " connect via getProofSymm method..." << std::endl; + // call the get function with symmetry, which will do the update + std::shared_ptr pfss = getProofSymm(symExpected); + } + else + { + Trace("cdproof") << " no connect" << std::endl; + } + } +} + +bool CDProof::addStep(Node expected, + const ProofStep& step, + bool ensureChildren, + CDPOverwrite opolicy) +{ + return addStep(expected, + step.d_rule, + step.d_children, + step.d_args, + ensureChildren, + opolicy); +} + +bool CDProof::addSteps(const ProofStepBuffer& psb, + bool ensureChildren, + CDPOverwrite opolicy) +{ + const std::vector>& steps = psb.getSteps(); + for (const std::pair& ps : steps) + { + if (!addStep(ps.first, ps.second, ensureChildren, opolicy)) + { + return false; + } + } + return true; +} + +bool CDProof::addProof(std::shared_ptr pn, + CDPOverwrite opolicy, + bool doCopy) +{ + if (!doCopy) + { + // If we aren't doing a deep copy, we either store pn or link its top + // node into the existing pointer + Node curFact = pn->getResult(); + std::shared_ptr cur = getProofSymm(curFact); + if (cur == nullptr) + { + // Assert that the checker of this class agrees with (the externally + // provided) pn. This ensures that if pn was checked by a different + // checker than the one of the manager in this class, then it is double + // checked here, so that this class maintains the invariant that all of + // its nodes in d_nodes have been checked by the underlying checker. + Assert(d_manager->getChecker() == nullptr + || d_manager->getChecker()->check(pn.get(), curFact) == curFact); + // just store the proof for fact + d_nodes.insert(curFact, pn); + } + else if (shouldOverwrite(cur.get(), pn->getRule(), opolicy)) + { + // We update cur to have the structure of the top node of pn. Notice that + // the interface to update this node will ensure that the proof apf is a + // proof of the assumption. If it does not, then pn was wrong. + if (!d_manager->updateNode( + cur.get(), pn->getRule(), pn->getChildren(), pn->getArguments())) + { + return false; + } + } + // also need to connect via SYMM if necessary + notifyNewProof(curFact); + return true; + } + std::unordered_map visited; + std::unordered_map::iterator it; + std::vector visit; + ProofNode* cur; + Node curFact; + visit.push_back(pn.get()); + bool retValue = true; + do + { + cur = visit.back(); + curFact = cur->getResult(); + visit.pop_back(); + it = visited.find(cur); + if (it == visited.end()) + { + // visit the children + visited[cur] = false; + visit.push_back(cur); + const std::vector>& cs = cur->getChildren(); + for (const std::shared_ptr& c : cs) + { + visit.push_back(c.get()); + } + } + else if (!it->second) + { + // we always call addStep, which may or may not overwrite the + // current step + std::vector pexp; + const std::vector>& cs = cur->getChildren(); + for (const std::shared_ptr& c : cs) + { + Assert(!c->getResult().isNull()); + pexp.push_back(c->getResult()); + } + // can ensure children at this point + bool res = addStep( + curFact, cur->getRule(), pexp, cur->getArguments(), true, opolicy); + // should always succeed + Assert(res); + retValue = retValue && res; + visited[cur] = true; + } + } while (!visit.empty()); + + return retValue; +} + +bool CDProof::hasStep(Node fact) +{ + std::shared_ptr pf = getProof(fact); + if (pf != nullptr && !isAssumption(pf.get())) + { + return true; + } + else if (!d_autoSymm) + { + return false; + } + Node symFact = getSymmFact(fact); + if (symFact.isNull()) + { + return false; + } + pf = getProof(symFact); + if (pf != nullptr && !isAssumption(pf.get())) + { + return true; + } + return false; +} + +ProofNodeManager* CDProof::getManager() const { return d_manager; } + +bool CDProof::shouldOverwrite(ProofNode* pn, PfRule newId, CDPOverwrite opol) +{ + Assert(pn != nullptr); + // we overwrite only if opol is CDPOverwrite::ALWAYS, or if + // opol is CDPOverwrite::ASSUME_ONLY and the previously + // provided proof pn was an assumption and the currently provided step is not + return opol == CDPOverwrite::ALWAYS + || (opol == CDPOverwrite::ASSUME_ONLY && isAssumption(pn) + && newId != PfRule::ASSUME); +} + +bool CDProof::isAssumption(ProofNode* pn) +{ + PfRule rule = pn->getRule(); + if (rule == PfRule::ASSUME) + { + return true; + } + else if (rule == PfRule::SYMM) + { + const std::vector>& pc = pn->getChildren(); + Assert(pc.size() == 1); + return pc[0]->getRule() == PfRule::ASSUME; + } + return false; +} + +bool CDProof::isSame(TNode f, TNode g) +{ + if (f == g) + { + return true; + } + Kind fk = f.getKind(); + Kind gk = g.getKind(); + if (fk == EQUAL && gk == EQUAL && f[0] == g[1] && f[1] == g[0]) + { + // symmetric equality + return true; + } + if (fk == NOT && gk == NOT && f[0].getKind() == EQUAL + && g[0].getKind() == EQUAL && f[0][0] == g[0][1] && f[0][1] == g[0][0]) + { + // symmetric disequality + return true; + } + return false; +} + +Node CDProof::getSymmFact(TNode f) +{ + bool polarity = f.getKind() != NOT; + TNode fatom = polarity ? f : f[0]; + if (fatom.getKind() != EQUAL || fatom[0] == fatom[1]) + { + return Node::null(); + } + Node symFact = fatom[1].eqNode(fatom[0]); + return polarity ? symFact : symFact.notNode(); +} + +std::string CDProof::identify() const { return d_name; } + +} // namespace cvc5 diff --git a/src/proof/proof.h b/src/proof/proof.h new file mode 100644 index 000000000..2c57e0a2e --- /dev/null +++ b/src/proof/proof.h @@ -0,0 +1,278 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_H +#define CVC5__PROOF__PROOF_H + +#include + +#include "context/cdhashmap.h" +#include "expr/node.h" +#include "proof/proof_generator.h" +#include "proof/proof_step_buffer.h" + +namespace cvc5 { + +class ProofNode; +class ProofNodeManager; + +/** + * A (context-dependent) proof. + * + * This maintains a context-dependent map from formulas to ProofNode shared + * pointers. When a step is added, it internally uses mutable ProofNode pointers + * to link the steps in the proof together. + * + * It is important to note that the premises of proof steps given to this class + * are *Node* not *ProofNode*. In other words, the user of this class does + * not have to manage ProofNode pointers while using this class. Instead, + * ProofNode is the final product of this class, via the getProof interface, + * which returns a ProofNode object that has linked together the proof steps + * added to this object. + * + * Its main interface is the function addStep, which registers a single + * step in the proof. Its interface is: + * addStep( , , , , ...... ) + * where conclusion is what to be proven, rule is an identifier, premises + * are the formulas that imply conclusion, and args are additional arguments to + * the proof rule application. The options include whether we ensure children + * proofs already exist for the premises and our policty for overwriting + * existing steps. + * + * As an example, let A, B, C, D be formulas represented by Nodes. If we + * call: + * - addStep( A, ID_A, { B, C }, {} ) + * - addStep( B, ID_B, { D }, {} ) + * - addStep( E, ID_E, {}, {} ) + * with default options, then getProof( A ) returns the ProofNode of the form: + * ID_A( ID_B( ASSUME( D ) ), ASSUME( C ) ) + * Notice that the above calls to addStep can be made in either order. The + * proof step for E was not involved in the proof of A. + * + * If the user wants to combine proofs, then a addProof interface is + * available. The method addProof can be seen as syntax sugar for making + * multiple calls to addStep. Continuing the above example, if we call: + * - addProof( F, ID_F( ASSUME( A ), ASSUME( E ) ) ) + * is the same as calling steps corresponding the steps in the provided proof + * bottom-up, starting from the leaves. + * --- addStep( A, ASSUME, {}, {}, true ) + * --- addStep( E, ASSUME, {}, {}, true ) + * --- addStep( F, ID_F, { A, E }, {}, true ) + * The fifth argument provided indicates that we want to ensure child proofs + * are already available for the step (see ensureChildren in addStep below). + * This will result in getProof( F ) returning: + * ID_F( ID_A( ID_B( ASSUME( D ) ), ASSUME( C ) ), ID_E() ) + * Notice that the proof of A by ID_A was not overwritten by ASSUME in the + * addStep call above. + * + * The default policy for overwriting proof steps (CDPOverwrite::ASSUME_ONLY) + * gives ASSUME a special status. An ASSUME step can be seen as a step whose + * justification has not yet been provided. Thus, it is always overwritten. + * Other proof rules are never overwritten, unless the argument opolicy is + * CDPOverwrite::ALWAYS. + * + * As an another example, say that we call: + * - addStep( B, ID_B1 {}, {} ) + * - addStep( A, ID_A1, {B, C}, {} ) + * At this point, getProof( A ) returns: + * ID_A1( ID_B1(), ASSUME(C) ) + * Now, assume an additional call is made to addProof, where notice + * the overwrite policy is CDPOverwrite::ASSUME_ONLY by default: + * - addProof( D, ID_D( ID_A2( ID_B2(), ID_C() ) ) ) + * where assume ID_B2() and ID_C() prove B and C respectively. This call is + * equivalent to calling: + * --- addStep( B, ID_B2, {}, {}, true ) + * --- addStep( C, ID_C, {}, {}, true ) + * --- addStep( A, ID_A2, {B, C}, {}, true ) + * --- addStep( D, ID_D, {A}, {}, true ) + * Afterwards, getProof( D ) returns: + * ID_D( ID_A1( ID_B1(), ID_C() ) ) + * Notice that the steps with ID_A1 and ID_B1 were not overwritten by this call, + * whereas the assumption of C was overwritten by the proof ID_C(). Notice that + * the proof of D was completely traversed in addProof, despite encountering + * formulas, A and B, that were already given proof steps. + * + * This class requires a ProofNodeManager object, which is a trusted way of + * allocating ProofNode pointers. This manager may have an underlying checker, + * although this is not required. It also (optionally) takes a context c. + * If no context is provided, then this proof is context-independent. This + * is implemented internally by using a dummy context that is never pushed + * or popped. The map from Nodes to ProofNodes is context-dependent and is + * backtracked when its context backtracks. + * + * An important invariant of this object is that there exists (at most) one + * proof step for each Node. Thus, the ProofNode objects returned by this + * class share proofs for common subformulas, as guaranteed by the fact that + * Node objects have perfect sharing. + * + * Notice that this class is agnostic to symmetry of equality. In other + * words, adding a step that concludes (= x y) is effectively the same as + * providing a step that concludes (= y x) from the point of view of a user + * of this class. This is accomplished by adding SYMM steps on demand when + * a formula is looked up. For example say we call: + * - addStep( (= x y), ID_1 {}, {} ) + * - addStep( P, ID_2, {(= y x)}, {} ) + * Afterwards, getProof( P ) returns: + * ID_2( SYMM( ID_1() ) ) + * where notice SYMM was added so that (= y x) is a premise of the application + * of ID_2. More generally, CDProof::isSame(F,G) returns true if F and G are + * essentially the same formula according to this class. + */ +class CDProof : public ProofGenerator +{ + public: + /** + * @param pnm The proof node manager responsible for constructor ProofNode + * @param c The context this proof depends on + * @param name The name of this proof (for debugging) + * @param autoSymm Whether this proof automatically adds symmetry steps based + * on policy documented above. + */ + CDProof(ProofNodeManager* pnm, + context::Context* c = nullptr, + std::string name = "CDProof", + bool autoSymm = true); + virtual ~CDProof(); + /** + * Make proof for fact. + * + * This method always returns a non-null ProofNode. It may generate new + * steps so that a ProofNode can be constructed for fact. In particular, + * if no step exists for fact, then we may construct and return ASSUME(fact). + * If fact is of the form (= t s), no step exists for fact, but a proof + * P for (= s t) exists, then this method returns SYMM(P). + * + * Notice that this call does *not* clone the ProofNode object. Hence, the + * returned proof may be updated by further calls to this class. The caller + * should call ProofNode::clone if they want to own it. + */ + std::shared_ptr getProofFor(Node fact) override; + /** Add step + * + * @param expected The intended conclusion of this proof step. This must be + * non-null. + * @param id The identifier for the proof step. + * @param children The antecendants of the proof step. Each children[i] should + * be a fact previously added as a conclusion of an addStep call when + * ensureChildren is true. + * @param args The arguments of the proof step. + * @param ensureChildren Whether we wish to ensure steps have been added + * for all nodes in children + * @param opolicy Policy for whether to overwrite if a step for + * expected was already provided (via a previous call to addStep) + * @return The true if indeed the proof step proves expected, or + * false otherwise. The latter can happen if the proof has a different (or + * invalid) conclusion, or if one of the children does not have a proof and + * ensureChildren is true. + * + * This method may create proofs justified by ASSUME of the facts in + * children that have not already been proven when ensureChildren is false. + * Notice that ensureChildren should be true if the proof is being + * constructed is a strictly eager fashion (bottom up from its leaves), while + * ensureChildren should be false if the steps are added lazily or out + * of order. + * + * This method only overwrites proofs for facts that were added as + * steps with id ASSUME when opolicy is CDPOverwrite::ASSUME_ONLY, and always + * (resp. never) overwrites an existing step if one was provided when opolicy + * is CDPOverwrite::ALWAYS (resp. CDPOverwrite::NEVER). + */ + bool addStep(Node expected, + PfRule id, + const std::vector& children, + const std::vector& args, + bool ensureChildren = false, + CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY); + /** Version with ProofStep */ + bool addStep(Node expected, + const ProofStep& step, + bool ensureChildren = false, + CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY); + /** Version with ProofStepBuffer */ + bool addSteps(const ProofStepBuffer& psb, + bool ensureChildren = false, + CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY); + /** Add proof + * + * @param pn The proof of the given fact. + * @param opolicy Policy for whether to force overwriting if a step + * was already provided. This is used for each node in the proof if doCopy + * is true. + * @param doCopy Whether we make a deep copy of the pn. + * @return true if all steps were successfully added to this class. If it + * returns true, it registers a copy of all of the subnodes of pn to this + * proof class. + * + * If doCopy is true, this method is implemented by calling addStep above for + * all subnodes of pn, traversed as a dag. This means that fresh ProofNode + * objects may be generated by this call, and further modifications to pn do + * not impact the internal data of this class. + */ + bool addProof(std::shared_ptr pn, + CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY, + bool doCopy = false); + /** Return true if fact already has a proof step */ + bool hasStep(Node fact); + /** Get the proof manager for this proof */ + ProofNodeManager* getManager() const; + /** + * Is same? Returns true if f and g are the same formula, or if they are + * symmetric equalities. If two nodes f and g are the same, then a proof for + * f suffices as a proof for g according to this class. + */ + static bool isSame(TNode f, TNode g); + /** Get proof for fact, or nullptr if it does not exist. */ + std::shared_ptr getProof(Node fact) const; + /** + * Get symmetric fact (a g such that isSame returns true for isSame(f,g)), or + * null if none exist. + */ + static Node getSymmFact(TNode f); + /** identify */ + std::string identify() const override; + + protected: + typedef context::CDHashMap> NodeProofNodeMap; + /** The proof manager, used for allocating new ProofNode objects */ + ProofNodeManager* d_manager; + /** A dummy context used by this class if none is provided */ + context::Context d_context; + /** The nodes of the proof */ + NodeProofNodeMap d_nodes; + /** Name identifier */ + std::string d_name; + /** Whether we automatically add symmetry steps */ + bool d_autoSymm; + /** Ensure fact sym */ + std::shared_ptr getProofSymm(Node fact); + /** + * Returns true if we should overwrite proof node pn with a step having id + * newId, based on policy opol. + */ + static bool shouldOverwrite(ProofNode* pn, PfRule newId, CDPOverwrite opol); + /** Returns true if pn is an assumption. */ + static bool isAssumption(ProofNode* pn); + /** + * Notify new proof, called when a new proof of expected is provided. This is + * used internally to connect proofs of symmetric facts, when necessary. + */ + void notifyNewProof(Node expected); +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_MANAGER_H */ diff --git a/src/proof/proof_checker.cpp b/src/proof/proof_checker.cpp new file mode 100644 index 000000000..7a2e5293e --- /dev/null +++ b/src/proof/proof_checker.cpp @@ -0,0 +1,345 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof checker. + */ + +#include "proof/proof_checker.h" + +#include "expr/skolem_manager.h" +#include "options/proof_options.h" +#include "proof/proof_node.h" +#include "smt/smt_statistics_registry.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +Node ProofRuleChecker::check(PfRule id, + const std::vector& children, + const std::vector& args) +{ + // call instance-specific checkInternal method + return checkInternal(id, children, args); +} + +bool ProofRuleChecker::getUInt32(TNode n, uint32_t& i) +{ + // must be a non-negative integer constant that fits an unsigned int + if (n.isConst() && n.getType().isInteger() + && n.getConst().sgn() >= 0 + && n.getConst().getNumerator().fitsUnsignedInt()) + { + i = n.getConst().getNumerator().toUnsignedInt(); + return true; + } + return false; +} + +bool ProofRuleChecker::getBool(TNode n, bool& b) +{ + if (n.isConst() && n.getType().isBoolean()) + { + b = n.getConst(); + return true; + } + return false; +} + +bool ProofRuleChecker::getKind(TNode n, Kind& k) +{ + uint32_t i; + if (!getUInt32(n, i)) + { + return false; + } + k = static_cast(i); + return true; +} + +Node ProofRuleChecker::mkKindNode(Kind k) +{ + if (k == UNDEFINED_KIND) + { + // UNDEFINED_KIND is negative, hence return null to avoid cast + return Node::null(); + } + return NodeManager::currentNM()->mkConst(Rational(static_cast(k))); +} + +ProofCheckerStatistics::ProofCheckerStatistics() + : d_ruleChecks(smtStatisticsRegistry().registerHistogram( + "ProofCheckerStatistics::ruleChecks")), + d_totalRuleChecks(smtStatisticsRegistry().registerInt( + "ProofCheckerStatistics::totalRuleChecks")) +{ +} + +Node ProofChecker::check(ProofNode* pn, Node expected) +{ + return check(pn->getRule(), pn->getChildren(), pn->getArguments(), expected); +} + +Node ProofChecker::check( + PfRule id, + const std::vector>& children, + const std::vector& args, + Node expected) +{ + // optimization: immediately return for ASSUME + if (id == PfRule::ASSUME) + { + Assert(children.empty()); + Assert(args.size() == 1 && args[0].getType().isBoolean()); + Assert(expected.isNull() || expected == args[0]); + return expected; + } + // record stat + d_stats.d_ruleChecks << id; + ++d_stats.d_totalRuleChecks; + Trace("pfcheck") << "ProofChecker::check: " << id << std::endl; + std::vector cchildren; + for (const std::shared_ptr& pc : children) + { + Assert(pc != nullptr); + Node cres = pc->getResult(); + if (cres.isNull()) + { + Trace("pfcheck") << "ProofChecker::check: failed child" << std::endl; + Unreachable() + << "ProofChecker::check: child proof was invalid (null conclusion)" + << std::endl; + // should not have been able to create such a proof node + return Node::null(); + } + cchildren.push_back(cres); + if (Trace.isOn("pfcheck")) + { + std::stringstream ssc; + pc->printDebug(ssc); + Trace("pfcheck") << " child: " << ssc.str() << " : " << cres + << std::endl; + } + } + Trace("pfcheck") << " args: " << args << std::endl; + Trace("pfcheck") << " expected: " << expected << std::endl; + std::stringstream out; + // we use trusted (null) checkers here, since we want the proof generation to + // proceed without failing here. We always enable output since a failure + // implies that we will exit with the error message below. + Node res = checkInternal(id, cchildren, args, expected, out, true, true); + if (res.isNull()) + { + Trace("pfcheck") << "ProofChecker::check: failed" << std::endl; + Unreachable() << "ProofChecker::check: failed, " << out.str() << std::endl; + // it did not match the given expectation, fail + return Node::null(); + } + Trace("pfcheck") << "ProofChecker::check: success!" << std::endl; + return res; +} + +Node ProofChecker::checkDebug(PfRule id, + const std::vector& cchildren, + const std::vector& args, + Node expected, + const char* traceTag) +{ + std::stringstream out; + bool traceEnabled = Trace.isOn(traceTag); + // Since we are debugging, we want to treat trusted (null) checkers as + // a failure. We only enable output if the trace is enabled for efficiency. + Node res = + checkInternal(id, cchildren, args, expected, out, false, traceEnabled); + if (traceEnabled) + { + Trace(traceTag) << "ProofChecker::checkDebug: " << id; + if (res.isNull()) + { + Trace(traceTag) << " failed, " << out.str() << std::endl; + } + else + { + Trace(traceTag) << " success" << std::endl; + } + Trace(traceTag) << "cchildren: " << cchildren << std::endl; + Trace(traceTag) << " args: " << args << std::endl; + } + return res; +} + +Node ProofChecker::checkInternal(PfRule id, + const std::vector& cchildren, + const std::vector& args, + Node expected, + std::stringstream& out, + bool useTrustedChecker, + bool enableOutput) +{ + std::map::iterator it = d_checker.find(id); + if (it == d_checker.end()) + { + // no checker for the rule + if (enableOutput) + { + out << "no checker for rule " << id << std::endl; + } + return Node::null(); + } + else if (it->second == nullptr) + { + if (useTrustedChecker) + { + Notice() << "ProofChecker::check: trusting PfRule " << id << std::endl; + // trusted checker + return expected; + } + else + { + if (enableOutput) + { + out << "trusted checker for rule " << id << std::endl; + } + return Node::null(); + } + } + // check it with the corresponding checker + Node res = it->second->check(id, cchildren, args); + if (!expected.isNull()) + { + Node expectedw = expected; + if (res != expectedw) + { + if (enableOutput) + { + out << "result does not match expected value." << std::endl + << " PfRule: " << id << std::endl; + for (const Node& c : cchildren) + { + out << " child: " << c << std::endl; + } + for (const Node& a : args) + { + out << " arg: " << a << std::endl; + } + out << " result: " << res << std::endl + << " expected: " << expected << std::endl; + } + // it did not match the given expectation, fail + return Node::null(); + } + } + // fails if pedantic level is not met + if (options::proofEagerChecking()) + { + std::stringstream serr; + if (isPedanticFailure(id, serr, enableOutput)) + { + if (enableOutput) + { + out << serr.str() << std::endl; + if (Trace.isOn("proof-pedantic")) + { + Trace("proof-pedantic") + << "Failed pedantic check for " << id << std::endl; + Trace("proof-pedantic") << "Expected: " << expected << std::endl; + out << "Expected: " << expected << std::endl; + } + } + return Node::null(); + } + } + return res; +} + +void ProofChecker::registerChecker(PfRule id, ProofRuleChecker* psc) +{ + std::map::iterator it = d_checker.find(id); + if (it != d_checker.end()) + { + // checker is already provided + Notice() << "ProofChecker::registerChecker: checker already exists for " + << id << std::endl; + return; + } + d_checker[id] = psc; +} + +void ProofChecker::registerTrustedChecker(PfRule id, + ProofRuleChecker* psc, + uint32_t plevel) +{ + AlwaysAssert(plevel <= 10) << "ProofChecker::registerTrustedChecker: " + "pedantic level must be 0-10, got " + << plevel << " for " << id; + registerChecker(id, psc); + // overwrites if already there + if (d_plevel.find(id) != d_plevel.end()) + { + Notice() << "ProofChecker::registerTrustedRule: already provided pedantic " + "level for " + << id << std::endl; + } + d_plevel[id] = plevel; +} + +ProofRuleChecker* ProofChecker::getCheckerFor(PfRule id) +{ + std::map::const_iterator it = d_checker.find(id); + if (it == d_checker.end()) + { + return nullptr; + } + return it->second; +} + +uint32_t ProofChecker::getPedanticLevel(PfRule id) const +{ + std::map::const_iterator itp = d_plevel.find(id); + if (itp != d_plevel.end()) + { + return itp->second; + } + return 0; +} + +bool ProofChecker::isPedanticFailure(PfRule id, + std::ostream& out, + bool enableOutput) const +{ + if (d_pclevel == 0) + { + return false; + } + std::map::const_iterator itp = d_plevel.find(id); + if (itp != d_plevel.end()) + { + if (itp->second <= d_pclevel) + { + if (enableOutput) + { + out << "pedantic level for " << id << " not met (rule level is " + << itp->second << " which is at or below the pedantic level " + << d_pclevel << ")"; + bool pedanticTraceEnabled = Trace.isOn("proof-pedantic"); + if (!pedanticTraceEnabled) + { + out << ", use -t proof-pedantic for details"; + } + } + return true; + } + } + return false; +} + +} // namespace cvc5 diff --git a/src/proof/proof_checker.h b/src/proof/proof_checker.h new file mode 100644 index 000000000..ac5531bf4 --- /dev/null +++ b/src/proof/proof_checker.h @@ -0,0 +1,206 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof checker utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_CHECKER_H +#define CVC5__PROOF__PROOF_CHECKER_H + +#include + +#include "expr/node.h" +#include "proof/proof_rule.h" +#include "util/statistics_stats.h" + +namespace cvc5 { + +class ProofChecker; +class ProofNode; + +/** A virtual base class for checking a proof rule */ +class ProofRuleChecker +{ + public: + ProofRuleChecker() {} + virtual ~ProofRuleChecker() {} + /** + * This checks a single step in a proof. + * + * Return the formula that is proven by a proof node with the given id, + * premises and arguments, or null if such a proof node is not well-formed. + * + * Note that the input/output of this method expects to be terms in *Skolem + * form*, which is passed to checkInternal below. Rule checkers may + * convert premises to witness form when necessary. + * + * @param id The id of the proof node to check + * @param children The premises of the proof node to check. These are nodes + * corresponding to the conclusion (ProofNode::getResult) of the children + * of the proof node we are checking in Skolem form. + * @param args The arguments of the proof node to check + * @return The conclusion of the proof node, in Skolem form, if successful or + * null if such a proof node is malformed. + */ + Node check(PfRule id, + const std::vector& children, + const std::vector& args); + + /** get an index from a node, return false if we fail */ + static bool getUInt32(TNode n, uint32_t& i); + /** get a Boolean from a node, return false if we fail */ + static bool getBool(TNode n, bool& b); + /** get a Kind from a node, return false if we fail */ + static bool getKind(TNode n, Kind& k); + /** Make a Kind into a node */ + static Node mkKindNode(Kind k); + + /** Register all rules owned by this rule checker into pc. */ + virtual void registerTo(ProofChecker* pc) {} + + protected: + /** + * This checks a single step in a proof. + * + * @param id The id of the proof node to check + * @param children The premises of the proof node to check. These are nodes + * corresponding to the conclusion (ProofNode::getResult) of the children + * of the proof node we are checking. + * @param args The arguments of the proof node to check + * @return The conclusion of the proof node if successful or null if such a + * proof node is malformed. + */ + virtual Node checkInternal(PfRule id, + const std::vector& children, + const std::vector& args) = 0; +}; + +/** Statistics class */ +class ProofCheckerStatistics +{ + public: + ProofCheckerStatistics(); + /** Counts the number of checks for each kind of proof rule */ + HistogramStat d_ruleChecks; + /** Total number of rule checks */ + IntStat d_totalRuleChecks; +}; + +/** A class for checking proofs */ +class ProofChecker +{ + public: + ProofChecker(uint32_t pclevel = 0) : d_pclevel(pclevel) {} + ~ProofChecker() {} + /** + * Return the formula that is proven by proof node pn, or null if pn is not + * well-formed. If expected is non-null, then we return null if pn does not + * prove expected. + * + * @param pn The proof node to check + * @param expected The (optional) formula that is the expected conclusion of + * the proof node. + * @return The conclusion of the proof node if successful or null if the + * proof is malformed, or if no checker is available for id. + */ + Node check(ProofNode* pn, Node expected = Node::null()); + /** Same as above, with explicit arguments + * + * @param id The id of the proof node to check + * @param children The children of the proof node to check + * @param args The arguments of the proof node to check + * @param expected The (optional) formula that is the expected conclusion of + * the proof node. + * @return The conclusion of the proof node if successful or null if the + * proof is malformed, or if no checker is available for id. + */ + Node check(PfRule id, + const std::vector>& children, + const std::vector& args, + Node expected = Node::null()); + /** + * Same as above, without conclusions instead of proof node children. This + * is used for debugging. In particular, this function does not throw an + * assertion failure when a proof step is malformed and can be used without + * constructing proof nodes. + * + * @param id The id of the proof node to check + * @param children The conclusions of the children of the proof node to check + * @param args The arguments of the proof node to check + * @param expected The (optional) formula that is the expected conclusion of + * the proof node. + * @param traceTag The trace tag to print debug information to + * @return The conclusion of the proof node if successful or null if the + * proof is malformed, or if no checker is available for id. + */ + Node checkDebug(PfRule id, + const std::vector& cchildren, + const std::vector& args, + Node expected = Node::null(), + const char* traceTag = ""); + /** Indicate that psc is the checker for proof rule id */ + void registerChecker(PfRule id, ProofRuleChecker* psc); + /** + * Indicate that id is a trusted rule with the given pedantic level, e.g.: + * 0: (mandatory) always a failure to use the given id + * 1: (major) failure on all (non-zero) pedantic levels + * 10: (minor) failure only on pedantic levels >= 10. + */ + void registerTrustedChecker(PfRule id, + ProofRuleChecker* psc, + uint32_t plevel = 10); + /** get checker for */ + ProofRuleChecker* getCheckerFor(PfRule id); + + /** + * Get the pedantic level for id if it has been assigned a pedantic + * level via registerTrustedChecker above, or zero otherwise. + */ + uint32_t getPedanticLevel(PfRule id) const; + + /** + * Is pedantic failure? If so, we return true and write a debug message on the + * output stream out if enableOutput is true. + */ + bool isPedanticFailure(PfRule id, + std::ostream& out, + bool enableOutput = true) const; + + private: + /** statistics class */ + ProofCheckerStatistics d_stats; + /** Maps proof rules to their checker */ + std::map d_checker; + /** Maps proof trusted rules to their pedantic level */ + std::map d_plevel; + /** The pedantic level of this checker */ + uint32_t d_pclevel; + /** + * Check internal. This is used by check and checkDebug above. It writes + * checking errors on out when enableOutput is true. We treat trusted checkers + * (nullptr in the range of the map d_checker) as failures if + * useTrustedChecker = false. + */ + Node checkInternal(PfRule id, + const std::vector& cchildren, + const std::vector& args, + Node expected, + std::stringstream& out, + bool useTrustedChecker, + bool enableOutput); +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_CHECKER_H */ diff --git a/src/proof/proof_ensure_closed.cpp b/src/proof/proof_ensure_closed.cpp new file mode 100644 index 000000000..774b25ef6 --- /dev/null +++ b/src/proof/proof_ensure_closed.cpp @@ -0,0 +1,183 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of debug checks for ensuring proofs are closed. + */ + +#include "proof/proof_ensure_closed.h" + +#include + +#include "options/proof_options.h" +#include "options/smt_options.h" +#include "proof/proof_generator.h" +#include "proof/proof_node.h" +#include "proof/proof_node_algorithm.h" + +namespace cvc5 { + +/** + * Ensure closed with respect to assumptions, internal version, which + * generalizes the check for a proof generator or a proof node. + */ +void ensureClosedWrtInternal(Node proven, + ProofGenerator* pg, + ProofNode* pnp, + const std::vector& assumps, + const char* c, + const char* ctx, + bool reqGen) +{ + if (!options::produceProofs()) + { + // proofs not enabled, do not do check + return; + } + bool isTraceDebug = Trace.isOn(c); + if (!options::proofEagerChecking() && !isTraceDebug) + { + // trace is off and proof new eager checking is off, do not do check + return; + } + std::stringstream sdiag; + bool isTraceOn = Trace.isOn(c); + if (!isTraceOn) + { + sdiag << ", use -t " << c << " for details"; + } + bool dumpProofTraceOn = Trace.isOn("dump-proof-error"); + if (!dumpProofTraceOn) + { + sdiag << ", use -t dump-proof-error for details on proof"; + } + // get the proof node in question, which is either provided or built by the + // proof generator + std::shared_ptr pn; + std::stringstream ss; + if (pnp != nullptr) + { + Assert(pg == nullptr); + ss << "ProofNode in context " << ctx; + } + else + { + ss << "ProofGenerator: " << (pg == nullptr ? "null" : pg->identify()) + << " in context " << ctx; + if (pg == nullptr) + { + // only failure if flag is true + if (reqGen) + { + Unreachable() << "...ensureClosed: no generator in context " << ctx + << sdiag.str(); + } + } + else + { + Assert(!proven.isNull()); + pn = pg->getProofFor(proven); + pnp = pn.get(); + } + } + Trace(c) << "=== ensureClosed: " << ss.str() << std::endl; + Trace(c) << "Proven: " << proven << std::endl; + if (pnp == nullptr) + { + if (pg == nullptr) + { + // did not require generator + Assert(!reqGen); + Trace(c) << "...ensureClosed: no generator in context " << ctx + << std::endl; + return; + } + } + // if we don't have a proof node, a generator failed + if (pnp == nullptr) + { + Assert(pg != nullptr); + AlwaysAssert(false) << "...ensureClosed: null proof from " << ss.str() + << sdiag.str(); + } + std::vector fassumps; + expr::getFreeAssumptions(pnp, fassumps); + bool isClosed = true; + std::stringstream ssf; + for (const Node& fa : fassumps) + { + if (std::find(assumps.begin(), assumps.end(), fa) == assumps.end()) + { + isClosed = false; + ssf << "- " << fa << std::endl; + } + } + if (!isClosed) + { + Trace(c) << "Free assumptions:" << std::endl; + Trace(c) << ssf.str(); + if (!assumps.empty()) + { + Trace(c) << "Expected assumptions:" << std::endl; + for (const Node& a : assumps) + { + Trace(c) << "- " << a << std::endl; + } + } + if (dumpProofTraceOn) + { + Trace("dump-proof-error") << " Proof: " << *pnp << std::endl; + } + } + AlwaysAssert(isClosed) << "...ensureClosed: open proof from " << ss.str() + << sdiag.str(); + Trace(c) << "...ensureClosed: success" << std::endl; + Trace(c) << "====" << std::endl; +} + +void pfgEnsureClosed(Node proven, + ProofGenerator* pg, + const char* c, + const char* ctx, + bool reqGen) +{ + Assert(!proven.isNull()); + // proof generator may be null + std::vector assumps; + ensureClosedWrtInternal(proven, pg, nullptr, assumps, c, ctx, reqGen); +} + +void pfgEnsureClosedWrt(Node proven, + ProofGenerator* pg, + const std::vector& assumps, + const char* c, + const char* ctx, + bool reqGen) +{ + Assert(!proven.isNull()); + // proof generator may be null + ensureClosedWrtInternal(proven, pg, nullptr, assumps, c, ctx, reqGen); +} + +void pfnEnsureClosed(ProofNode* pn, const char* c, const char* ctx) +{ + ensureClosedWrtInternal(Node::null(), nullptr, pn, {}, c, ctx, false); +} + +void pfnEnsureClosedWrt(ProofNode* pn, + const std::vector& assumps, + const char* c, + const char* ctx) +{ + ensureClosedWrtInternal(Node::null(), nullptr, pn, assumps, c, ctx, false); +} + +} // namespace cvc5 diff --git a/src/proof/proof_ensure_closed.h b/src/proof/proof_ensure_closed.h new file mode 100644 index 000000000..cacfeeeed --- /dev/null +++ b/src/proof/proof_ensure_closed.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Debug checks for ensuring proofs are closed. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_ENSURE_CLOSED_H +#define CVC5__PROOF__PROOF_ENSURE_CLOSED_H + +#include "expr/node.h" + +namespace cvc5 { + +class ProofGenerator; +class ProofNode; + +/** + * Debug check closed on Trace c. Context ctx is string for debugging. + * This method throws an assertion failure if pg cannot provide a closed + * proof for fact proven. This is checked only if --proof-eager-checking + * is enabled or the Trace c is enabled. + * + * @param reqGen Whether we consider a null generator to be a failure. + */ +void pfgEnsureClosed(Node proven, + ProofGenerator* pg, + const char* c, + const char* ctx, + bool reqGen = true); + +/** + * Debug check closed with Trace c. Context ctx is string for debugging and + * assumps is the set of allowed open assertions. This method throws an + * assertion failure if pg cannot provide a proof for fact proven whose + * free assumptions are contained in assumps. + * + * @param reqGen Whether we consider a null generator to be a failure. + */ +void pfgEnsureClosedWrt(Node proven, + ProofGenerator* pg, + const std::vector& assumps, + const char* c, + const char* ctx, + bool reqGen = true); + +/** + * Debug check closed with Trace c, proof node versions. This gives an + * assertion failure if pn is not closed. Detailed information is printed + * on trace c. Context ctx is a string used for debugging. + */ +void pfnEnsureClosed(ProofNode* pn, const char* c, const char* ctx); +/** + * Same as above, but throws an assertion failure only if the free assumptions + * of pn are not contained in assumps. + */ +void pfnEnsureClosedWrt(ProofNode* pn, + const std::vector& assumps, + const char* c, + const char* ctx); +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_ENSURE_CLOSED_H */ diff --git a/src/proof/proof_generator.cpp b/src/proof/proof_generator.cpp new file mode 100644 index 000000000..bbfde7986 --- /dev/null +++ b/src/proof/proof_generator.cpp @@ -0,0 +1,77 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof generator utility. + */ + +#include "proof/proof_generator.h" + +#include + +#include "options/smt_options.h" +#include "proof/proof.h" +#include "proof/proof_node.h" +#include "proof/proof_node_algorithm.h" + +namespace cvc5 { + +std::ostream& operator<<(std::ostream& out, CDPOverwrite opol) +{ + switch (opol) + { + case CDPOverwrite::ALWAYS: out << "ALWAYS"; break; + case CDPOverwrite::ASSUME_ONLY: out << "ASSUME_ONLY"; break; + case CDPOverwrite::NEVER: out << "NEVER"; break; + default: out << "CDPOverwrite:unknown"; break; + } + return out; +} + +ProofGenerator::ProofGenerator() {} + +ProofGenerator::~ProofGenerator() {} + +std::shared_ptr ProofGenerator::getProofFor(Node f) +{ + Unreachable() << "ProofGenerator::getProofFor: " << identify() + << " has no implementation" << std::endl; + return nullptr; +} + +bool ProofGenerator::addProofTo(Node f, + CDProof* pf, + CDPOverwrite opolicy, + bool doCopy) +{ + Trace("pfgen") << "ProofGenerator::addProofTo: " << f << "..." << std::endl; + Assert(pf != nullptr); + // plug in the proof provided by the generator, if it exists + std::shared_ptr apf = getProofFor(f); + if (apf != nullptr) + { + Trace("pfgen") << "...got proof " << *apf.get() << std::endl; + if (pf->addProof(apf, opolicy, doCopy)) + { + Trace("pfgen") << "...success!" << std::endl; + return true; + } + Trace("pfgen") << "...failed to add proof" << std::endl; + } + else + { + Trace("pfgen") << "...failed, no proof" << std::endl; + Assert(false) << "Failed to get proof from generator for fact " << f; + } + return false; +} + +} // namespace cvc5 diff --git a/src/proof/proof_generator.h b/src/proof/proof_generator.h new file mode 100644 index 000000000..a8fe43909 --- /dev/null +++ b/src/proof/proof_generator.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * The abstract proof generator class. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_GENERATOR_H +#define CVC5__PROOF__PROOF_GENERATOR_H + +#include "expr/node.h" + +namespace cvc5 { + +class CDProof; +class ProofNode; + +/** An overwrite policy for CDProof */ +enum class CDPOverwrite : uint32_t +{ + // always overwrite an existing step. + ALWAYS, + // overwrite ASSUME with non-ASSUME steps. + ASSUME_ONLY, + // never overwrite an existing step. + NEVER, +}; +/** Writes a overwrite policy name to a stream. */ +std::ostream& operator<<(std::ostream& out, CDPOverwrite opol); + +/** + * An abstract proof generator class. + * + * A proof generator is intended to be used as a utility e.g. in theory + * solvers for constructing and storing proofs internally. A theory may have + * multiple instances of ProofGenerator objects, e.g. if it has more than one + * way of justifying lemmas or conflicts. + * + * A proof generator has two main interfaces for generating proofs: + * (1) getProofFor, and (2) addProofTo. The latter is optional. + * + * The addProofTo method can be used as an optimization for avoiding + * the construction of the ProofNode for a given fact. + * + * If no implementation of addProofTo is provided, then addProofTo(f, pf) + * calls getProofFor(f) and links the topmost ProofNode of the returned proof + * into pf. Note this top-most ProofNode can be avoided in the addProofTo + * method. + */ +class ProofGenerator +{ + public: + ProofGenerator(); + virtual ~ProofGenerator(); + /** Get the proof for formula f + * + * This forces the proof generator to construct a proof for formula f and + * return it. If this is an "eager" proof generator, this function is expected + * to be implemented as a map lookup. If this is a "lazy" proof generator, + * this function is expected to invoke a proof producing procedure of the + * generator. + * + * It should be the case that hasProofFor(f) is true. + * + * @param f The fact to get the proof for. + * @return The proof for f. + */ + virtual std::shared_ptr getProofFor(Node f); + /** + * Add the proof for formula f to proof pf. The proof of f is overwritten in + * pf based on the policy opolicy. + * + * @param f The fact to get the proof for. + * @param pf The CDProof object to add the proof to. + * @param opolicy The overwrite policy for adding to pf. + * @param doCopy Whether to do a deep copy of the proof steps into pf. + * @return True if this call was sucessful. + */ + virtual bool addProofTo(Node f, + CDProof* pf, + CDPOverwrite opolicy = CDPOverwrite::ASSUME_ONLY, + bool doCopy = false); + /** + * Can we give the proof for formula f? This is used for debugging. This + * returns false if the generator cannot provide a proof of formula f. + * + * Also notice that this function does not require the proof for f to be + * constructed at the time of this call. Thus, if this is a "lazy" proof + * generator, it is expected that this call is implemented as a map lookup + * into the bookkeeping maintained by the generator only. + * + * Notice the default return value is true. In other words, a proof generator + * may choose to override this function to verify the construction, although + * we do not insist this is the case. + */ + virtual bool hasProofFor(Node f) { return true; } + /** Identify this generator (for debugging, etc..) */ + virtual std::string identify() const = 0; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_GENERATOR_H */ diff --git a/src/proof/proof_node.cpp b/src/proof/proof_node.cpp new file mode 100644 index 000000000..e3affb306 --- /dev/null +++ b/src/proof/proof_node.cpp @@ -0,0 +1,72 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof node utility. + */ + +#include "proof/proof_node.h" + +#include "proof/proof_node_algorithm.h" +#include "proof/proof_node_to_sexpr.h" + +namespace cvc5 { + +ProofNode::ProofNode(PfRule id, + const std::vector>& children, + const std::vector& args) +{ + setValue(id, children, args); +} + +PfRule ProofNode::getRule() const { return d_rule; } + +const std::vector>& ProofNode::getChildren() const +{ + return d_children; +} + +const std::vector& ProofNode::getArguments() const { return d_args; } + +Node ProofNode::getResult() const { return d_proven; } + +bool ProofNode::isClosed() +{ + std::vector assumps; + expr::getFreeAssumptions(this, assumps); + return assumps.empty(); +} + +void ProofNode::setValue( + PfRule id, + const std::vector>& children, + const std::vector& args) +{ + d_rule = id; + d_children = children; + d_args = args; +} + +void ProofNode::printDebug(std::ostream& os) const +{ + // convert to sexpr and print + ProofNodeToSExpr pnts; + Node ps = pnts.convertToSExpr(this); + os << ps; +} + +std::ostream& operator<<(std::ostream& out, const ProofNode& pn) +{ + pn.printDebug(out); + return out; +} + +} // namespace cvc5 diff --git a/src/proof/proof_node.h b/src/proof/proof_node.h new file mode 100644 index 000000000..db82cc63d --- /dev/null +++ b/src/proof/proof_node.h @@ -0,0 +1,145 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Alex Ozdemir + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof node utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_NODE_H +#define CVC5__PROOF__PROOF_NODE_H + +#include + +#include "expr/node.h" +#include "proof/proof_rule.h" + +namespace cvc5 { + +class ProofNodeManager; +class ProofNode; + +// Alias for shared pointer to a proof node +using Pf = std::shared_ptr; + +struct ProofNodeHashFunction +{ + inline size_t operator()(std::shared_ptr pfn) const; +}; /* struct ProofNodeHashFunction */ + +/** A node in a proof + * + * A ProofNode represents a single step in a proof. It contains: + * (1) d_id, an identifier indicating the kind of inference, + * (2) d_children, the child ProofNode objects indicating its premises, + * (3) d_args, additional arguments used to determine the conclusion, + * (4) d_proven, cache of the formula that this ProofNode proves. + * + * Overall, a ProofNode and its children form a directed acyclic graph. + * + * A ProofNode is partially mutable in that (1), (2) and (3) can be + * modified. A motivating example of when this is useful is when a ProofNode + * is established to be a "hole" for something to be proven later. On the other + * hand, (4) is intended to be immutable. + * + * The method setValue is private and can be called by objects that manage + * ProofNode objects in trusted ways that ensure that the node maintains + * the invariant above. Furthermore, notice that this class is not responsible + * for setting d_proven; this is done externally by a ProofNodeManager class. + * + * Notice that all fields of ProofNode are stored in ***Skolem form***. Their + * correctness is checked in ***witness form*** (for details on this + * terminology, see expr/skolem_manager.h). As a simple example, say a + * theory solver has a term t, and wants to introduce a unit lemma (= k t) + * where k is a fresh Skolem variable. It creates this variable via: + * k = SkolemManager::mkPurifySkolem(t,"k"); + * A checked ProofNode for the fact (= k t) then may have fields: + * d_rule := MACRO_SR_PRED_INTRO, + * d_children := {}, + * d_args := {(= k t)} + * d_proven := (= k t). + * Its justification via the rule MACRO_SR_PRED_INTRO (see documentation + * in theory/builtin/proof_kinds) is in terms of the witness form of the + * argument: + * (= (witness ((z T)) (= z t)) t) + * which, by that rule's side condition, is such that: + * Rewriter::rewrite((= (witness ((z T)) (= z t)) t)) = true. + * Notice that the correctness of the rule is justified here by rewriting + * the witness form of (= k t). The conversion to/from witness form is + * managed by ProofRuleChecker::check. + * + * An external proof checker is expected to formalize the ProofNode only in + * terms of *witness* forms. + * + * However, the rest of cvc5 sees only the *Skolem* form of arguments and + * conclusions in ProofNode, since this is what is used throughout cvc5. + */ +class ProofNode +{ + friend class ProofNodeManager; + + public: + ProofNode(PfRule id, + const std::vector>& children, + const std::vector& args); + ~ProofNode() {} + /** get the rule of this proof node */ + PfRule getRule() const; + /** Get children */ + const std::vector>& getChildren() const; + /** Get arguments */ + const std::vector& getArguments() const; + /** get what this node proves, or the null node if this is an invalid proof */ + Node getResult() const; + /** + * Returns true if this is a closed proof (i.e. it has no free assumptions). + */ + bool isClosed(); + /** Print debug on output strem os */ + void printDebug(std::ostream& os) const; + + private: + /** + * Set value, called to overwrite the contents of this ProofNode with the + * given arguments. + */ + void setValue(PfRule id, + const std::vector>& children, + const std::vector& args); + /** The proof rule */ + PfRule d_rule; + /** The children of this proof node */ + std::vector> d_children; + /** arguments of this node */ + std::vector d_args; + /** The cache of the fact that has been proven, modifiable by ProofChecker */ + Node d_proven; +}; + +inline size_t ProofNodeHashFunction::operator()( + std::shared_ptr pfn) const +{ + return pfn->getResult().getId() + static_cast(pfn->getRule()); +} + +/** + * Serializes a given proof node to the given stream. + * + * @param out the output stream to use + * @param pn the proof node to output to the stream + * @return the stream + */ +std::ostream& operator<<(std::ostream& out, const ProofNode& pn); + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_NODE_H */ diff --git a/src/proof/proof_node_algorithm.cpp b/src/proof/proof_node_algorithm.cpp new file mode 100644 index 000000000..82ccc034f --- /dev/null +++ b/src/proof/proof_node_algorithm.cpp @@ -0,0 +1,176 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof node algorithm utilities. + */ + +#include "proof/proof_node_algorithm.h" + +#include "proof/proof_node.h" + +namespace cvc5 { +namespace expr { + +void getFreeAssumptions(ProofNode* pn, std::vector& assump) +{ + std::map>> amap; + std::shared_ptr spn = std::make_shared( + pn->getRule(), pn->getChildren(), pn->getArguments()); + getFreeAssumptionsMap(spn, amap); + for (const std::pair>>& p : + amap) + { + assump.push_back(p.first); + } +} + +void getFreeAssumptionsMap( + std::shared_ptr pn, + std::map>>& amap) +{ + // proof should not be cyclic + // visited set false after preorder traversal, true after postorder traversal + std::unordered_map visited; + std::unordered_map::iterator it; + std::vector> visit; + std::vector> traversing; + // Maps a bound assumption to the number of bindings it is under + // e.g. in (SCOPE (SCOPE (ASSUME x) (x y)) (y)), y would be mapped to 2 at + // (ASSUME x), and x would be mapped to 1. + // + // This map is used to track which nodes are in scope while traversing the + // DAG. The in-scope assumptions are keys in the map. They're removed when + // their binding count drops to zero. Let's annotate the above example to + // serve as an illustration: + // + // (SCOPE0 (SCOPE1 (ASSUME x) (x y)) (y)) + // + // This is how the map changes during the traversal: + // after previsiting SCOPE0: { y: 1 } + // after previsiting SCOPE1: { y: 2, x: 1 } + // at ASSUME: { y: 2, x: 1 } (so x is in scope!) + // after postvisiting SCOPE1: { y: 1 } + // after postvisiting SCOPE2: {} + // + std::unordered_map scopeDepth; + std::shared_ptr cur; + visit.push_back(pn); + do + { + cur = visit.back(); + visit.pop_back(); + it = visited.find(cur.get()); + const std::vector& cargs = cur->getArguments(); + if (it == visited.end()) + { + PfRule id = cur->getRule(); + if (id == PfRule::ASSUME) + { + visited[cur.get()] = true; + Assert(cargs.size() == 1); + Node f = cargs[0]; + if (!scopeDepth.count(f)) + { + amap[f].push_back(cur); + } + } + else + { + if (id == PfRule::SCOPE) + { + // mark that its arguments are bound in the current scope + for (const Node& a : cargs) + { + scopeDepth[a] += 1; + } + // will need to unbind the variables below + } + // The following loop cannot be merged with the loop above because the + // same subproof + visited[cur.get()] = false; + visit.push_back(cur); + traversing.push_back(cur); + const std::vector>& cs = cur->getChildren(); + for (const std::shared_ptr& cp : cs) + { + if (std::find(traversing.begin(), traversing.end(), cp) + != traversing.end()) + { + Unhandled() << "getFreeAssumptionsMap: cyclic proof! (use " + "--proof-eager-checking)" + << std::endl; + } + visit.push_back(cp); + } + } + } + else if (!it->second) + { + Assert(!traversing.empty()); + traversing.pop_back(); + visited[cur.get()] = true; + if (cur->getRule() == PfRule::SCOPE) + { + // unbind its assumptions + for (const Node& a : cargs) + { + auto scopeCt = scopeDepth.find(a); + Assert(scopeCt != scopeDepth.end()); + scopeCt->second -= 1; + if (scopeCt->second == 0) + { + scopeDepth.erase(scopeCt); + } + } + } + } + } while (!visit.empty()); +} + +bool containsSubproof(ProofNode* pn, ProofNode* pnc) +{ + std::unordered_set visited; + return containsSubproof(pn, pnc, visited); +} + +bool containsSubproof(ProofNode* pn, + ProofNode* pnc, + std::unordered_set& visited) +{ + std::unordered_map::iterator it; + std::vector visit; + visit.push_back(pn); + const ProofNode* cur; + while (!visit.empty()) + { + cur = visit.back(); + visit.pop_back(); + if (visited.find(cur) == visited.end()) + { + visited.insert(cur); + if (cur == pnc) + { + return true; + } + const std::vector>& children = + cur->getChildren(); + for (const std::shared_ptr& cp : children) + { + visit.push_back(cp.get()); + } + } + } + return false; +} + +} // namespace expr +} // namespace cvc5 diff --git a/src/proof/proof_node_algorithm.h b/src/proof/proof_node_algorithm.h new file mode 100644 index 000000000..1ccefb0a2 --- /dev/null +++ b/src/proof/proof_node_algorithm.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof node algorithm utilities. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_NODE_ALGORITHM_H +#define CVC5__PROOF__PROOF_NODE_ALGORITHM_H + +#include + +#include "expr/node.h" + +namespace cvc5 { + +class ProofNode; + +namespace expr { + +/** + * This adds to the vector assump all formulas that are "free assumptions" of + * the proof whose root is ProofNode pn. A free assumption is a formula F + * that is an argument (in d_args) of a ProofNode whose kind is ASSUME, and + * that proof node is not beneath an application of SCOPE containing F as an + * argument. + * + * This traverses the structure of the dag represented by this ProofNode. + * Its implementation is analogous to expr::getFreeVariables. + * + * @param pn The proof node. + * @param assump The vector to add the free asumptions of pn to. + */ +void getFreeAssumptions(ProofNode* pn, std::vector& assump); +/** + * Same as above, but maps assumptions to the proof pointer(s) for that + * assumption. Notice that depending on how pn is constructed, there may be + * multiple ProofNode that are ASSUME proofs of the same assumption, which + * are each added to the range of the assumption. + * + * @param pn The proof node. + * @param amap The mapping to add the free asumptions of pn and their + * corresponding proof nodes to. + */ +void getFreeAssumptionsMap( + std::shared_ptr pn, + std::map>>& amap); + +/** + * @return true if pn contains pnc. + */ +bool containsSubproof(ProofNode* pn, ProofNode* pnc); + +/** + * Same as above, with a visited cache. + * + * @return true if pn contains pnc. + */ +bool containsSubproof(ProofNode* pn, + ProofNode* pnc, + std::unordered_set& visited); + +} // namespace expr +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_NODE_ALGORITHM_H */ diff --git a/src/proof/proof_node_manager.cpp b/src/proof/proof_node_manager.cpp new file mode 100644 index 000000000..cf19eebdf --- /dev/null +++ b/src/proof/proof_node_manager.cpp @@ -0,0 +1,407 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof node manager. + */ + +#include "proof/proof_node_manager.h" + +#include + +#include "options/proof_options.h" +#include "proof/proof.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" +#include "proof/proof_node_algorithm.h" +#include "theory/rewriter.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +ProofNodeManager::ProofNodeManager(ProofChecker* pc) : d_checker(pc) +{ + d_true = NodeManager::currentNM()->mkConst(true); +} + +std::shared_ptr ProofNodeManager::mkNode( + PfRule id, + const std::vector>& children, + const std::vector& args, + Node expected) +{ + Trace("pnm") << "ProofNodeManager::mkNode " << id << " {" << expected.getId() + << "} " << expected << "\n"; + Node res = checkInternal(id, children, args, expected); + if (res.isNull()) + { + // if it was invalid, then we return the null node + return nullptr; + } + // otherwise construct the proof node and set its proven field + std::shared_ptr pn = + std::make_shared(id, children, args); + pn->d_proven = res; + return pn; +} + +std::shared_ptr ProofNodeManager::mkAssume(Node fact) +{ + Assert(!fact.isNull()); + Assert(fact.getType().isBoolean()); + return mkNode(PfRule::ASSUME, {}, {fact}, fact); +} + +std::shared_ptr ProofNodeManager::mkTrans( + const std::vector>& children, Node expected) +{ + Assert(!children.empty()); + if (children.size() == 1) + { + Assert(expected.isNull() || children[0]->getResult() == expected); + return children[0]; + } + return mkNode(PfRule::TRANS, children, {}, expected); +} + +std::shared_ptr ProofNodeManager::mkScope( + std::shared_ptr pf, + std::vector& assumps, + bool ensureClosed, + bool doMinimize, + Node expected) +{ + if (!ensureClosed) + { + return mkNode(PfRule::SCOPE, {pf}, assumps, expected); + } + Trace("pnm-scope") << "ProofNodeManager::mkScope " << assumps << std::endl; + // we first ensure the assumptions are flattened + std::unordered_set ac{assumps.begin(), assumps.end()}; + // map from the rewritten form of assumptions to the original. This is only + // computed in the rare case when we need rewriting to match the + // assumptions. An example of this is for Boolean constant equalities in + // scoped proofs from the proof equality engine. + std::unordered_map acr; + // whether we have compute the map above + bool computedAcr = false; + + // The free assumptions of the proof + std::map>> famap; + expr::getFreeAssumptionsMap(pf, famap); + std::unordered_set acu; + for (const std::pair>>& + fa : famap) + { + Node a = fa.first; + if (ac.find(a) != ac.end()) + { + // already covered by an assumption + acu.insert(a); + continue; + } + // trivial assumption + if (a == d_true) + { + Trace("pnm-scope") << "- justify trivial True assumption\n"; + for (std::shared_ptr pfs : fa.second) + { + Assert(pfs->getResult() == a); + updateNode(pfs.get(), PfRule::MACRO_SR_PRED_INTRO, {}, {a}); + } + Trace("pnm-scope") << "...finished" << std::endl; + acu.insert(a); + continue; + } + Trace("pnm-scope") << "- try matching free assumption " << a << "\n"; + // otherwise it may be due to symmetry? + Node aeqSym = CDProof::getSymmFact(a); + Trace("pnm-scope") << " - try sym " << aeqSym << "\n"; + Node aMatch; + if (!aeqSym.isNull() && ac.count(aeqSym)) + { + Trace("pnm-scope") << "- reorient assumption " << aeqSym << " via " << a + << " for " << fa.second.size() << " proof nodes" + << std::endl; + aMatch = aeqSym; + } + else + { + // Otherwise, may be derivable by rewriting. Note this is used in + // ensuring that proofs from the proof equality engine that involve + // equality with Boolean constants are closed. + if (!computedAcr) + { + computedAcr = true; + for (const Node& acc : ac) + { + Node accr = theory::Rewriter::rewrite(acc); + if (accr != acc) + { + acr[accr] = acc; + } + } + } + Node ar = theory::Rewriter::rewrite(a); + std::unordered_map::iterator itr = acr.find(ar); + if (itr != acr.end()) + { + aMatch = itr->second; + } + } + + // if we found a match either by symmetry or rewriting, then we connect + // the assumption here. + if (!aMatch.isNull()) + { + std::shared_ptr pfaa = mkAssume(aMatch); + // must correct the orientation on this leaf + std::vector> children; + children.push_back(pfaa); + for (std::shared_ptr pfs : fa.second) + { + Assert(pfs->getResult() == a); + // use SYMM if possible + if (aMatch == aeqSym) + { + updateNode(pfs.get(), PfRule::SYMM, children, {}); + } + else + { + updateNode(pfs.get(), PfRule::MACRO_SR_PRED_TRANSFORM, children, {a}); + } + } + Trace("pnm-scope") << "...finished" << std::endl; + acu.insert(aMatch); + continue; + } + // If we did not find a match, it is an error, since all free assumptions + // should be arguments to SCOPE. + std::stringstream ss; + + bool dumpProofTraceOn = Trace.isOn("dump-proof-error"); + if (dumpProofTraceOn) + { + ss << "The proof : " << *pf << std::endl; + } + ss << std::endl << "Free assumption: " << a << std::endl; + for (const Node& aprint : ac) + { + ss << "- assumption: " << aprint << std::endl; + } + if (!dumpProofTraceOn) + { + ss << "Use -t dump-proof-error for details on proof" << std::endl; + } + Unreachable() << "Generated a proof that is not closed by the scope: " + << ss.str() << std::endl; + } + if (acu.size() < ac.size()) + { + // All assumptions should match a free assumption; if one does not, then + // the explanation could have been smaller. + for (const Node& a : ac) + { + if (acu.find(a) == acu.end()) + { + Notice() << "ProofNodeManager::mkScope: assumption " << a + << " does not match a free assumption in proof" << std::endl; + } + } + } + if (doMinimize && acu.size() < ac.size()) + { + assumps.clear(); + assumps.insert(assumps.end(), acu.begin(), acu.end()); + } + else if (ac.size() < assumps.size()) + { + // remove duplicates to avoid redundant literals in clauses + assumps.clear(); + assumps.insert(assumps.end(), ac.begin(), ac.end()); + } + Node minExpected; + NodeManager* nm = NodeManager::currentNM(); + Node exp; + if (assumps.empty()) + { + // SCOPE with no arguments is a no-op, just return original + return pf; + } + Node conc = pf->getResult(); + exp = assumps.size() == 1 ? assumps[0] : nm->mkNode(AND, assumps); + if (conc.isConst() && !conc.getConst()) + { + minExpected = exp.notNode(); + } + else + { + minExpected = nm->mkNode(IMPLIES, exp, conc); + } + return mkNode(PfRule::SCOPE, {pf}, assumps, minExpected); +} + +bool ProofNodeManager::updateNode( + ProofNode* pn, + PfRule id, + const std::vector>& children, + const std::vector& args) +{ + return updateNodeInternal(pn, id, children, args, true); +} + +bool ProofNodeManager::updateNode(ProofNode* pn, ProofNode* pnr) +{ + Assert(pn != nullptr); + Assert(pnr != nullptr); + if (pn->getResult() != pnr->getResult()) + { + return false; + } + // can shortcut re-check of rule + return updateNodeInternal( + pn, pnr->getRule(), pnr->getChildren(), pnr->getArguments(), false); +} + +Node ProofNodeManager::checkInternal( + PfRule id, + const std::vector>& children, + const std::vector& args, + Node expected) +{ + Node res; + if (d_checker) + { + // check with the checker, which takes expected as argument + res = d_checker->check(id, children, args, expected); + Assert(!res.isNull()) + << "ProofNodeManager::checkInternal: failed to check proof"; + } + else + { + // otherwise we trust the expected value, if it exists + Assert(!expected.isNull()) << "ProofNodeManager::checkInternal: no checker " + "or expected value provided"; + res = expected; + } + return res; +} + +ProofChecker* ProofNodeManager::getChecker() const { return d_checker; } + +std::shared_ptr ProofNodeManager::clone( + std::shared_ptr pn) +{ + const ProofNode* orig = pn.get(); + std::unordered_map> visited; + std::unordered_map>::iterator it; + std::vector visit; + std::shared_ptr cloned; + visit.push_back(orig); + const ProofNode* cur; + while (!visit.empty()) + { + cur = visit.back(); + it = visited.find(cur); + if (it == visited.end()) + { + visited[cur] = nullptr; + const std::vector>& children = + cur->getChildren(); + for (const std::shared_ptr& cp : children) + { + visit.push_back(cp.get()); + } + continue; + } + visit.pop_back(); + if (it->second.get() == nullptr) + { + std::vector> cchildren; + const std::vector>& children = + cur->getChildren(); + for (const std::shared_ptr& cp : children) + { + it = visited.find(cp.get()); + Assert(it != visited.end()); + Assert(it->second != nullptr); + cchildren.push_back(it->second); + } + cloned = std::make_shared( + cur->getRule(), cchildren, cur->getArguments()); + visited[cur] = cloned; + // we trust the above cloning does not change what is proven + cloned->d_proven = cur->d_proven; + } + } + Assert(visited.find(orig) != visited.end()); + return visited[orig]; +} + +bool ProofNodeManager::updateNodeInternal( + ProofNode* pn, + PfRule id, + const std::vector>& children, + const std::vector& args, + bool needsCheck) +{ + Assert(pn != nullptr); + // ---------------- check for cyclic + if (options::proofEagerChecking()) + { + std::unordered_set visited; + for (const std::shared_ptr& cpc : children) + { + if (expr::containsSubproof(cpc.get(), pn, visited)) + { + std::stringstream ss; + ss << "ProofNodeManager::updateNode: attempting to make cyclic proof! " + << id << " " << pn->getResult() << ", children = " << std::endl; + for (const std::shared_ptr& cp : children) + { + ss << " " << cp->getRule() << " " << cp->getResult() << std::endl; + } + ss << "Full children:" << std::endl; + for (const std::shared_ptr& cp : children) + { + ss << " - "; + cp->printDebug(ss); + ss << std::endl; + } + Unreachable() << ss.str(); + } + } + } + // ---------------- end check for cyclic + + // should have already computed what is proven + Assert(!pn->d_proven.isNull()) + << "ProofNodeManager::updateProofNode: invalid proof provided"; + if (needsCheck) + { + // We expect to prove the same thing as before + Node res = checkInternal(id, children, args, pn->d_proven); + if (res.isNull()) + { + // if it was invalid, then we do not update + return false; + } + // proven field should already be the same as the result + Assert(res == pn->d_proven); + } + + // we update its value + pn->setValue(id, children, args); + return true; +} + +} // namespace cvc5 diff --git a/src/proof/proof_node_manager.h b/src/proof/proof_node_manager.h new file mode 100644 index 000000000..2fa7ed3e9 --- /dev/null +++ b/src/proof/proof_node_manager.h @@ -0,0 +1,206 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof node manager utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_NODE_MANAGER_H +#define CVC5__PROOF__PROOF_NODE_MANAGER_H + +#include + +#include "expr/node.h" +#include "proof/proof_rule.h" + +namespace cvc5 { + +class ProofChecker; +class ProofNode; + +/** + * A manager for proof node objects. This is a trusted interface for creating + * and updating ProofNode objects. + * + * In more detail, we say a ProofNode is "well-formed (with respect to checker + * X)" if its d_proven field is non-null, and corresponds to the formula that + * the ProofNode proves according to X. The ProofNodeManager class constructs + * and update nodes that are well-formed with respect to its underlying checker. + * + * If no checker is provided, then the ProofNodeManager assigns the d_proven + * field of ProofNode based on the provided "expected" argument in mkNode below, + * which must be provided in this case. + * + * The ProofNodeManager is used as a trusted way of updating ProofNode objects + * via updateNode below. In particular, this method leaves the d_proven field + * unchanged and updates (if possible) the remaining content of a given proof + * node. + * + * Notice that ProofNode objects are mutable, and hence this class does not + * cache the results of mkNode. A version of this class that caches + * immutable version of ProofNode objects could be built as an extension + * or layer on top of this class. + */ +class ProofNodeManager +{ + public: + ProofNodeManager(ProofChecker* pc = nullptr); + ~ProofNodeManager() {} + /** + * This constructs a ProofNode with the given arguments. The expected + * argument, when provided, indicates the formula that the returned node + * is expected to prove. If we find that it does not, based on the underlying + * checker, this method returns nullptr. + * + * @param id The id of the proof node. + * @param children The children of the proof node. + * @param args The arguments of the proof node. + * @param expected (Optional) the expected conclusion of the proof node. + * @return the proof node, or nullptr if the given arguments do not + * consistute a proof of the expected conclusion according to the underlying + * checker, if both are provided. It also returns nullptr if neither the + * checker nor the expected field is provided, since in this case the + * conclusion is unknown. + */ + std::shared_ptr mkNode( + PfRule id, + const std::vector>& children, + const std::vector& args, + Node expected = Node::null()); + /** + * Make the proof node corresponding to the assumption of fact. + * + * @param fact The fact to assume. + * @return The ASSUME proof of fact. + */ + std::shared_ptr mkAssume(Node fact); + /** + * Make transitivity proof, where children contains one or more proofs of + * equalities that form an ordered chain. In other words, the vector children + * is a legal set of children for an application of TRANS. + */ + std::shared_ptr mkTrans( + const std::vector>& children, + Node expected = Node::null()); + + /** + * Make scope having body pf and arguments (assumptions-to-close) assumps. + * If ensureClosed is true, then this method throws an assertion failure if + * the returned proof is not closed. This is the case if a free assumption + * of pf is missing from the vector assumps. + * + * For conveinence, the proof pf may be modified to ensure that the overall + * result is closed. For instance, given input: + * pf = TRANS( ASSUME( x=y ), ASSUME( y=z ) ) + * assumps = { y=x, y=z } + * This method will modify pf to be: + * pf = TRANS( SYMM( ASSUME( y=x ) ), ASSUME( y=z ) ) + * so that y=x matches the free assumption. The returned proof is: + * SCOPE(TRANS( SYMM( ASSUME( y=x ) ), ASSUME( y=z ) ) :args { y=x, y=z }) + * + * When ensureClosed is true, duplicates are eliminated from assumps. The + * reason for this is due to performance, since in this method, assumps is + * converted to an unordered_set to do the above check and hence it is a + * convienient time to eliminate duplicate literals. + * + * Additionally, if both ensureClosed and doMinimize are true, assumps is + * updated to contain exactly the free asumptions of pf. This also includes + * having no duplicates. Furthermore, if assumps is empty after minimization, + * this method is a no-op. + * + * In each case, the update vector assumps is passed as arguments to SCOPE. + * + * @param pf The body of the proof, + * @param assumps The assumptions-to-close of the scope, + * @param ensureClosed Whether to ensure that the proof is closed, + * @param doMinimize Whether to minimize assumptions. + * @param expected the node that the scope should prove. + * @return The scoped proof. + */ + std::shared_ptr mkScope(std::shared_ptr pf, + std::vector& assumps, + bool ensureClosed = true, + bool doMinimize = false, + Node expected = Node::null()); + /** + * This method updates pn to be a proof of the form ( children, args ), + * while maintaining its d_proven field. This method returns false if this + * proof manager is using a checker, and we compute that the above proof + * is not a proof of the fact that pn previously proved. + * + * @param pn The proof node to update. + * @param id The updated id of the proof node. + * @param children The updated children of the proof node. + * @param args The updated arguments of the proof node. + * @return true if the update was successful. + * + * Notice that updateNode always returns true if there is no underlying + * checker. + */ + bool updateNode(ProofNode* pn, + PfRule id, + const std::vector>& children, + const std::vector& args); + /** + * Update node pn to have the contents of pnr. It should be the case that + * pn and pnr prove the same fact, otherwise false is returned and pn is + * unchanged. + */ + bool updateNode(ProofNode* pn, ProofNode* pnr); + /** Get the underlying proof checker */ + ProofChecker* getChecker() const; + /** + * Clone a proof node, which creates a deep copy of pn and returns it. The + * dag structure of pn is the same as that in the returned proof node. + * + * @param pn The proof node to clone + * @return the cloned proof node. + */ + std::shared_ptr clone(std::shared_ptr pn); + + private: + /** The (optional) proof checker */ + ProofChecker* d_checker; + /** the true node */ + Node d_true; + /** Check internal + * + * This returns the result of proof checking a ProofNode with the provided + * arguments with an expected conclusion, which may not null if there is + * no expected conclusion. + * + * This throws an assertion error if we fail to check such a proof node, or + * if expected is provided (non-null) and is different what is proven by the + * other arguments. + */ + Node checkInternal(PfRule id, + const std::vector>& children, + const std::vector& args, + Node expected); + /** + * Update node internal, return true if successful. This is called by + * the update node methods above. The argument needsCheck is whether we + * need to check the correctness of the rule application. This is false + * for the updateNode routine where pnr is an (already checked) proof node. + */ + bool updateNodeInternal( + ProofNode* pn, + PfRule id, + const std::vector>& children, + const std::vector& args, + bool needsCheck); +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_NODE_H */ diff --git a/src/proof/proof_node_to_sexpr.cpp b/src/proof/proof_node_to_sexpr.cpp new file mode 100644 index 000000000..85fc2395e --- /dev/null +++ b/src/proof/proof_node_to_sexpr.cpp @@ -0,0 +1,148 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof node to s-expression. + */ + +#include "proof/proof_node_to_sexpr.h" + +#include +#include + +#include "options/proof_options.h" +#include "proof/proof_node.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +ProofNodeToSExpr::ProofNodeToSExpr() +{ + NodeManager* nm = NodeManager::currentNM(); + d_conclusionMarker = nm->mkBoundVar(":conclusion", nm->sExprType()); + d_argsMarker = nm->mkBoundVar(":args", nm->sExprType()); +} + +Node ProofNodeToSExpr::convertToSExpr(const ProofNode* pn) +{ + NodeManager* nm = NodeManager::currentNM(); + std::map::iterator it; + std::vector visit; + std::vector traversing; + const ProofNode* cur; + visit.push_back(pn); + do + { + cur = visit.back(); + visit.pop_back(); + it = d_pnMap.find(cur); + + if (it == d_pnMap.end()) + { + d_pnMap[cur] = Node::null(); + traversing.push_back(cur); + visit.push_back(cur); + const std::vector>& pc = cur->getChildren(); + for (const std::shared_ptr& cp : pc) + { + if (std::find(traversing.begin(), traversing.end(), cp.get()) + != traversing.end()) + { + Unhandled() << "ProofNodeToSExpr::convertToSExpr: cyclic proof! (use " + "--proof-eager-checking)" + << std::endl; + return Node::null(); + } + visit.push_back(cp.get()); + } + } + else if (it->second.isNull()) + { + Assert(!traversing.empty()); + traversing.pop_back(); + std::vector children; + // add proof rule + children.push_back(getOrMkPfRuleVariable(cur->getRule())); + if (options::proofPrintConclusion()) + { + children.push_back(d_conclusionMarker); + children.push_back(cur->getResult()); + } + const std::vector>& pc = cur->getChildren(); + for (const std::shared_ptr& cp : pc) + { + it = d_pnMap.find(cp.get()); + Assert(it != d_pnMap.end()); + Assert(!it->second.isNull()); + children.push_back(it->second); + } + // add arguments + const std::vector& args = cur->getArguments(); + if (!args.empty()) + { + children.push_back(d_argsMarker); + // needed to ensure builtin operators are not treated as operators + // this can be the case for CONG where d_args may contain a builtin + // operator + std::vector argsSafe; + for (const Node& a : args) + { + Node av = a; + if (a.getNumChildren() == 0 + && NodeManager::operatorToKind(a) != UNDEFINED_KIND) + { + av = getOrMkNodeVariable(a); + } + argsSafe.push_back(av); + } + Node argsC = nm->mkNode(SEXPR, argsSafe); + children.push_back(argsC); + } + d_pnMap[cur] = nm->mkNode(SEXPR, children); + } + } while (!visit.empty()); + Assert(d_pnMap.find(pn) != d_pnMap.end()); + Assert(!d_pnMap.find(pn)->second.isNull()); + return d_pnMap[pn]; +} + +Node ProofNodeToSExpr::getOrMkPfRuleVariable(PfRule r) +{ + std::map::iterator it = d_pfrMap.find(r); + if (it != d_pfrMap.end()) + { + return it->second; + } + std::stringstream ss; + ss << r; + NodeManager* nm = NodeManager::currentNM(); + Node var = nm->mkBoundVar(ss.str(), nm->sExprType()); + d_pfrMap[r] = var; + return var; +} + +Node ProofNodeToSExpr::getOrMkNodeVariable(Node n) +{ + std::map::iterator it = d_nodeMap.find(n); + if (it != d_nodeMap.end()) + { + return it->second; + } + std::stringstream ss; + ss << n; + NodeManager* nm = NodeManager::currentNM(); + Node var = nm->mkBoundVar(ss.str(), nm->sExprType()); + d_nodeMap[n] = var; + return var; +} + +} // namespace cvc5 diff --git a/src/proof/proof_node_to_sexpr.h b/src/proof/proof_node_to_sexpr.h new file mode 100644 index 000000000..c358f3445 --- /dev/null +++ b/src/proof/proof_node_to_sexpr.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Conversion from ProofNode to s-expressions. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_NODE_TO_SEXPR_H +#define CVC5__PROOF__PROOF_NODE_TO_SEXPR_H + +#include + +#include "expr/node.h" +#include "proof/proof_rule.h" + +namespace cvc5 { + +class ProofNode; + +/** A class to convert ProofNode objects to s-expressions */ +class ProofNodeToSExpr +{ + public: + ProofNodeToSExpr(); + ~ProofNodeToSExpr() {} + /** Convert the given proof node to an s-expression + * + * This is useful for operations where it is useful to view a ProofNode as + * a Node. Printing is one such example, where a ProofNode can be printed + * as a dag after this conversion. + * + * The s-expression for a ProofNode has the form: + * (SEXPR (VAR "") S1 ... Sn (VAR ":args") (SEXPR )) + * where S1, ..., Sn are the s-expressions for its . + */ + Node convertToSExpr(const ProofNode* pn); + + private: + /** map proof rules to a variable */ + std::map d_pfrMap; + /** Dummy ":args" marker */ + Node d_argsMarker; + /** Dummy ":conclusion" marker */ + Node d_conclusionMarker; + /** map proof nodes to their s-expression */ + std::map d_pnMap; + /** + * map nodes to a bound variable, used for nodes that have special AST status + * like builtin operators + */ + std::map d_nodeMap; + /** get or make pf rule variable */ + Node getOrMkPfRuleVariable(PfRule r); + /** get or make node variable */ + Node getOrMkNodeVariable(Node n); +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_RULE_H */ diff --git a/src/proof/proof_node_updater.cpp b/src/proof/proof_node_updater.cpp new file mode 100644 index 000000000..2bdb54a45 --- /dev/null +++ b/src/proof/proof_node_updater.cpp @@ -0,0 +1,258 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of a utility for updating proof nodes. + */ + +#include "proof/proof_node_updater.h" + +#include "proof/lazy_proof.h" +#include "proof/proof_ensure_closed.h" +#include "proof/proof_node_algorithm.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { + +ProofNodeUpdaterCallback::ProofNodeUpdaterCallback() {} +ProofNodeUpdaterCallback::~ProofNodeUpdaterCallback() {} + +bool ProofNodeUpdaterCallback::update(Node res, + PfRule id, + const std::vector& children, + const std::vector& args, + CDProof* cdp, + bool& continueUpdate) +{ + return false; +} + +ProofNodeUpdater::ProofNodeUpdater(ProofNodeManager* pnm, + ProofNodeUpdaterCallback& cb, + bool mergeSubproofs, + bool autoSym) + : d_pnm(pnm), + d_cb(cb), + d_debugFreeAssumps(false), + d_mergeSubproofs(mergeSubproofs), + d_autoSym(autoSym) +{ +} + +void ProofNodeUpdater::process(std::shared_ptr pf) +{ + if (d_debugFreeAssumps) + { + if (Trace.isOn("pfnu-debug")) + { + Trace("pfnu-debug2") << "Initial proof: " << *pf.get() << std::endl; + Trace("pfnu-debug") << "ProofNodeUpdater::process" << std::endl; + Trace("pfnu-debug") << "Expected free assumptions: " << std::endl; + for (const Node& fa : d_freeAssumps) + { + Trace("pfnu-debug") << "- " << fa << std::endl; + } + std::vector assump; + expr::getFreeAssumptions(pf.get(), assump); + Trace("pfnu-debug") << "Current free assumptions: " << std::endl; + for (const Node& fa : assump) + { + Trace("pfnu-debug") << "- " << fa << std::endl; + } + } + } + std::vector> traversing; + processInternal(pf, d_freeAssumps, traversing); +} + +void ProofNodeUpdater::processInternal( + std::shared_ptr pf, + const std::vector& fa, + std::vector>& traversing) +{ + Trace("pf-process") << "ProofNodeUpdater::process" << std::endl; + std::unordered_map, bool> visited; + std::unordered_map, bool>::iterator it; + std::vector> visit; + std::shared_ptr cur; + visit.push_back(pf); + std::map>::iterator itc; + // A cache from formulas to proof nodes that are in the current scope. + // Notice that we make a fresh recursive call to process if the current + // rule is SCOPE below. + std::map> resCache; + Node res; + do + { + cur = visit.back(); + visit.pop_back(); + it = visited.find(cur); + res = cur->getResult(); + if (it == visited.end()) + { + if (d_mergeSubproofs) + { + itc = resCache.find(res); + if (itc != resCache.end()) + { + // already have a proof, merge it into this one + visited[cur] = true; + d_pnm->updateNode(cur.get(), itc->second.get()); + continue; + } + } + // run update to a fixed point + bool continueUpdate = true; + while (runUpdate(cur, fa, continueUpdate) && continueUpdate) + { + Trace("pf-process-debug") << "...updated proof." << std::endl; + } + visited[cur] = !continueUpdate; + if (!continueUpdate) + { + // no further changes should be made to cur according to the callback + Trace("pf-process-debug") + << "...marked to not continue update." << std::endl; + runFinalize(cur, fa, resCache); + continue; + } + traversing.push_back(cur); + visit.push_back(cur); + // If we are not the top-level proof, we were a scope, or became a scope + // after updating, we do a separate recursive call to this method. This + // allows us to properly track the assumptions in scope, which is + // important for example to merge or to determine updates based on free + // assumptions. + if (cur->getRule() == PfRule::SCOPE && cur != pf) + { + std::vector nfa; + nfa.insert(nfa.end(), fa.begin(), fa.end()); + const std::vector& args = cur->getArguments(); + nfa.insert(nfa.end(), args.begin(), args.end()); + Trace("pfnu-debug2") << "Process new scope with " << args << std::endl; + // Process in new call separately + processInternal(cur, nfa, traversing); + continue; + } + const std::vector>& ccp = cur->getChildren(); + // now, process children + for (const std::shared_ptr& cp : ccp) + { + if (std::find(traversing.begin(), traversing.end(), cp) + != traversing.end()) + { + Unhandled() + << "ProofNodeUpdater::processInternal: cyclic proof! (use " + "--proof-eager-checking)" + << std::endl; + } + visit.push_back(cp); + } + } + else if (!it->second) + { + Assert(!traversing.empty()); + traversing.pop_back(); + visited[cur] = true; + // finalize the node + runFinalize(cur, fa, resCache); + } + } while (!visit.empty()); + Trace("pf-process") << "ProofNodeUpdater::process: finished" << std::endl; +} + +bool ProofNodeUpdater::runUpdate(std::shared_ptr cur, + const std::vector& fa, + bool& continueUpdate) +{ + // should it be updated? + if (!d_cb.shouldUpdate(cur, fa, continueUpdate)) + { + return false; + } + PfRule id = cur->getRule(); + // use CDProof to open a scope for which the callback updates + CDProof cpf(d_pnm, nullptr, "ProofNodeUpdater::CDProof", d_autoSym); + const std::vector>& cc = cur->getChildren(); + std::vector ccn; + for (const std::shared_ptr& cp : cc) + { + Node cpres = cp->getResult(); + ccn.push_back(cpres); + // store in the proof + cpf.addProof(cp); + } + Node res = cur->getResult(); + Trace("pf-process-debug") + << "Updating (" << cur->getRule() << "): " << res << std::endl; + // only if the callback updated the node + if (d_cb.update(res, id, ccn, cur->getArguments(), &cpf, continueUpdate)) + { + std::shared_ptr npn = cpf.getProofFor(res); + std::vector fullFa; + if (d_debugFreeAssumps) + { + expr::getFreeAssumptions(cur.get(), fullFa); + Trace("pfnu-debug") << "Original proof : " << *cur << std::endl; + } + // then, update the original proof node based on this one + Trace("pf-process-debug") << "Update node..." << std::endl; + d_pnm->updateNode(cur.get(), npn.get()); + Trace("pf-process-debug") << "...update node finished." << std::endl; + if (d_debugFreeAssumps) + { + fullFa.insert(fullFa.end(), fa.begin(), fa.end()); + // We have that npn is a node we occurring the final updated version of + // the proof. We can now debug based on the expected set of free + // assumptions. + Trace("pfnu-debug") << "Ensure update closed..." << std::endl; + pfnEnsureClosedWrt( + npn.get(), fullFa, "pfnu-debug", "ProofNodeUpdater:postupdate"); + } + Trace("pf-process-debug") << "..finished" << std::endl; + return true; + } + Trace("pf-process-debug") << "..finished" << std::endl; + return false; +} + +void ProofNodeUpdater::runFinalize( + std::shared_ptr cur, + const std::vector& fa, + std::map>& resCache) +{ + if (d_mergeSubproofs) + { + Node res = cur->getResult(); + // cache result if we are merging subproofs + resCache[res] = cur; + } + if (d_debugFreeAssumps) + { + // We have that npn is a node we occurring the final updated version of + // the proof. We can now debug based on the expected set of free + // assumptions. + Trace("pfnu-debug") << "Ensure update closed..." << std::endl; + pfnEnsureClosedWrt( + cur.get(), fa, "pfnu-debug", "ProofNodeUpdater:finalize"); + } +} + +void ProofNodeUpdater::setDebugFreeAssumptions( + const std::vector& freeAssumps) +{ + d_freeAssumps.clear(); + d_freeAssumps.insert( + d_freeAssumps.end(), freeAssumps.begin(), freeAssumps.end()); + d_debugFreeAssumps = true; +} + +} // namespace cvc5 diff --git a/src/proof/proof_node_updater.h b/src/proof/proof_node_updater.h new file mode 100644 index 000000000..6b8841e67 --- /dev/null +++ b/src/proof/proof_node_updater.h @@ -0,0 +1,164 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * A utility for updating proof nodes. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_NODE_UPDATER_H +#define CVC5__PROOF__PROOF_NODE_UPDATER_H + +#include +#include + +#include "expr/node.h" +#include "proof/proof_node.h" + +namespace cvc5 { + +class CDProof; +class ProofNode; +class ProofNodeManager; + +/** + * A virtual callback class for updating ProofNode. An example use case of this + * class is to eliminate a proof rule by expansion. + */ +class ProofNodeUpdaterCallback +{ + public: + ProofNodeUpdaterCallback(); + virtual ~ProofNodeUpdaterCallback(); + /** Should proof pn be updated? + * + * @param pn the proof node that maybe should be updated + * @param fa the assumptions in scope + * @param continueUpdate whether we should continue recursively updating pn + * @return whether we should run the update method on pn + */ + virtual bool shouldUpdate(std::shared_ptr pn, + const std::vector& fa, + bool& continueUpdate) = 0; + /** + * Update the proof rule application, store steps in cdp. Return true if + * the proof changed. It can be assumed that cdp contains proofs of each + * fact in children. + * + * If continueUpdate is set to false in this method, then the resulting + * proof (the proof of res in cdp) is *not* called back to update by the + * proof node updater, nor are its children recursed. Otherwise, by default, + * the proof node updater will continue updating the resulting proof and will + * recursively update its children. This is analogous to marking REWRITE_DONE + * in a rewrite response. + */ + virtual bool update(Node res, + PfRule id, + const std::vector& children, + const std::vector& args, + CDProof* cdp, + bool& continueUpdate); +}; + +/** + * A generic class for updating ProofNode. It is parameterized by a callback + * class. Its process method runs this callback on all subproofs of a provided + * ProofNode application that meet some criteria + * (ProofNodeUpdaterCallback::shouldUpdate) + * and overwrites them based on the update procedure of the callback + * (ProofNodeUpdaterCallback::update), which uses local CDProof objects that + * should be filled in the callback for each ProofNode to update. This update + * process is applied in a *pre-order* traversal. + */ +class ProofNodeUpdater +{ + public: + /** + * @param pnm The proof node manager we are using + * @param cb The callback to apply to each node + * @param mergeSubproofs Whether to automatically merge subproofs within + * the same SCOPE that prove the same fact. + * @param autoSym Whether intermediate CDProof objects passed to updater + * callbacks automatically introduce SYMM steps. + */ + ProofNodeUpdater(ProofNodeManager* pnm, + ProofNodeUpdaterCallback& cb, + bool mergeSubproofs = false, + bool autoSym = true); + /** + * Post-process, which performs the main post-processing technique described + * above. + */ + void process(std::shared_ptr pf); + + /** + * Set free assumptions to freeAssumps. This indicates that we expect + * the proof we are processing to have free assumptions that are in + * freeAssumps. This enables checking when this is violated, which is + * expensive in general. It is not recommended that this method is called + * by default. + */ + void setDebugFreeAssumptions(const std::vector& freeAssumps); + + private: + /** The proof node manager */ + ProofNodeManager* d_pnm; + /** The callback */ + ProofNodeUpdaterCallback& d_cb; + /** + * Post-process, which performs the main post-processing technique described + * above. + * + * @param pf The proof to process + * @param fa The assumptions of the scope that fa is a subproof of with + * respect to the original proof. For example, if (SCOPE P :args (A B)), we + * may call this method on P with fa = { A, B }. + * @param traversing The list of proof nodes we are currently traversing + * beneath. This is used for checking for cycles in the overall proof. + */ + void processInternal(std::shared_ptr pf, + const std::vector& fa, + std::vector>& traversing); + /** + * Update proof node cur based on the callback. This modifies curr using + * ProofNodeManager::updateNode based on the proof node constructed to + * replace it by the callback. Return true if cur was updated. If + * continueUpdate is updated to false, then cur is not updated further + * and its children are not traversed. + */ + bool runUpdate(std::shared_ptr cur, + const std::vector& fa, + bool& continueUpdate); + /** + * Finalize the node cur. This is called at the moment that it is established + * that cur will appear in the final proof. We do any final debug checking + * and add it to the results cache resCache if we are merging subproofs. + */ + void runFinalize(std::shared_ptr cur, + const std::vector& fa, + std::map>& resCache); + /** Are we debugging free assumptions? */ + bool d_debugFreeAssumps; + /** The initial free assumptions */ + std::vector d_freeAssumps; + /** Whether we are merging subproofs */ + bool d_mergeSubproofs; + /** + * Whether intermediate CDProof objects passed to updater callbacks + * automatically introduce SYMM steps. + */ + bool d_autoSym; +}; + +} // namespace cvc5 + +#endif diff --git a/src/proof/proof_rule.cpp b/src/proof/proof_rule.cpp new file mode 100644 index 000000000..0cefe1209 --- /dev/null +++ b/src/proof/proof_rule.cpp @@ -0,0 +1,258 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof rule. + */ + +#include "proof/proof_rule.h" + +#include + +namespace cvc5 { + +const char* toString(PfRule id) +{ + switch (id) + { + //================================================= Core rules + case PfRule::ASSUME: return "ASSUME"; + case PfRule::SCOPE: return "SCOPE"; + case PfRule::SUBS: return "SUBS"; + case PfRule::REWRITE: return "REWRITE"; + case PfRule::EVALUATE: return "EVALUATE"; + case PfRule::MACRO_SR_EQ_INTRO: return "MACRO_SR_EQ_INTRO"; + case PfRule::MACRO_SR_PRED_INTRO: return "MACRO_SR_PRED_INTRO"; + case PfRule::MACRO_SR_PRED_ELIM: return "MACRO_SR_PRED_ELIM"; + case PfRule::MACRO_SR_PRED_TRANSFORM: return "MACRO_SR_PRED_TRANSFORM"; + case PfRule::REMOVE_TERM_FORMULA_AXIOM: return "REMOVE_TERM_FORMULA_AXIOM"; + //================================================= Trusted rules + case PfRule::THEORY_LEMMA: return "THEORY_LEMMA"; + case PfRule::THEORY_REWRITE: return "THEORY_REWRITE"; + case PfRule::PREPROCESS: return "PREPROCESS"; + case PfRule::PREPROCESS_LEMMA: return "PREPROCESS_LEMMA"; + case PfRule::THEORY_PREPROCESS: return "THEORY_PREPROCESS"; + case PfRule::THEORY_PREPROCESS_LEMMA: return "THEORY_PREPROCESS_LEMMA"; + case PfRule::THEORY_EXPAND_DEF: return "THEORY_EXPAND_DEF"; + case PfRule::WITNESS_AXIOM: return "WITNESS_AXIOM"; + case PfRule::TRUST_REWRITE: return "TRUST_REWRITE"; + case PfRule::TRUST_SUBS: return "TRUST_SUBS"; + case PfRule::TRUST_SUBS_MAP: return "TRUST_SUBS_MAP"; + //================================================= Boolean rules + case PfRule::RESOLUTION: return "RESOLUTION"; + case PfRule::CHAIN_RESOLUTION: return "CHAIN_RESOLUTION"; + case PfRule::FACTORING: return "FACTORING"; + case PfRule::REORDERING: return "REORDERING"; + case PfRule::MACRO_RESOLUTION: return "MACRO_RESOLUTION"; + case PfRule::MACRO_RESOLUTION_TRUST: return "MACRO_RESOLUTION_TRUST"; + case PfRule::SPLIT: return "SPLIT"; + case PfRule::EQ_RESOLVE: return "EQ_RESOLVE"; + case PfRule::MODUS_PONENS: return "MODUS_PONENS"; + case PfRule::NOT_NOT_ELIM: return "NOT_NOT_ELIM"; + case PfRule::CONTRA: return "CONTRA"; + case PfRule::AND_ELIM: return "AND_ELIM"; + case PfRule::AND_INTRO: return "AND_INTRO"; + case PfRule::NOT_OR_ELIM: return "NOT_OR_ELIM"; + case PfRule::IMPLIES_ELIM: return "IMPLIES_ELIM"; + case PfRule::NOT_IMPLIES_ELIM1: return "NOT_IMPLIES_ELIM1"; + case PfRule::NOT_IMPLIES_ELIM2: return "NOT_IMPLIES_ELIM2"; + case PfRule::EQUIV_ELIM1: return "EQUIV_ELIM1"; + case PfRule::EQUIV_ELIM2: return "EQUIV_ELIM2"; + case PfRule::NOT_EQUIV_ELIM1: return "NOT_EQUIV_ELIM1"; + case PfRule::NOT_EQUIV_ELIM2: return "NOT_EQUIV_ELIM2"; + case PfRule::XOR_ELIM1: return "XOR_ELIM1"; + case PfRule::XOR_ELIM2: return "XOR_ELIM2"; + case PfRule::NOT_XOR_ELIM1: return "NOT_XOR_ELIM1"; + case PfRule::NOT_XOR_ELIM2: return "NOT_XOR_ELIM2"; + case PfRule::ITE_ELIM1: return "ITE_ELIM1"; + case PfRule::ITE_ELIM2: return "ITE_ELIM2"; + case PfRule::NOT_ITE_ELIM1: return "NOT_ITE_ELIM1"; + case PfRule::NOT_ITE_ELIM2: return "NOT_ITE_ELIM2"; + //================================================= De Morgan rules + case PfRule::NOT_AND: return "NOT_AND"; + //================================================= CNF rules + case PfRule::CNF_AND_POS: return "CNF_AND_POS"; + case PfRule::CNF_AND_NEG: return "CNF_AND_NEG"; + case PfRule::CNF_OR_POS: return "CNF_OR_POS"; + case PfRule::CNF_OR_NEG: return "CNF_OR_NEG"; + case PfRule::CNF_IMPLIES_POS: return "CNF_IMPLIES_POS"; + case PfRule::CNF_IMPLIES_NEG1: return "CNF_IMPLIES_NEG1"; + case PfRule::CNF_IMPLIES_NEG2: return "CNF_IMPLIES_NEG2"; + case PfRule::CNF_EQUIV_POS1: return "CNF_EQUIV_POS1"; + case PfRule::CNF_EQUIV_POS2: return "CNF_EQUIV_POS2"; + case PfRule::CNF_EQUIV_NEG1: return "CNF_EQUIV_NEG1"; + case PfRule::CNF_EQUIV_NEG2: return "CNF_EQUIV_NEG2"; + case PfRule::CNF_XOR_POS1: return "CNF_XOR_POS1"; + case PfRule::CNF_XOR_POS2: return "CNF_XOR_POS2"; + case PfRule::CNF_XOR_NEG1: return "CNF_XOR_NEG1"; + case PfRule::CNF_XOR_NEG2: return "CNF_XOR_NEG2"; + case PfRule::CNF_ITE_POS1: return "CNF_ITE_POS1"; + case PfRule::CNF_ITE_POS2: return "CNF_ITE_POS2"; + case PfRule::CNF_ITE_POS3: return "CNF_ITE_POS3"; + case PfRule::CNF_ITE_NEG1: return "CNF_ITE_NEG1"; + case PfRule::CNF_ITE_NEG2: return "CNF_ITE_NEG2"; + case PfRule::CNF_ITE_NEG3: return "CNF_ITE_NEG3"; + //================================================= Equality rules + case PfRule::REFL: return "REFL"; + case PfRule::SYMM: return "SYMM"; + case PfRule::TRANS: return "TRANS"; + case PfRule::CONG: return "CONG"; + case PfRule::TRUE_INTRO: return "TRUE_INTRO"; + case PfRule::TRUE_ELIM: return "TRUE_ELIM"; + case PfRule::FALSE_INTRO: return "FALSE_INTRO"; + case PfRule::FALSE_ELIM: return "FALSE_ELIM"; + case PfRule::HO_APP_ENCODE: return "HO_APP_ENCODE"; + case PfRule::HO_CONG: return "HO_CONG"; + //================================================= Array rules + case PfRule::ARRAYS_READ_OVER_WRITE: return "ARRAYS_READ_OVER_WRITE"; + case PfRule::ARRAYS_READ_OVER_WRITE_CONTRA: + return "ARRAYS_READ_OVER_WRITE_CONTRA"; + case PfRule::ARRAYS_READ_OVER_WRITE_1: return "ARRAYS_READ_OVER_WRITE_1"; + case PfRule::ARRAYS_EXT: return "ARRAYS_EXT"; + case PfRule::ARRAYS_TRUST: return "ARRAYS_TRUST"; + //================================================= Bit-Vector rules + case PfRule::BV_BITBLAST: return "BV_BITBLAST"; + case PfRule::BV_BITBLAST_CONST: return "BV_BITBLAST_CONST"; + case PfRule::BV_BITBLAST_VAR: return "BV_BITBLAST_VAR"; + case PfRule::BV_BITBLAST_EQUAL: return "BV_BITBLAST_EQUAL"; + case PfRule::BV_BITBLAST_ULT: return "BV_BITBLAST_ULT"; + case PfRule::BV_BITBLAST_ULE: return "BV_BITBLAST_ULE"; + case PfRule::BV_BITBLAST_UGT: return "BV_BITBLAST_UGT"; + case PfRule::BV_BITBLAST_UGE: return "BV_BITBLAST_UGE"; + case PfRule::BV_BITBLAST_SLT: return "BV_BITBLAST_SLT"; + case PfRule::BV_BITBLAST_SLE: return "BV_BITBLAST_SLE"; + case PfRule::BV_BITBLAST_SGT: return "BV_BITBLAST_SGT"; + case PfRule::BV_BITBLAST_SGE: return "BV_BITBLAST_SGE"; + case PfRule::BV_BITBLAST_NOT: return "BV_BITBLAST_NOT"; + case PfRule::BV_BITBLAST_CONCAT: return "BV_BITBLAST_CONCAT"; + case PfRule::BV_BITBLAST_AND: return "BV_BITBLAST_AND"; + case PfRule::BV_BITBLAST_OR: return "BV_BITBLAST_OR"; + case PfRule::BV_BITBLAST_XOR: return "BV_BITBLAST_XOR"; + case PfRule::BV_BITBLAST_XNOR: return "BV_BITBLAST_XNOR"; + case PfRule::BV_BITBLAST_NAND: return "BV_BITBLAST_NAND"; + case PfRule::BV_BITBLAST_NOR: return "BV_BITBLAST_NOR"; + case PfRule::BV_BITBLAST_COMP: return "BV_BITBLAST_COMP"; + case PfRule::BV_BITBLAST_MULT: return "BV_BITBLAST_MULT"; + case PfRule::BV_BITBLAST_ADD: return "BV_BITBLAST_ADD"; + case PfRule::BV_BITBLAST_SUB: return "BV_BITBLAST_SUB"; + case PfRule::BV_BITBLAST_NEG: return "BV_BITBLAST_NEG"; + case PfRule::BV_BITBLAST_UDIV: return "BV_BITBLAST_UDIV"; + case PfRule::BV_BITBLAST_UREM: return "BV_BITBLAST_UREM"; + case PfRule::BV_BITBLAST_SDIV: return "BV_BITBLAST_SDIV"; + case PfRule::BV_BITBLAST_SREM: return "BV_BITBLAST_SREM"; + case PfRule::BV_BITBLAST_SMOD: return "BV_BITBLAST_SMOD"; + case PfRule::BV_BITBLAST_SHL: return "BV_BITBLAST_SHL"; + case PfRule::BV_BITBLAST_LSHR: return "BV_BITBLAST_LSHR"; + case PfRule::BV_BITBLAST_ASHR: return "BV_BITBLAST_ASHR"; + case PfRule::BV_BITBLAST_ULTBV: return "BV_BITBLAST_ULTBV"; + case PfRule::BV_BITBLAST_SLTBV: return "BV_BITBLAST_SLTBV"; + case PfRule::BV_BITBLAST_ITE: return "BV_BITBLAST_ITE"; + case PfRule::BV_BITBLAST_EXTRACT: return "BV_BITBLAST_EXTRACT"; + case PfRule::BV_BITBLAST_REPEAT: return "BV_BITBLAST_REPEAT"; + case PfRule::BV_BITBLAST_ZERO_EXTEND: return "BV_BITBLAST_ZERO_EXTEND"; + case PfRule::BV_BITBLAST_SIGN_EXTEND: return "BV_BITBLAST_SIGN_EXTEND"; + case PfRule::BV_BITBLAST_ROTATE_RIGHT: return "BV_BITBLAST_ROTATE_RIGHT"; + case PfRule::BV_BITBLAST_ROTATE_LEFT: return "BV_BITBLAST_ROTATE_LEFT"; + case PfRule::BV_EAGER_ATOM: return "BV_EAGER_ATOM"; + //================================================= Datatype rules + case PfRule::DT_UNIF: return "DT_UNIF"; + case PfRule::DT_INST: return "DT_INST"; + case PfRule::DT_COLLAPSE: return "DT_COLLAPSE"; + case PfRule::DT_SPLIT: return "DT_SPLIT"; + case PfRule::DT_CLASH: return "DT_CLASH"; + case PfRule::DT_TRUST: return "DT_TRUST"; + //================================================= Quantifiers rules + case PfRule::SKOLEM_INTRO: return "SKOLEM_INTRO"; + case PfRule::EXISTS_INTRO: return "EXISTS_INTRO"; + case PfRule::SKOLEMIZE: return "SKOLEMIZE"; + case PfRule::INSTANTIATE: return "INSTANTIATE"; + //================================================= String rules + case PfRule::CONCAT_EQ: return "CONCAT_EQ"; + case PfRule::CONCAT_UNIFY: return "CONCAT_UNIFY"; + case PfRule::CONCAT_CONFLICT: return "CONCAT_CONFLICT"; + case PfRule::CONCAT_SPLIT: return "CONCAT_SPLIT"; + case PfRule::CONCAT_CSPLIT: return "CONCAT_CSPLIT"; + case PfRule::CONCAT_LPROP: return "CONCAT_LPROP"; + case PfRule::CONCAT_CPROP: return "CONCAT_CPROP"; + case PfRule::STRING_DECOMPOSE: return "STRING_DECOMPOSE"; + case PfRule::STRING_LENGTH_POS: return "STRING_LENGTH_POS"; + case PfRule::STRING_LENGTH_NON_EMPTY: return "STRING_LENGTH_NON_EMPTY"; + case PfRule::STRING_REDUCTION: return "STRING_REDUCTION"; + case PfRule::STRING_EAGER_REDUCTION: return "STRING_EAGER_REDUCTION"; + case PfRule::RE_INTER: return "RE_INTER"; + case PfRule::RE_UNFOLD_POS: return "RE_UNFOLD_POS"; + case PfRule::RE_UNFOLD_NEG: return "RE_UNFOLD_NEG"; + case PfRule::RE_UNFOLD_NEG_CONCAT_FIXED: + return "RE_UNFOLD_NEG_CONCAT_FIXED"; + case PfRule::RE_ELIM: return "RE_ELIM"; + case PfRule::STRING_CODE_INJ: return "STRING_CODE_INJ"; + case PfRule::STRING_SEQ_UNIT_INJ: return "STRING_SEQ_UNIT_INJ"; + case PfRule::STRING_TRUST: return "STRING_TRUST"; + //================================================= Arith rules + case PfRule::MACRO_ARITH_SCALE_SUM_UB: + return "ARITH_SCALE_SUM_UPPER_BOUNDS"; + case PfRule::ARITH_SUM_UB: return "ARITH_SUM_UB"; + case PfRule::ARITH_TRICHOTOMY: return "ARITH_TRICHOTOMY"; + case PfRule::INT_TIGHT_LB: return "INT_TIGHT_LB"; + case PfRule::INT_TIGHT_UB: return "INT_TIGHT_UB"; + case PfRule::INT_TRUST: return "INT_TRUST"; + case PfRule::ARITH_MULT_SIGN: return "ARITH_MULT_SIGN"; + case PfRule::ARITH_MULT_POS: return "ARITH_MULT_POS"; + case PfRule::ARITH_MULT_NEG: return "ARITH_MULT_NEG"; + case PfRule::ARITH_MULT_TANGENT: return "ARITH_MULT_TANGENT"; + case PfRule::ARITH_OP_ELIM_AXIOM: return "ARITH_OP_ELIM_AXIOM"; + case PfRule::ARITH_TRANS_PI: return "ARITH_TRANS_PI"; + case PfRule::ARITH_TRANS_EXP_NEG: return "ARITH_TRANS_EXP_NEG"; + case PfRule::ARITH_TRANS_EXP_POSITIVITY: + return "ARITH_TRANS_EXP_POSITIVITY"; + case PfRule::ARITH_TRANS_EXP_SUPER_LIN: return "ARITH_TRANS_EXP_SUPER_LIN"; + case PfRule::ARITH_TRANS_EXP_ZERO: return "ARITH_TRANS_EXP_ZERO"; + case PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_NEG: + return "ARITH_TRANS_EXP_APPROX_ABOVE_NEG"; + case PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_POS: + return "ARITH_TRANS_EXP_APPROX_ABOVE_POS"; + case PfRule::ARITH_TRANS_EXP_APPROX_BELOW: + return "ARITH_TRANS_EXP_APPROX_BELOW"; + case PfRule::ARITH_TRANS_SINE_BOUNDS: return "ARITH_TRANS_SINE_BOUNDS"; + case PfRule::ARITH_TRANS_SINE_SHIFT: return "ARITH_TRANS_SINE_SHIFT"; + case PfRule::ARITH_TRANS_SINE_SYMMETRY: return "ARITH_TRANS_SINE_SYMMETRY"; + case PfRule::ARITH_TRANS_SINE_TANGENT_ZERO: + return "ARITH_TRANS_SINE_TANGENT_ZERO"; + case PfRule::ARITH_TRANS_SINE_TANGENT_PI: + return "ARITH_TRANS_SINE_TANGENT_PI"; + case PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_NEG: + return "ARITH_TRANS_SINE_APPROX_ABOVE_NEG"; + case PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS: + return "ARITH_TRANS_SINE_APPROX_ABOVE_POS"; + case PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG: + return "ARITH_TRANS_SINE_APPROX_BELOW_NEG"; + case PfRule::ARITH_TRANS_SINE_APPROX_BELOW_POS: + return "ARITH_TRANS_SINE_APPROX_BELOW_POS"; + case PfRule::ARITH_NL_CAD_DIRECT: return "ARITH_NL_CAD_DIRECT"; + case PfRule::ARITH_NL_CAD_RECURSIVE: return "ARITH_NL_CAD_RECURSIVE"; + //================================================= Unknown rule + case PfRule::UNKNOWN: return "UNKNOWN"; + default: return "?"; + } +} + +std::ostream& operator<<(std::ostream& out, PfRule id) +{ + out << toString(id); + return out; +} + +size_t PfRuleHashFunction::operator()(PfRule id) const +{ + return static_cast(id); +} + +} // namespace cvc5 diff --git a/src/proof/proof_rule.h b/src/proof/proof_rule.h new file mode 100644 index 000000000..107285cc3 --- /dev/null +++ b/src/proof/proof_rule.h @@ -0,0 +1,1453 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof rule enumeration. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_RULE_H +#define CVC5__PROOF__PROOF_RULE_H + +#include + +namespace cvc5 { + +/** + * An enumeration for proof rules. This enumeration is analogous to Kind for + * Node objects. In the documentation below, P:F denotes a ProofNode that + * proves formula F. + * + * Conceptually, the following proof rules form a calculus whose target + * user is the Node-level theory solvers. This means that the rules below + * are designed to reason about, among other things, common operations on Node + * objects like Rewriter::rewrite or Node::substitute. It is intended to be + * translated or printed in other formats. + * + * The following PfRule values include core rules and those categorized by + * theory, including the theory of equality. + * + * The "core rules" include two distinguished rules which have special status: + * (1) ASSUME, which represents an open leaf in a proof. + * (2) SCOPE, which closes the scope of assumptions. + * The core rules additionally correspond to generic operations that are done + * internally on nodes, e.g. calling Rewriter::rewrite. + * + * Rules with prefix MACRO_ are those that can be defined in terms of other + * rules. These exist for convienience. We provide their definition in the line + * "Macro:". + */ +enum class PfRule : uint32_t +{ + //================================================= Core rules + //======================== Assume and Scope + // ======== Assumption (a leaf) + // Children: none + // Arguments: (F) + // -------------- + // Conclusion: F + // + // This rule has special status, in that an application of assume is an + // open leaf in a proof that is not (yet) justified. An assume leaf is + // analogous to a free variable in a term, where we say "F is a free + // assumption in proof P" if it contains an application of F that is not + // bound by SCOPE (see below). + ASSUME, + // ======== Scope (a binder for assumptions) + // Children: (P:F) + // Arguments: (F1, ..., Fn) + // -------------- + // Conclusion: (=> (and F1 ... Fn) F) or (not (and F1 ... Fn)) if F is false + // + // This rule has a dual purpose with ASSUME. It is a way to close + // assumptions in a proof. We require that F1 ... Fn are free assumptions in + // P and say that F1, ..., Fn are not free in (SCOPE P). In other words, they + // are bound by this application. For example, the proof node: + // (SCOPE (ASSUME F) :args F) + // has the conclusion (=> F F) and has no free assumptions. More generally, a + // proof with no free assumptions always concludes a valid formula. + SCOPE, + + //======================== Builtin theory (common node operations) + // ======== Substitution + // Children: (P1:F1, ..., Pn:Fn) + // Arguments: (t, (ids)?) + // --------------------------------------------------------------- + // Conclusion: (= t t*sigma{ids}(Fn)*...*sigma{ids}(F1)) + // where sigma{ids}(Fi) are substitutions, which notice are applied in + // reverse order. + // Notice that ids is a MethodId identifier, which determines how to convert + // the formulas F1, ..., Fn into substitutions. + SUBS, + // ======== Rewrite + // Children: none + // Arguments: (t, (idr)?) + // ---------------------------------------- + // Conclusion: (= t Rewriter{idr}(t)) + // where idr is a MethodId identifier, which determines the kind of rewriter + // to apply, e.g. Rewriter::rewrite. + REWRITE, + // ======== Evaluate + // Children: none + // Arguments: (t) + // ---------------------------------------- + // Conclusion: (= t Evaluator::evaluate(t)) + // Note this is equivalent to: + // (REWRITE t MethodId::RW_EVALUATE) + EVALUATE, + // ======== Substitution + Rewriting equality introduction + // + // In this rule, we provide a term t and conclude that it is equal to its + // rewritten form under a (proven) substitution. + // + // Children: (P1:F1, ..., Pn:Fn) + // Arguments: (t, (ids (ida (idr)?)?)?) + // --------------------------------------------------------------- + // Conclusion: (= t t') + // where + // t' is + // Rewriter{idr}(t*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) + // + // In other words, from the point of view of Skolem forms, this rule + // transforms t to t' by standard substitution + rewriting. + // + // The arguments ids, ida and idr are optional and specify the identifier of + // the substitution, the substitution application and rewriter respectively to + // be used. For details, see theory/builtin/proof_checker.h. + MACRO_SR_EQ_INTRO, + // ======== Substitution + Rewriting predicate introduction + // + // In this rule, we provide a formula F and conclude it, under the condition + // that it rewrites to true under a proven substitution. + // + // Children: (P1:F1, ..., Pn:Fn) + // Arguments: (F, (ids (ida (idr)?)?)?) + // --------------------------------------------------------------- + // Conclusion: F + // where + // Rewriter{idr}(F*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) == true + // where ids and idr are method identifiers. + // + // More generally, this rule also holds when: + // Rewriter::rewrite(toOriginal(F')) == true + // where F' is the result of the left hand side of the equality above. Here, + // notice that we apply rewriting on the original form of F', meaning that + // this rule may conclude an F whose Skolem form is justified by the + // definition of its (fresh) Skolem variables. For example, this rule may + // justify the conclusion (= k t) where k is the purification Skolem for t, + // e.g. where the original form of k is t. + // + // Furthermore, notice that the rewriting and substitution is applied only + // within the side condition, meaning the rewritten form of the original form + // of F does not escape this rule. + MACRO_SR_PRED_INTRO, + // ======== Substitution + Rewriting predicate elimination + // + // In this rule, if we have proven a formula F, then we may conclude its + // rewritten form under a proven substitution. + // + // Children: (P1:F, P2:F1, ..., P_{n+1}:Fn) + // Arguments: ((ids (ida (idr)?)?)?) + // ---------------------------------------- + // Conclusion: F' + // where + // F' is + // Rewriter{idr}(F*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)). + // where ids and idr are method identifiers. + // + // We rewrite only on the Skolem form of F, similar to MACRO_SR_EQ_INTRO. + MACRO_SR_PRED_ELIM, + // ======== Substitution + Rewriting predicate transform + // + // In this rule, if we have proven a formula F, then we may provide a formula + // G and conclude it if F and G are equivalent after rewriting under a proven + // substitution. + // + // Children: (P1:F, P2:F1, ..., P_{n+1}:Fn) + // Arguments: (G, (ids (ida (idr)?)?)?) + // ---------------------------------------- + // Conclusion: G + // where + // Rewriter{idr}(F*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) == + // Rewriter{idr}(G*sigma{ids, ida}(Fn)*...*sigma{ids, ida}(F1)) + // + // More generally, this rule also holds when: + // Rewriter::rewrite(toOriginal(F')) == Rewriter::rewrite(toOriginal(G')) + // where F' and G' are the result of each side of the equation above. Here, + // original forms are used in a similar manner to MACRO_SR_PRED_INTRO above. + MACRO_SR_PRED_TRANSFORM, + //================================================= Processing rules + // ======== Remove Term Formulas Axiom + // Children: none + // Arguments: (t) + // --------------------------------------------------------------- + // Conclusion: RemoveTermFormulas::getAxiomFor(t). + REMOVE_TERM_FORMULA_AXIOM, + + //================================================= Trusted rules + // ======== Theory lemma + // Children: none + // Arguments: (F, tid) + // --------------------------------------------------------------- + // Conclusion: F + // where F is a (T-valid) theory lemma generated by theory with TheoryId tid. + // This is a "coarse-grained" rule that is used as a placeholder if a theory + // did not provide a proof for a lemma or conflict. + THEORY_LEMMA, + // ======== Theory Rewrite + // Children: none + // Arguments: (F, tid, rid) + // ---------------------------------------- + // Conclusion: F + // where F is an equality of the form (= t t') where t' is obtained by + // applying the kind of rewriting given by the method identifier rid, which + // is one of: + // { RW_REWRITE_THEORY_PRE, RW_REWRITE_THEORY_POST, RW_REWRITE_EQ_EXT } + // Notice that the checker for this rule does not replay the rewrite to ensure + // correctness, since theory rewriter methods are not static. For example, + // the quantifiers rewriter involves constructing new bound variables that are + // not guaranteed to be consistent on each call. + THEORY_REWRITE, + // The remaining rules in this section have the signature of a "trusted rule": + // + // Children: none + // Arguments: (F) + // --------------------------------------------------------------- + // Conclusion: F + // + // where F is an equality of the form t = t' where t was replaced by t' + // based on some preprocessing pass, or otherwise F was added as a new + // assertion by some preprocessing pass. + PREPROCESS, + // where F was added as a new assertion by some preprocessing pass. + PREPROCESS_LEMMA, + // where F is an equality of the form t = Theory::ppRewrite(t) for some + // theory. Notice this is a "trusted" rule. + THEORY_PREPROCESS, + // where F was added as a new assertion by theory preprocessing. + THEORY_PREPROCESS_LEMMA, + // where F is an equality of the form t = t' where t was replaced by t' + // based on theory expand definitions. + THEORY_EXPAND_DEF, + // where F is an existential (exists ((x T)) (P x)) used for introducing + // a witness term (witness ((x T)) (P x)). + WITNESS_AXIOM, + // where F is an equality (= t t') that holds by a form of rewriting that + // could not be replayed during proof postprocessing. + TRUST_REWRITE, + // where F is an equality (= t t') that holds by a form of substitution that + // could not be replayed during proof postprocessing. + TRUST_SUBS, + // where F is an equality (= t t') that holds by a form of substitution that + // could not be determined by the TrustSubstitutionMap. + TRUST_SUBS_MAP, + // ========= SAT Refutation for assumption-based unsat cores + // Children: (P1, ..., Pn) + // Arguments: none + // --------------------- + // Conclusion: false + // Note: P1, ..., Pn correspond to the unsat core determined by the SAT + // solver. + SAT_REFUTATION, + + //================================================= Boolean rules + // ======== Resolution + // Children: + // (P1:C1, P2:C2) + // Arguments: (pol, L) + // --------------------- + // Conclusion: C + // where + // - C1 and C2 are nodes viewed as clauses, i.e., either an OR node with + // each children viewed as a literal or a node viewed as a literal. Note + // that an OR node could also be a literal. + // - pol is either true or false, representing the polarity of the pivot on + // the first clause + // - L is the pivot of the resolution, which occurs as is (resp. under a + // NOT) in C1 and negatively (as is) in C2 if pol = true (pol = false). + // C is a clause resulting from collecting all the literals in C1, minus the + // first occurrence of the pivot or its negation, and C2, minus the first + // occurrence of the pivot or its negation, according to the policy above. + // If the resulting clause has a single literal, that literal itself is the + // result; if it has no literals, then the result is false; otherwise it's + // an OR node of the resulting literals. + // + // Note that it may be the case that the pivot does not occur in the + // clauses. In this case the rule is not unsound, but it does not correspond + // to resolution but rather to a weakening of the clause that did not have a + // literal eliminated. + RESOLUTION, + // ======== N-ary Resolution + // Children: (P1:C_1, ..., Pm:C_n) + // Arguments: (pol_1, L_1, ..., pol_{n-1}, L_{n-1}) + // --------------------- + // Conclusion: C + // where + // - let C_1 ... C_n be nodes viewed as clauses, as defined above + // - let "C_1 <>_{L,pol} C_2" represent the resolution of C_1 with C_2 with + // pivot L and polarity pol, as defined above + // - let C_1' = C_1 (from P1), + // - for each i > 1, let C_i' = C_{i-1} <>_{L_{i-1}, pol_{i-1}} C_i' + // The result of the chain resolution is C = C_n' + CHAIN_RESOLUTION, + // ======== Factoring + // Children: (P:C1) + // Arguments: () + // --------------------- + // Conclusion: C2 + // where + // Set representations of C1 and C2 is the same and the number of literals in + // C2 is smaller than that of C1 + FACTORING, + // ======== Reordering + // Children: (P:C1) + // Arguments: (C2) + // --------------------- + // Conclusion: C2 + // where + // Set representations of C1 and C2 are the same and the number of literals + // in C2 is the same of that of C1 + REORDERING, + // ======== N-ary Resolution + Factoring + Reordering + // Children: (P1:C_1, ..., Pm:C_n) + // Arguments: (C, pol_1, L_1, ..., pol_{n-1}, L_{n-1}) + // --------------------- + // Conclusion: C + // where + // - let C_1 ... C_n be nodes viewed as clauses, as defined in RESOLUTION + // - let "C_1 <>_{L,pol} C_2" represent the resolution of C_1 with C_2 with + // pivot L and polarity pol, as defined in RESOLUTION + // - let C_1' be equal, in its set representation, to C_1 (from P1), + // - for each i > 1, let C_i' be equal, it its set representation, to + // C_{i-1} <>_{L_{i-1}, pol_{i-1}} C_i' + // The result of the chain resolution is C, which is equal, in its set + // representation, to C_n' + MACRO_RESOLUTION, + // As above but not checked + MACRO_RESOLUTION_TRUST, + + // ======== Split + // Children: none + // Arguments: (F) + // --------------------- + // Conclusion: (or F (not F)) + SPLIT, + // ======== Equality resolution + // Children: (P1:F1, P2:(= F1 F2)) + // Arguments: none + // --------------------- + // Conclusion: (F2) + // Note this can optionally be seen as a macro for EQUIV_ELIM1+RESOLUTION. + EQ_RESOLVE, + // ======== Modus ponens + // Children: (P1:F1, P2:(=> F1 F2)) + // Arguments: none + // --------------------- + // Conclusion: (F2) + // Note this can optionally be seen as a macro for IMPLIES_ELIM+RESOLUTION. + MODUS_PONENS, + // ======== Double negation elimination + // Children: (P:(not (not F))) + // Arguments: none + // --------------------- + // Conclusion: (F) + NOT_NOT_ELIM, + // ======== Contradiction + // Children: (P1:F P2:(not F)) + // Arguments: () + // --------------------- + // Conclusion: false + CONTRA, + // ======== And elimination + // Children: (P:(and F1 ... Fn)) + // Arguments: (i) + // --------------------- + // Conclusion: (Fi) + AND_ELIM, + // ======== And introduction + // Children: (P1:F1 ... Pn:Fn)) + // Arguments: () + // --------------------- + // Conclusion: (and P1 ... Pn) + AND_INTRO, + // ======== Not Or elimination + // Children: (P:(not (or F1 ... Fn))) + // Arguments: (i) + // --------------------- + // Conclusion: (not Fi) + NOT_OR_ELIM, + // ======== Implication elimination + // Children: (P:(=> F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) F2) + IMPLIES_ELIM, + // ======== Not Implication elimination version 1 + // Children: (P:(not (=> F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (F1) + NOT_IMPLIES_ELIM1, + // ======== Not Implication elimination version 2 + // Children: (P:(not (=> F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (not F2) + NOT_IMPLIES_ELIM2, + // ======== Equivalence elimination version 1 + // Children: (P:(= F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) F2) + EQUIV_ELIM1, + // ======== Equivalence elimination version 2 + // Children: (P:(= F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or F1 (not F2)) + EQUIV_ELIM2, + // ======== Not Equivalence elimination version 1 + // Children: (P:(not (= F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or F1 F2) + NOT_EQUIV_ELIM1, + // ======== Not Equivalence elimination version 2 + // Children: (P:(not (= F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) (not F2)) + NOT_EQUIV_ELIM2, + // ======== XOR elimination version 1 + // Children: (P:(xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or F1 F2) + XOR_ELIM1, + // ======== XOR elimination version 2 + // Children: (P:(xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) (not F2)) + XOR_ELIM2, + // ======== Not XOR elimination version 1 + // Children: (P:(not (xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or F1 (not F2)) + NOT_XOR_ELIM1, + // ======== Not XOR elimination version 2 + // Children: (P:(not (xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) F2) + NOT_XOR_ELIM2, + // ======== ITE elimination version 1 + // Children: (P:(ite C F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or (not C) F1) + ITE_ELIM1, + // ======== ITE elimination version 2 + // Children: (P:(ite C F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or C F2) + ITE_ELIM2, + // ======== Not ITE elimination version 1 + // Children: (P:(not (ite C F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not C) (not F1)) + NOT_ITE_ELIM1, + // ======== Not ITE elimination version 1 + // Children: (P:(not (ite C F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or C (not F2)) + NOT_ITE_ELIM2, + + //================================================= De Morgan rules + // ======== Not And + // Children: (P:(not (and F1 ... Fn)) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) ... (not Fn)) + NOT_AND, + //================================================= CNF rules + // ======== CNF And Pos + // Children: () + // Arguments: ((and F1 ... Fn), i) + // --------------------- + // Conclusion: (or (not (and F1 ... Fn)) Fi) + CNF_AND_POS, + // ======== CNF And Neg + // Children: () + // Arguments: ((and F1 ... Fn)) + // --------------------- + // Conclusion: (or (and F1 ... Fn) (not F1) ... (not Fn)) + CNF_AND_NEG, + // ======== CNF Or Pos + // Children: () + // Arguments: ((or F1 ... Fn)) + // --------------------- + // Conclusion: (or (not (or F1 ... Fn)) F1 ... Fn) + CNF_OR_POS, + // ======== CNF Or Neg + // Children: () + // Arguments: ((or F1 ... Fn), i) + // --------------------- + // Conclusion: (or (or F1 ... Fn) (not Fi)) + CNF_OR_NEG, + // ======== CNF Implies Pos + // Children: () + // Arguments: ((implies F1 F2)) + // --------------------- + // Conclusion: (or (not (implies F1 F2)) (not F1) F2) + CNF_IMPLIES_POS, + // ======== CNF Implies Neg version 1 + // Children: () + // Arguments: ((implies F1 F2)) + // --------------------- + // Conclusion: (or (implies F1 F2) F1) + CNF_IMPLIES_NEG1, + // ======== CNF Implies Neg version 2 + // Children: () + // Arguments: ((implies F1 F2)) + // --------------------- + // Conclusion: (or (implies F1 F2) (not F2)) + CNF_IMPLIES_NEG2, + // ======== CNF Equiv Pos version 1 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (not (= F1 F2)) (not F1) F2) + CNF_EQUIV_POS1, + // ======== CNF Equiv Pos version 2 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (not (= F1 F2)) F1 (not F2)) + CNF_EQUIV_POS2, + // ======== CNF Equiv Neg version 1 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (= F1 F2) F1 F2) + CNF_EQUIV_NEG1, + // ======== CNF Equiv Neg version 2 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (= F1 F2) (not F1) (not F2)) + CNF_EQUIV_NEG2, + // ======== CNF Xor Pos version 1 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (not (xor F1 F2)) F1 F2) + CNF_XOR_POS1, + // ======== CNF Xor Pos version 2 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (not (xor F1 F2)) (not F1) (not F2)) + CNF_XOR_POS2, + // ======== CNF Xor Neg version 1 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (xor F1 F2) (not F1) F2) + CNF_XOR_NEG1, + // ======== CNF Xor Neg version 2 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (xor F1 F2) F1 (not F2)) + CNF_XOR_NEG2, + // ======== CNF ITE Pos version 1 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (not (ite C F1 F2)) (not C) F1) + CNF_ITE_POS1, + // ======== CNF ITE Pos version 2 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (not (ite C F1 F2)) C F2) + CNF_ITE_POS2, + // ======== CNF ITE Pos version 3 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (not (ite C F1 F2)) F1 F2) + CNF_ITE_POS3, + // ======== CNF ITE Neg version 1 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (ite C F1 F2) (not C) (not F1)) + CNF_ITE_NEG1, + // ======== CNF ITE Neg version 2 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (ite C F1 F2) C (not F2)) + CNF_ITE_NEG2, + // ======== CNF ITE Neg version 3 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (ite C F1 F2) (not F1) (not F2)) + CNF_ITE_NEG3, + + //================================================= Equality rules + // ======== Reflexive + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= t t) + REFL, + // ======== Symmetric + // Children: (P:(= t1 t2)) or (P:(not (= t1 t2))) + // Arguments: none + // ----------------------- + // Conclusion: (= t2 t1) or (not (= t2 t1)) + SYMM, + // ======== Transitivity + // Children: (P1:(= t1 t2), ..., Pn:(= t{n-1} tn)) + // Arguments: none + // ----------------------- + // Conclusion: (= t1 tn) + TRANS, + // ======== Congruence + // Children: (P1:(= t1 s1), ..., Pn:(= tn sn)) + // Arguments: ( f?) + // --------------------------------------------- + // Conclusion: (= ( f? t1 ... tn) ( f? s1 ... sn)) + // Notice that f must be provided iff is a parameterized kind, e.g. + // APPLY_UF. The actual node for is constructible via + // ProofRuleChecker::mkKindNode. + CONG, + // ======== True intro + // Children: (P:F) + // Arguments: none + // ---------------------------------------- + // Conclusion: (= F true) + TRUE_INTRO, + // ======== True elim + // Children: (P:(= F true)) + // Arguments: none + // ---------------------------------------- + // Conclusion: F + TRUE_ELIM, + // ======== False intro + // Children: (P:(not F)) + // Arguments: none + // ---------------------------------------- + // Conclusion: (= F false) + FALSE_INTRO, + // ======== False elim + // Children: (P:(= F false)) + // Arguments: none + // ---------------------------------------- + // Conclusion: (not F) + FALSE_ELIM, + // ======== HO trust + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= t TheoryUfRewriter::getHoApplyForApplyUf(t)) + // For example, this rule concludes (f x y) = (HO_APPLY (HO_APPLY f x) y) + HO_APP_ENCODE, + // ======== Congruence + // Children: (P1:(= f g), P2:(= t1 s1), ..., Pn+1:(= tn sn)) + // Arguments: () + // --------------------------------------------- + // Conclusion: (= (f t1 ... tn) (g s1 ... sn)) + // Notice that this rule is only used when the application kinds are APPLY_UF. + HO_CONG, + + //================================================= Array rules + // ======== Read over write + // Children: (P:(not (= i1 i2))) + // Arguments: ((select (store a i2 e) i1)) + // ---------------------------------------- + // Conclusion: (= (select (store a i2 e) i1) (select a i1)) + ARRAYS_READ_OVER_WRITE, + // ======== Read over write, contrapositive + // Children: (P:(not (= (select (store a i2 e) i1) (select a i1))) + // Arguments: none + // ---------------------------------------- + // Conclusion: (= i1 i2) + ARRAYS_READ_OVER_WRITE_CONTRA, + // ======== Read over write 1 + // Children: none + // Arguments: ((select (store a i e) i)) + // ---------------------------------------- + // Conclusion: (= (select (store a i e) i) e) + ARRAYS_READ_OVER_WRITE_1, + // ======== Extensionality + // Children: (P:(not (= a b))) + // Arguments: none + // ---------------------------------------- + // Conclusion: (not (= (select a k) (select b k))) + // where k is arrays::SkolemCache::getExtIndexSkolem((not (= a b))). + ARRAYS_EXT, + // ======== Array Trust + // Children: (P1 ... Pn) + // Arguments: (F) + // --------------------- + // Conclusion: F + ARRAYS_TRUST, + + //================================================= Bit-Vector rules + // Note: bitblast() represents the result of the bit-blasted term as a + // bit-vector consisting of the output bits of the bit-blasted circuit + // representation of the term. Terms are bit-blasted according to the + // strategies defined in + // theory/bv/bitblast/bitblast_strategies_template.h. + // ======== Bitblast + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= t bitblast(t)) + BV_BITBLAST, + // ======== Bitblast Bit-Vector Constant + // Children: none + // Arguments: (= t bitblast(t)) + // --------------------- + // Conclusion: (= t bitblast(t)) + BV_BITBLAST_CONST, + // ======== Bitblast Bit-Vector Variable + // Children: none + // Arguments: (= t bitblast(t)) + // --------------------- + // Conclusion: (= t bitblast(t)) + BV_BITBLAST_VAR, + // ======== Bitblast Bit-Vector Terms + // TODO cvc4-projects issue #275 + // Children: none + // Arguments: (= (KIND bitblast(child_1) ... bitblast(child_n)) bitblast(t)) + // --------------------- + // Conclusion: (= (KIND bitblast(child_1) ... bitblast(child_n)) bitblast(t)) + BV_BITBLAST_EQUAL, + BV_BITBLAST_ULT, + BV_BITBLAST_ULE, + BV_BITBLAST_UGT, + BV_BITBLAST_UGE, + BV_BITBLAST_SLT, + BV_BITBLAST_SLE, + BV_BITBLAST_SGT, + BV_BITBLAST_SGE, + BV_BITBLAST_NOT, + BV_BITBLAST_CONCAT, + BV_BITBLAST_AND, + BV_BITBLAST_OR, + BV_BITBLAST_XOR, + BV_BITBLAST_XNOR, + BV_BITBLAST_NAND, + BV_BITBLAST_NOR, + BV_BITBLAST_COMP, + BV_BITBLAST_MULT, + BV_BITBLAST_ADD, + BV_BITBLAST_SUB, + BV_BITBLAST_NEG, + BV_BITBLAST_UDIV, + BV_BITBLAST_UREM, + BV_BITBLAST_SDIV, + BV_BITBLAST_SREM, + BV_BITBLAST_SMOD, + BV_BITBLAST_SHL, + BV_BITBLAST_LSHR, + BV_BITBLAST_ASHR, + BV_BITBLAST_ULTBV, + BV_BITBLAST_SLTBV, + BV_BITBLAST_ITE, + BV_BITBLAST_EXTRACT, + BV_BITBLAST_REPEAT, + BV_BITBLAST_ZERO_EXTEND, + BV_BITBLAST_SIGN_EXTEND, + BV_BITBLAST_ROTATE_RIGHT, + BV_BITBLAST_ROTATE_LEFT, + // ======== Eager Atom + // Children: none + // Arguments: (F) + // --------------------- + // Conclusion: (= F F[0]) + // where F is of kind BITVECTOR_EAGER_ATOM + BV_EAGER_ATOM, + + //================================================= Datatype rules + // ======== Unification + // Children: (P:(= (C t1 ... tn) (C s1 ... sn))) + // Arguments: (i) + // ---------------------------------------- + // Conclusion: (= ti si) + // where C is a constructor. + DT_UNIF, + // ======== Instantiate + // Children: none + // Arguments: (t, n) + // ---------------------------------------- + // Conclusion: (= ((_ is C) t) (= t (C (sel_1 t) ... (sel_n t)))) + // where C is the n^th constructor of the type of T, and (_ is C) is the + // discriminator (tester) for C. + DT_INST, + // ======== Collapse + // Children: none + // Arguments: ((sel_i (C_j t_1 ... t_n))) + // ---------------------------------------- + // Conclusion: (= (sel_i (C_j t_1 ... t_n)) r) + // where C_j is a constructor, r is t_i if sel_i is a correctly applied + // selector, or TypeNode::mkGroundTerm() of the proper type otherwise. Notice + // that the use of mkGroundTerm differs from the rewriter which uses + // mkGroundValue in this case. + DT_COLLAPSE, + // ======== Split + // Children: none + // Arguments: (t) + // ---------------------------------------- + // Conclusion: (or ((_ is C1) t) ... ((_ is Cn) t)) + DT_SPLIT, + // ======== Clash + // Children: (P1:((_ is Ci) t), P2: ((_ is Cj) t)) + // Arguments: none + // ---------------------------------------- + // Conclusion: false + // for i != j. + DT_CLASH, + // ======== Datatype Trust + // Children: (P1 ... Pn) + // Arguments: (F) + // --------------------- + // Conclusion: F + DT_TRUST, + + //================================================= Quantifiers rules + // ======== Skolem intro + // Children: none + // Arguments: (k) + // ---------------------------------------- + // Conclusion: (= k t) + // where t is the original form of skolem k. + SKOLEM_INTRO, + // ======== Exists intro + // Children: (P:F[t]) + // Arguments: ((exists ((x T)) F[x])) + // ---------------------------------------- + // Conclusion: (exists ((x T)) F[x]) + // This rule verifies that F[x] indeed matches F[t] with a substitution + // over x. + EXISTS_INTRO, + // ======== Skolemize + // Children: (P:(exists ((x1 T1) ... (xn Tn)) F)) + // Arguments: none + // ---------------------------------------- + // Conclusion: F*sigma + // sigma maps x1 ... xn to their representative skolems obtained by + // SkolemManager::mkSkolemize, returned in the skolems argument of that + // method. Alternatively, can use negated forall as a premise. The witness + // terms for the returned skolems can be obtained by + // SkolemManager::getWitnessForm. + SKOLEMIZE, + // ======== Instantiate + // Children: (P:(forall ((x1 T1) ... (xn Tn)) F)) + // Arguments: (t1 ... tn) + // ---------------------------------------- + // Conclusion: F*sigma + // sigma maps x1 ... xn to t1 ... tn. + INSTANTIATE, + + //================================================= String rules + //======================== Core solver + // ======== Concat eq + // Children: (P1:(= (str.++ t1 ... tn t) (str.++ t1 ... tn s))) + // Arguments: (b), indicating if reverse direction + // --------------------- + // Conclusion: (= t s) + // + // Notice that t or s may be empty, in which case they are implicit in the + // concatenation above. For example, if + // P1 concludes (= x (str.++ x z)), then + // (CONCAT_EQ P1 :args false) concludes (= "" z) + // + // Also note that constants are split, such that if + // P1 concludes (= (str.++ "abc" x) (str.++ "a" y)), then + // (CONCAT_EQ P1 :args false) concludes (= (str.++ "bc" x) y) + // This splitting is done only for constants such that Word::splitConstant + // returns non-null. + CONCAT_EQ, + // ======== Concat unify + // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), + // P2:(= (str.len t1) (str.len s1))) + // Arguments: (b), indicating if reverse direction + // --------------------- + // Conclusion: (= t1 s1) + CONCAT_UNIFY, + // ======== Concat conflict + // Children: (P1:(= (str.++ c1 t) (str.++ c2 s))) + // Arguments: (b), indicating if reverse direction + // --------------------- + // Conclusion: false + // Where c1, c2 are constants such that Word::splitConstant(c1,c2,index,b) + // is null, in other words, neither is a prefix of the other. + CONCAT_CONFLICT, + // ======== Concat split + // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), + // P2:(not (= (str.len t1) (str.len s1)))) + // Arguments: (false) + // --------------------- + // Conclusion: (or (= t1 (str.++ s1 r_t)) (= s1 (str.++ t1 r_s))) + // where + // r_t = (skolem (suf t1 (str.len s1)))), + // r_s = (skolem (suf s1 (str.len t1)))). + // + // or the reverse form of the above: + // + // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), + // P2:(not (= (str.len t2) (str.len s2)))) + // Arguments: (true) + // --------------------- + // Conclusion: (or (= t2 (str.++ r_t s2)) (= s2 (str.++ r_s t2))) + // where + // r_t = (skolem (pre t2 (- (str.len t2) (str.len s2))))), + // r_s = (skolem (pre s2 (- (str.len s2) (str.len t2))))). + // + // Above, (suf x n) is shorthand for (str.substr x n (- (str.len x) n)) and + // (pre x n) is shorthand for (str.substr x 0 n). + CONCAT_SPLIT, + // ======== Concat constant split + // Children: (P1:(= (str.++ t1 t2) (str.++ c s2)), + // P2:(not (= (str.len t1) 0))) + // Arguments: (false) + // --------------------- + // Conclusion: (= t1 (str.++ c r)) + // where + // r = (skolem (suf t1 1)). + // + // or the reverse form of the above: + // + // Children: (P1:(= (str.++ t1 t2) (str.++ s1 c)), + // P2:(not (= (str.len t2) 0))) + // Arguments: (true) + // --------------------- + // Conclusion: (= t2 (str.++ r c)) + // where + // r = (skolem (pre t2 (- (str.len t2) 1))). + CONCAT_CSPLIT, + // ======== Concat length propagate + // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), + // P2:(> (str.len t1) (str.len s1))) + // Arguments: (false) + // --------------------- + // Conclusion: (= t1 (str.++ s1 r_t)) + // where + // r_t = (skolem (suf t1 (str.len s1))) + // + // or the reverse form of the above: + // + // Children: (P1:(= (str.++ t1 t2) (str.++ s1 s2)), + // P2:(> (str.len t2) (str.len s2))) + // Arguments: (false) + // --------------------- + // Conclusion: (= t2 (str.++ r_t s2)) + // where + // r_t = (skolem (pre t2 (- (str.len t2) (str.len s2)))). + CONCAT_LPROP, + // ======== Concat constant propagate + // Children: (P1:(= (str.++ t1 w1 t2) (str.++ w2 s)), + // P2:(not (= (str.len t1) 0))) + // Arguments: (false) + // --------------------- + // Conclusion: (= t1 (str.++ w3 r)) + // where + // w1, w2, w3, w4 are words, + // w3 is (pre w2 p), + // w4 is (suf w2 p), + // p = Word::overlap((suf w2 1), w1), + // r = (skolem (suf t1 (str.len w3))). + // In other words, w4 is the largest suffix of (suf w2 1) that can contain a + // prefix of w1; since t1 is non-empty, w3 must therefore be contained in t1. + // + // or the reverse form of the above: + // + // Children: (P1:(= (str.++ t1 w1 t2) (str.++ s w2)), + // P2:(not (= (str.len t2) 0))) + // Arguments: (true) + // --------------------- + // Conclusion: (= t2 (str.++ r w3)) + // where + // w1, w2, w3, w4 are words, + // w3 is (suf w2 (- (str.len w2) p)), + // w4 is (pre w2 (- (str.len w2) p)), + // p = Word::roverlap((pre w2 (- (str.len w2) 1)), w1), + // r = (skolem (pre t2 (- (str.len t2) (str.len w3)))). + // In other words, w4 is the largest prefix of (pre w2 (- (str.len w2) 1)) + // that can contain a suffix of w1; since t2 is non-empty, w3 must therefore + // be contained in t2. + CONCAT_CPROP, + // ======== String decompose + // Children: (P1: (>= (str.len t) n) + // Arguments: (false) + // --------------------- + // Conclusion: (and (= t (str.++ w1 w2)) (= (str.len w1) n)) + // or + // Children: (P1: (>= (str.len t) n) + // Arguments: (true) + // --------------------- + // Conclusion: (and (= t (str.++ w1 w2)) (= (str.len w2) n)) + // where + // w1 is (skolem (pre t n)) + // w2 is (skolem (suf t n)) + STRING_DECOMPOSE, + // ======== Length positive + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (or (and (= (str.len t) 0) (= t "")) (> (str.len t) 0)) + STRING_LENGTH_POS, + // ======== Length non-empty + // Children: (P1:(not (= t ""))) + // Arguments: none + // --------------------- + // Conclusion: (not (= (str.len t) 0)) + STRING_LENGTH_NON_EMPTY, + //======================== Extended functions + // ======== Reduction + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (and R (= t w)) + // where w = strings::StringsPreprocess::reduce(t, R, ...). + // In other words, R is the reduction predicate for extended term t, and w is + // (skolem t) + // Notice that the free variables of R are w and the free variables of t. + STRING_REDUCTION, + // ======== Eager Reduction + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: R + // where R = strings::TermRegistry::eagerReduce(t). + STRING_EAGER_REDUCTION, + //======================== Regular expressions + // ======== Regular expression intersection + // Children: (P:(str.in.re t R1), P:(str.in.re t R2)) + // Arguments: none + // --------------------- + // Conclusion: (str.in.re t (re.inter R1 R2)). + RE_INTER, + // ======== Regular expression unfold positive + // Children: (P:(str.in.re t R)) + // Arguments: none + // --------------------- + // Conclusion:(RegExpOpr::reduceRegExpPos((str.in.re t R))), + // corresponding to the one-step unfolding of the premise. + RE_UNFOLD_POS, + // ======== Regular expression unfold negative + // Children: (P:(not (str.in.re t R))) + // Arguments: none + // --------------------- + // Conclusion:(RegExpOpr::reduceRegExpNeg((not (str.in.re t R)))), + // corresponding to the one-step unfolding of the premise. + RE_UNFOLD_NEG, + // ======== Regular expression unfold negative concat fixed + // Children: (P:(not (str.in.re t R))) + // Arguments: none + // --------------------- + // Conclusion:(RegExpOpr::reduceRegExpNegConcatFixed((not (str.in.re t + // R)),L,i)) where RegExpOpr::getRegExpConcatFixed((not (str.in.re t R)), i) = + // L. corresponding to the one-step unfolding of the premise, optimized for + // fixed length of component i of the regular expression concatenation R. + RE_UNFOLD_NEG_CONCAT_FIXED, + // ======== Regular expression elimination + // Children: none + // Arguments: (F, b) + // --------------------- + // Conclusion: (= F strings::RegExpElimination::eliminate(F, b)) + // where b is a Boolean indicating whether we are using aggressive + // eliminations. Notice this rule concludes (= F F) if no eliminations + // are performed for F. + RE_ELIM, + //======================== Code points + // Children: none + // Arguments: (t, s) + // --------------------- + // Conclusion:(or (= (str.code t) (- 1)) + // (not (= (str.code t) (str.code s))) + // (not (= t s))) + STRING_CODE_INJ, + //======================== Sequence unit + // Children: (P:(= (seq.unit x) (seq.unit y))) + // Arguments: none + // --------------------- + // Conclusion:(= x y) + // Also applies to the case where (seq.unit y) is a constant sequence + // of length one. + STRING_SEQ_UNIT_INJ, + // ======== String Trust + // Children: none + // Arguments: (Q) + // --------------------- + // Conclusion: (Q) + STRING_TRUST, + + //================================================= Arithmetic rules + // ======== Adding Inequalities + // Note: an ArithLiteral is a term of the form (>< poly const) + // where + // >< is >=, >, ==, <, <=, or not(== ...). + // poly is a polynomial + // const is a rational constant + + // Children: (P1:l1, ..., Pn:ln) + // where each li is an ArithLiteral + // not(= ...) is dis-allowed! + // + // Arguments: (k1, ..., kn), non-zero reals + // --------------------- + // Conclusion: (>< t1 t2) + // where >< is the fusion of the combination of the >< is always one of <, <= + // NB: this implies that lower bounds must have negative ki, + // and upper bounds must have positive ki. + // t1 is the sum of the scaled polynomials (k_1 * poly_1 + ... + k_n * + // poly_n) t2 is the sum of the scaled constants (k_1 * const_1 + ... + k_n + // * const_n) + MACRO_ARITH_SCALE_SUM_UB, + // ======== Sum Upper Bounds + // Children: (P1, ... , Pn) + // where each Pi has form (>< L R) + // where >< is < if any > i c)) + // where i has integer type. + // Arguments: none + // --------------------- + // Conclusion: (>= i leastIntGreaterThan(c)}) + INT_TIGHT_LB, + // ======== Trichotomy of the reals + // Children: (A B) + // Arguments: (C) + // --------------------- + // Conclusion: (C), + // where (not A) (not B) and C + // are (> x c) (< x c) and (= x c) + // in some order + // note that "not" here denotes arithmetic negation, flipping + // >= to <, etc. + ARITH_TRICHOTOMY, + // ======== Arithmetic operator elimination + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: arith::OperatorElim::getAxiomFor(t) + ARITH_OP_ELIM_AXIOM, + // ======== Int Trust + // Children: (P1 ... Pn) + // Arguments: (Q) + // --------------------- + // Conclusion: (Q) + INT_TRUST, + + //======== Multiplication sign inference + // Children: none + // Arguments: (f1, ..., fk, m) + // --------------------- + // Conclusion: (=> (and f1 ... fk) (~ m 0)) + // Where f1, ..., fk are variables compared to zero (less, greater or not + // equal), m is a monomial from these variables, and ~ is the comparison (less + // or greater) that results from the signs of the variables. All variables + // with even exponent in m should be given as not equal to zero while all + // variables with odd exponent in m should be given as less or greater than + // zero. + ARITH_MULT_SIGN, + //======== Multiplication with positive factor + // Children: none + // Arguments: (m, (rel lhs rhs)) + // --------------------- + // Conclusion: (=> (and (> m 0) (rel lhs rhs)) (rel (* m lhs) (* m rhs))) + // Where rel is a relation symbol. + ARITH_MULT_POS, + //======== Multiplication with negative factor + // Children: none + // Arguments: (m, (rel lhs rhs)) + // --------------------- + // Conclusion: (=> (and (< m 0) (rel lhs rhs)) (rel_inv (* m lhs) (* m rhs))) + // Where rel is a relation symbol and rel_inv the inverted relation symbol. + ARITH_MULT_NEG, + //======== Multiplication tangent plane + // Children: none + // Arguments: (t, x, y, a, b, sgn) + // --------------------- + // Conclusion: + // sgn=-1: (= (<= t tplane) (or (and (<= x a) (>= y b)) (and (>= x a) (<= y + // b))) sgn= 1: (= (>= t tplane) (or (and (<= x a) (<= y b)) (and (>= x a) + // (>= y b))) + // Where x,y are real terms (variables or extended terms), t = (* x y) + // (possibly under rewriting), a,b are real constants, and sgn is either -1 + // or 1. tplane is the tangent plane of x*y at (a,b): b*x + a*y - a*b + ARITH_MULT_TANGENT, + + // ================ Lemmas for transcendentals + //======== Assert bounds on PI + // Children: none + // Arguments: (l, u) + // --------------------- + // Conclusion: (and (>= real.pi l) (<= real.pi u)) + // Where l (u) is a valid lower (upper) bound on pi. + ARITH_TRANS_PI, + //======== Exp at negative values + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= (< t 0) (< (exp t) 1)) + ARITH_TRANS_EXP_NEG, + //======== Exp is always positive + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (> (exp t) 0) + ARITH_TRANS_EXP_POSITIVITY, + //======== Exp grows super-linearly for positive values + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (or (<= t 0) (> exp(t) (+ t 1))) + ARITH_TRANS_EXP_SUPER_LIN, + //======== Exp at zero + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= (= t 0) (= (exp t) 1)) + ARITH_TRANS_EXP_ZERO, + //======== Exp is approximated from above for negative values + // Children: none + // Arguments: (d, t, l, u) + // --------------------- + // Conclusion: (=> (and (>= t l) (<= t u)) (<= (exp t) (secant exp l u t)) + // Where d is an even positive number, t an arithmetic term and l (u) a lower + // (upper) bound on t. Let p be the d'th taylor polynomial at zero (also + // called the Maclaurin series) of the exponential function. (secant exp l u + // t) denotes the secant of p from (l, exp(l)) to (u, exp(u)) evaluated at t, + // calculated as follows: + // (p(l) - p(u)) / (l - u) * (t - l) + p(l) + // The lemma states that if t is between l and u, then (exp t) is below the + // secant of p from l to u. + ARITH_TRANS_EXP_APPROX_ABOVE_NEG, + //======== Exp is approximated from above for positive values + // Children: none + // Arguments: (d, t, l, u) + // --------------------- + // Conclusion: (=> (and (>= t l) (<= t u)) (<= (exp t) (secant-pos exp l u t)) + // Where d is an even positive number, t an arithmetic term and l (u) a lower + // (upper) bound on t. Let p* be a modification of the d'th taylor polynomial + // at zero (also called the Maclaurin series) of the exponential function as + // follows where p(d-1) is the regular Maclaurin series of degree d-1: + // p* = p(d-1) * (1 + t^n / n!) + // (secant-pos exp l u t) denotes the secant of p from (l, exp(l)) to (u, + // exp(u)) evaluated at t, calculated as follows: + // (p(l) - p(u)) / (l - u) * (t - l) + p(l) + // The lemma states that if t is between l and u, then (exp t) is below the + // secant of p from l to u. + ARITH_TRANS_EXP_APPROX_ABOVE_POS, + //======== Exp is approximated from below + // Children: none + // Arguments: (d, t) + // --------------------- + // Conclusion: (>= (exp t) (maclaurin exp d t)) + // Where d is an odd positive number and (maclaurin exp d t) is the d'th + // taylor polynomial at zero (also called the Maclaurin series) of the + // exponential function evaluated at t. The Maclaurin series for the + // exponential function is the following: + // e^x = \sum_{n=0}^{\infty} x^n / n! + ARITH_TRANS_EXP_APPROX_BELOW, + //======== Sine is always between -1 and 1 + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (and (<= (sin t) 1) (>= (sin t) (- 1))) + ARITH_TRANS_SINE_BOUNDS, + //======== Sine arg shifted to -pi..pi + // Children: none + // Arguments: (x, y, s) + // --------------------- + // Conclusion: (and + // (<= -pi y pi) + // (= (sin y) (sin x)) + // (ite (<= -pi x pi) (= x y) (= x (+ y (* 2 pi s)))) + // ) + // Where x is the argument to sine, y is a new real skolem that is x shifted + // into -pi..pi and s is a new integer skolem that is the number of phases y + // is shifted. + ARITH_TRANS_SINE_SHIFT, + //======== Sine is symmetric with respect to negation of the argument + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= (- (sin t) (sin (- t))) 0) + ARITH_TRANS_SINE_SYMMETRY, + //======== Sine is bounded by the tangent at zero + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (and + // (=> (> t 0) (< (sin t) t)) + // (=> (< t 0) (> (sin t) t)) + // ) + ARITH_TRANS_SINE_TANGENT_ZERO, + //======== Sine is bounded by the tangents at -pi and pi + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (and + // (=> (> t -pi) (> (sin t) (- -pi t))) + // (=> (< t pi) (< (sin t) (- pi t))) + // ) + ARITH_TRANS_SINE_TANGENT_PI, + //======== Sine is approximated from above for negative values + // Children: none + // Arguments: (d, t, lb, ub, l, u) + // --------------------- + // Conclusion: (=> (and (>= t lb) (<= t ub)) (<= (sin t) (secant sin l u t)) + // Where d is an even positive number, t an arithmetic term, lb (ub) a + // symbolic lower (upper) bound on t (possibly containing pi) and l (u) the + // evaluated lower (upper) bound on t. Let p be the d'th taylor polynomial at + // zero (also called the Maclaurin series) of the sine function. (secant sin l + // u t) denotes the secant of p from (l, sin(l)) to (u, sin(u)) evaluated at + // t, calculated as follows: + // (p(l) - p(u)) / (l - u) * (t - l) + p(l) + // The lemma states that if t is between l and u, then (sin t) is below the + // secant of p from l to u. + ARITH_TRANS_SINE_APPROX_ABOVE_NEG, + //======== Sine is approximated from above for positive values + // Children: none + // Arguments: (d, t, c, lb, ub) + // --------------------- + // Conclusion: (=> (and (>= t lb) (<= t ub)) (<= (sin t) (upper sin c)) + // Where d is an even positive number, t an arithmetic term, c an arithmetic + // constant, and lb (ub) a symbolic lower (upper) bound on t (possibly + // containing pi). Let p be the d'th taylor polynomial at zero (also called + // the Maclaurin series) of the sine function. (upper sin c) denotes the upper + // bound on sin(c) given by p and lb,ub are such that sin(t) is the maximum of + // the sine function on (lb,ub). + ARITH_TRANS_SINE_APPROX_ABOVE_POS, + //======== Sine is approximated from below for negative values + // Children: none + // Arguments: (d, t, c, lb, ub) + // --------------------- + // Conclusion: (=> (and (>= t lb) (<= t ub)) (>= (sin t) (lower sin c)) + // Where d is an even positive number, t an arithmetic term, c an arithmetic + // constant, and lb (ub) a symbolic lower (upper) bound on t (possibly + // containing pi). Let p be the d'th taylor polynomial at zero (also called + // the Maclaurin series) of the sine function. (lower sin c) denotes the lower + // bound on sin(c) given by p and lb,ub are such that sin(t) is the minimum of + // the sine function on (lb,ub). + ARITH_TRANS_SINE_APPROX_BELOW_NEG, + //======== Sine is approximated from below for positive values + // Children: none + // Arguments: (d, t, lb, ub, l, u) + // --------------------- + // Conclusion: (=> (and (>= t lb) (<= t ub)) (>= (sin t) (secant sin l u t)) + // Where d is an even positive number, t an arithmetic term, lb (ub) a + // symbolic lower (upper) bound on t (possibly containing pi) and l (u) the + // evaluated lower (upper) bound on t. Let p be the d'th taylor polynomial at + // zero (also called the Maclaurin series) of the sine function. (secant sin l + // u t) denotes the secant of p from (l, sin(l)) to (u, sin(u)) evaluated at + // t, calculated as follows: + // (p(l) - p(u)) / (l - u) * (t - l) + p(l) + // The lemma states that if t is between l and u, then (sin t) is above the + // secant of p from l to u. + ARITH_TRANS_SINE_APPROX_BELOW_POS, + + // ================ CAD Lemmas + // We use IRP for IndexedRootPredicate. + // + // A formula "Interval" describes that a variable (xn is none is given) is + // within a particular interval whose bounds are given as IRPs. It is either + // an open interval or a point interval: + // (IRP k poly) < xn < (IRP k poly) + // xn == (IRP k poly) + // + // A formula "Cell" describes a portion + // of the real space in the following form: + // Interval(x1) and Interval(x2) and ... + // We implicitly assume a Cell to go up to n-1 (and can be empty). + // + // A formula "Covering" is a set of Intervals, implying that xn can be in + // neither of these intervals. To be a covering (of the real line), the union + // of these intervals should be the real numbers. + // ======== CAD direct conflict + // Children (Cell, A) + // --------------------- + // Conclusion: (false) + // A direct interval is generated from an assumption A (in variables x1...xn) + // over a Cell (in variables x1...xn). It derives that A evaluates to false + // over the Cell. In the actual algorithm, it means that xn can not be in the + // topmost interval of the Cell. + ARITH_NL_CAD_DIRECT, + // ======== CAD recursive interval + // Children (Cell, Covering) + // --------------------- + // Conclusion: (false) + // A recursive interval is generated from a Covering (for xn) over a Cell + // (in variables x1...xn-1). It generates the conclusion that no xn exists + // that extends the Cell and satisfies all assumptions. + ARITH_NL_CAD_RECURSIVE, + + //================================================= Unknown rule + UNKNOWN, +}; + +/** + * Converts a proof rule to a string. Note: This function is also used in + * `safe_print()`. Changing this function name or signature will result in + * `safe_print()` printing "" instead of the proper strings for + * the enum values. + * + * @param id The proof rule + * @return The name of the proof rule + */ +const char* toString(PfRule id); + +/** + * Writes a proof rule name to a stream. + * + * @param out The stream to write to + * @param id The proof rule to write to the stream + * @return The stream + */ +std::ostream& operator<<(std::ostream& out, PfRule id); + +/** Hash function for proof rules */ +struct PfRuleHashFunction +{ + size_t operator()(PfRule id) const; +}; /* struct PfRuleHashFunction */ + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_RULE_H */ diff --git a/src/proof/proof_set.h b/src/proof/proof_set.h new file mode 100644 index 000000000..4015e0466 --- /dev/null +++ b/src/proof/proof_set.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * Top contributors (to current version): + * Gereon Kremer, Andrew Reynolds + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof set utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_SET_H +#define CVC5__PROOF__PROOF_SET_H + +#include + +#include "context/cdlist.h" +#include "context/context.h" +#include "proof/proof_node_manager.h" + +namespace cvc5 { + +/** + * A (context-dependent) set of proofs, which is used for memory + * management purposes. + */ +template +class CDProofSet +{ + public: + CDProofSet(ProofNodeManager* pnm, + context::Context* c, + std::string namePrefix = "Proof") + : d_pnm(pnm), d_proofs(c), d_namePrefix(namePrefix) + { + } + /** + * Allocate a new proof. + * + * This returns a fresh proof object that remains alive in the context given + * to this class. Internally, this adds a new proof of type T to a + * context-dependent list of proofs and passes the following arguments to the + * T constructor: + * pnm, args..., name + * where pnm is the proof node manager + * provided to this proof set upon construction, args... are the arguments to + * allocateProof() and name is the namePrefix with an appended index. + */ + template + T* allocateProof(Args&&... args) + { + d_proofs.push_back(std::make_shared( + d_pnm, + std::forward(args)..., + d_namePrefix + "_" + std::to_string(d_proofs.size()))); + return d_proofs.back().get(); + } + + protected: + /** The proof node manager */ + ProofNodeManager* d_pnm; + /** A context-dependent list of lazy proofs. */ + context::CDList> d_proofs; + /** The name prefix of the lazy proofs */ + std::string d_namePrefix; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__LAZY_PROOF_SET_H */ diff --git a/src/proof/proof_step_buffer.cpp b/src/proof/proof_step_buffer.cpp new file mode 100644 index 000000000..309802505 --- /dev/null +++ b/src/proof/proof_step_buffer.cpp @@ -0,0 +1,112 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of proof step and proof step buffer utilities. + */ + +#include "proof/proof_step_buffer.h" + +#include "proof/proof_checker.h" + +using namespace cvc5::kind; + +namespace cvc5 { + +ProofStep::ProofStep() : d_rule(PfRule::UNKNOWN) {} +ProofStep::ProofStep(PfRule r, + const std::vector& children, + const std::vector& args) + : d_rule(r), d_children(children), d_args(args) +{ +} +std::ostream& operator<<(std::ostream& out, ProofStep step) +{ + out << "(step " << step.d_rule; + for (const Node& c : step.d_children) + { + out << " " << c; + } + if (!step.d_args.empty()) + { + out << " :args"; + for (const Node& a : step.d_args) + { + out << " " << a; + } + } + out << ")"; + return out; +} + +ProofStepBuffer::ProofStepBuffer(ProofChecker* pc) : d_checker(pc) {} + +Node ProofStepBuffer::tryStep(PfRule id, + const std::vector& children, + const std::vector& args, + Node expected) +{ + if (d_checker == nullptr) + { + Assert(false) << "ProofStepBuffer::ProofStepBuffer: no proof checker."; + return Node::null(); + } + Node res = + d_checker->checkDebug(id, children, args, expected, "pf-step-buffer"); + if (!res.isNull()) + { + // add proof step + d_steps.push_back( + std::pair(res, ProofStep(id, children, args))); + } + return res; +} + +void ProofStepBuffer::addStep(PfRule id, + const std::vector& children, + const std::vector& args, + Node expected) +{ + d_steps.push_back( + std::pair(expected, ProofStep(id, children, args))); +} + +void ProofStepBuffer::addSteps(ProofStepBuffer& psb) +{ + const std::vector>& steps = psb.getSteps(); + for (const std::pair& step : steps) + { + addStep(step.second.d_rule, + step.second.d_children, + step.second.d_args, + step.first); + } +} + +void ProofStepBuffer::popStep() +{ + Assert(!d_steps.empty()); + if (!d_steps.empty()) + { + d_steps.pop_back(); + } +} + +size_t ProofStepBuffer::getNumSteps() const { return d_steps.size(); } + +const std::vector>& ProofStepBuffer::getSteps() const +{ + return d_steps; +} + +void ProofStepBuffer::clear() { d_steps.clear(); } + +} // namespace cvc5 diff --git a/src/proof/proof_step_buffer.h b/src/proof/proof_step_buffer.h new file mode 100644 index 000000000..4c1bfa8a3 --- /dev/null +++ b/src/proof/proof_step_buffer.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Proof step and proof step buffer utilities. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_STEP_BUFFER_H +#define CVC5__PROOF__PROOF_STEP_BUFFER_H + +#include + +#include "expr/node.h" +#include "proof/proof_rule.h" + +namespace cvc5 { + +class ProofChecker; + +/** + * Information for constructing a step in a CDProof. Notice that the conclusion + * of the proof step is intentionally not included in this data structure. + * Instead, it is intended that conclusions may be associated with proof steps + * based on e.g. the result of proof checking. + */ +class ProofStep +{ + public: + ProofStep(); + ProofStep(PfRule r, + const std::vector& children, + const std::vector& args); + /** The proof rule */ + PfRule d_rule; + /** The proof children */ + std::vector d_children; + /** The proof arguments */ + std::vector d_args; +}; +std::ostream& operator<<(std::ostream& out, ProofStep step); + +/** + * Class used to speculatively try and buffer a set of proof steps before + * sending them to a proof object. + */ +class ProofStepBuffer +{ + public: + ProofStepBuffer(ProofChecker* pc = nullptr); + ~ProofStepBuffer() {} + /** + * Returns the conclusion of the proof step, as determined by the proof + * checker of the given proof. If this is non-null, then the given step + * is added to the buffer maintained by this class. + * + * If expected is non-null, then this method returns null if the result of + * checking is not equal to expected. + */ + Node tryStep(PfRule id, + const std::vector& children, + const std::vector& args, + Node expected = Node::null()); + /** Same as above, without checking */ + void addStep(PfRule id, + const std::vector& children, + const std::vector& args, + Node expected); + /** Multi-step version */ + void addSteps(ProofStepBuffer& psb); + /** pop step */ + void popStep(); + /** Get num steps */ + size_t getNumSteps() const; + /** Get steps */ + const std::vector>& getSteps() const; + /** Clear */ + void clear(); + + private: + /** The proof checker*/ + ProofChecker* d_checker; + /** the queued proof steps */ + std::vector> d_steps; +}; + +} // namespace cvc5 + +#endif /* CVC5__PROOF__PROOF_STEP_BUFFER_H */ diff --git a/src/proof/theory_proof_step_buffer.cpp b/src/proof/theory_proof_step_buffer.cpp new file mode 100644 index 000000000..f00c664c8 --- /dev/null +++ b/src/proof/theory_proof_step_buffer.cpp @@ -0,0 +1,240 @@ +/****************************************************************************** + * Top contributors (to current version): + * Haniel Barbosa, Andrew Reynolds, Aina Niemetz + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of theory proof step buffer utility. + */ + +#include "proof/theory_proof_step_buffer.h" + +#include "proof/proof.h" + +using namespace cvc5::kind; + +namespace cvc5 { +namespace theory { + +TheoryProofStepBuffer::TheoryProofStepBuffer(ProofChecker* pc) + : ProofStepBuffer(pc) +{ +} + +bool TheoryProofStepBuffer::applyEqIntro(Node src, + Node tgt, + const std::vector& exp, + MethodId ids, + MethodId ida, + MethodId idr) +{ + std::vector args; + args.push_back(src); + builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); + Node res = tryStep(PfRule::MACRO_SR_EQ_INTRO, exp, args); + if (res.isNull()) + { + // failed to apply + return false; + } + // should have concluded the expected equality + Node expected = src.eqNode(tgt); + if (res != expected) + { + // did not provide the correct target + popStep(); + return false; + } + // successfully proved src == tgt. + return true; +} + +bool TheoryProofStepBuffer::applyPredTransform(Node src, + Node tgt, + const std::vector& exp, + MethodId ids, + MethodId ida, + MethodId idr) +{ + // symmetric equalities + if (CDProof::isSame(src, tgt)) + { + return true; + } + std::vector children; + children.push_back(src); + std::vector args; + // try to prove that tgt rewrites to src + children.insert(children.end(), exp.begin(), exp.end()); + args.push_back(tgt); + builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); + Node res = tryStep(PfRule::MACRO_SR_PRED_TRANSFORM, children, args); + if (res.isNull()) + { + // failed to apply + return false; + } + // should definitely have concluded tgt + Assert(res == tgt); + return true; +} + +bool TheoryProofStepBuffer::applyPredIntro(Node tgt, + const std::vector& exp, + MethodId ids, + MethodId ida, + MethodId idr) +{ + std::vector args; + args.push_back(tgt); + builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); + Node res = tryStep(PfRule::MACRO_SR_PRED_INTRO, exp, args); + if (res.isNull()) + { + return false; + } + Assert(res == tgt); + return true; +} + +Node TheoryProofStepBuffer::applyPredElim(Node src, + const std::vector& exp, + MethodId ids, + MethodId ida, + MethodId idr) +{ + std::vector children; + children.push_back(src); + children.insert(children.end(), exp.begin(), exp.end()); + std::vector args; + builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); + Node srcRew = tryStep(PfRule::MACRO_SR_PRED_ELIM, children, args); + if (CDProof::isSame(src, srcRew)) + { + popStep(); + } + return srcRew; +} + +Node TheoryProofStepBuffer::factorReorderElimDoubleNeg(Node n) +{ + if (n.getKind() != kind::OR) + { + return elimDoubleNegLit(n); + } + NodeManager* nm = NodeManager::currentNM(); + std::vector children{n.begin(), n.end()}; + std::vector childrenEqs; + // eliminate double neg for each lit. Do it first because it may expose + // duplicates + bool hasDoubleNeg = false; + for (unsigned i = 0; i < children.size(); ++i) + { + if (children[i].getKind() == kind::NOT + && children[i][0].getKind() == kind::NOT) + { + hasDoubleNeg = true; + childrenEqs.push_back(children[i].eqNode(children[i][0][0])); + addStep(PfRule::MACRO_SR_PRED_INTRO, + {}, + {childrenEqs.back()}, + childrenEqs.back()); + // update child + children[i] = children[i][0][0]; + } + else + { + childrenEqs.push_back(children[i].eqNode(children[i])); + addStep(PfRule::REFL, {}, {children[i]}, childrenEqs.back()); + } + } + if (hasDoubleNeg) + { + Node oldn = n; + n = nm->mkNode(kind::OR, children); + // Create a congruence step to justify replacement of each doubly negated + // literal. This is done to avoid having to use MACRO_SR_PRED_TRANSFORM + // from the old clause to the new one, which, under the standard rewriter, + // may not hold. An example is + // + // --------------------------------------------------------------------- + // (or (or (not x2) x1 x2) (not (not x2))) = (or (or (not x2) x1 x2) x2) + // + // which fails due to factoring not happening after flattening. + // + // Using congruence only the + // + // ------------------ MACRO_SR_PRED_INTRO + // (not (not t)) = t + // + // steps are added, which, since double negation is eliminated in a + // pre-rewrite in the Boolean rewriter, will always hold under the + // standard rewriter. + Node congEq = oldn.eqNode(n); + addStep(PfRule::CONG, + childrenEqs, + {ProofRuleChecker::mkKindNode(kind::OR)}, + congEq); + // add an equality resolution step to derive normalize clause + addStep(PfRule::EQ_RESOLVE, {oldn, congEq}, {}, n); + } + children.clear(); + // remove duplicates while keeping the order of children + std::unordered_set clauseSet; + unsigned size = n.getNumChildren(); + for (unsigned i = 0; i < size; ++i) + { + if (clauseSet.count(n[i])) + { + continue; + } + children.push_back(n[i]); + clauseSet.insert(n[i]); + } + // if factoring changed + if (children.size() < size) + { + Node factored = children.empty() + ? nm->mkConst(false) + : children.size() == 1 ? children[0] + : nm->mkNode(kind::OR, children); + // don't overwrite what already has a proof step to avoid cycles + addStep(PfRule::FACTORING, {n}, {}, factored); + n = factored; + } + // nothing to order + if (children.size() < 2) + { + return n; + } + // order + std::sort(children.begin(), children.end()); + Node ordered = nm->mkNode(kind::OR, children); + // if ordering changed + if (ordered != n) + { + // don't overwrite what already has a proof step to avoid cycles + addStep(PfRule::REORDERING, {n}, {ordered}, ordered); + } + return ordered; +} + +Node TheoryProofStepBuffer::elimDoubleNegLit(Node n) +{ + // eliminate double neg + if (n.getKind() == kind::NOT && n[0].getKind() == kind::NOT) + { + addStep(PfRule::NOT_NOT_ELIM, {n}, {}, n[0][0]); + return n[0][0]; + } + return n; +} + +} // namespace theory +} // namespace cvc5 diff --git a/src/proof/theory_proof_step_buffer.h b/src/proof/theory_proof_step_buffer.h new file mode 100644 index 000000000..fc2e25e5a --- /dev/null +++ b/src/proof/theory_proof_step_buffer.h @@ -0,0 +1,120 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Haniel Barbosa + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Theory proof step buffer utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__THEORY_PROOF_STEP_BUFFER_H +#define CVC5__PROOF__THEORY_PROOF_STEP_BUFFER_H + +#include + +#include "expr/node.h" +#include "proof/proof_step_buffer.h" +#include "theory/builtin/proof_checker.h" + +namespace cvc5 { +namespace theory { +/** + * Class used to speculatively try and buffer a set of proof steps before + * sending them to a proof object, extended with theory-specfic proof rule + * utilities. + */ +class TheoryProofStepBuffer : public ProofStepBuffer +{ + public: + TheoryProofStepBuffer(ProofChecker* pc = nullptr); + ~TheoryProofStepBuffer() {} + //---------------------------- utilities builtin proof rules + /** + * Apply equality introduction. If this method returns true, it adds proof + * step(s) to the buffer that conclude (= src tgt) from premises exp. In + * particular, it may attempt to apply the rule MACRO_SR_EQ_INTRO. This + * method should be applied when tgt is equivalent to src assuming exp. + */ + bool applyEqIntro(Node src, + Node tgt, + const std::vector& exp, + MethodId ids = MethodId::SB_DEFAULT, + MethodId ida = MethodId::SBA_SEQUENTIAL, + MethodId idr = MethodId::RW_REWRITE); + /** + * Apply predicate transform. If this method returns true, it adds (at most + * one) proof step to the buffer that conclude tgt from premises src, exp. In + * particular, it may attempt to apply MACRO_SR_PRED_TRANSFORM. This method + * should be applied when src and tgt are equivalent formulas assuming exp. + */ + bool applyPredTransform(Node src, + Node tgt, + const std::vector& exp, + MethodId ids = MethodId::SB_DEFAULT, + MethodId ida = MethodId::SBA_SEQUENTIAL, + MethodId idr = MethodId::RW_REWRITE); + /** + * Apply predicate introduction. If this method returns true, it adds proof + * step(s) to the buffer that conclude tgt from premises exp. In particular, + * it may attempt to apply the rule MACRO_SR_PRED_INTRO. This method should be + * applied when tgt is equivalent to true assuming exp. + */ + bool applyPredIntro(Node tgt, + const std::vector& exp, + MethodId ids = MethodId::SB_DEFAULT, + MethodId ida = MethodId::SBA_SEQUENTIAL, + MethodId idr = MethodId::RW_REWRITE); + /** + * Apply predicate elimination. This method returns the result of applying + * the rule MACRO_SR_PRED_ELIM on src, exp. The returned formula is equivalent + * to src assuming exp. If the return value is equivalent to src, then no + * proof step is added to this buffer, since this step is a no-op in this + * case. + * + * Notice that in contrast to the other rules above, predicate elimination + * never fails and proves a formula that is not explicitly given as an + * argument tgt. Thus, the return value of this method is Node not bool. + */ + Node applyPredElim(Node src, + const std::vector& exp, + MethodId ids = MethodId::SB_DEFAULT, + MethodId ida = MethodId::SBA_SEQUENTIAL, + MethodId idr = MethodId::RW_REWRITE); + //---------------------------- end utilities builtin proof rules + + //---------------------------- utility methods for normalizing clauses + /** + * Normalizes a non-unit clause (an OR node) according to factoring and + * reordering, i.e. removes duplicates and reorders literals (according to + * node ids). Moreover it eliminates double negations, which can be done also + * for unit clauses (a arbitrary Boolean node). All normalization steps are + * tracked via proof steps added to this proof step buffer. + * + * @param n the clause to be normalized + * @return the normalized clause node + */ + Node factorReorderElimDoubleNeg(Node n); + + /** + * Eliminates double negation of a literal if it has the form + * (not (not t)) + * If the elimination happens, a step is added to this proof step buffer. + * + * @param n the node to have the top-level double negation eliminated + * @return the normalized clause node + */ + Node elimDoubleNegLit(Node n); +}; + +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__PROOF__THEORY_PROOF_STEP_BUFFER_H */ diff --git a/src/proof/trust_node.cpp b/src/proof/trust_node.cpp new file mode 100644 index 000000000..d99e6de51 --- /dev/null +++ b/src/proof/trust_node.cpp @@ -0,0 +1,150 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * Implementation of the trust node utility. + */ + +#include "proof/trust_node.h" + +#include "proof/proof_ensure_closed.h" +#include "proof/proof_generator.h" + +namespace cvc5 { +namespace theory { + +const char* toString(TrustNodeKind tnk) +{ + switch (tnk) + { + case TrustNodeKind::CONFLICT: return "CONFLICT"; + case TrustNodeKind::LEMMA: return "LEMMA"; + case TrustNodeKind::PROP_EXP: return "PROP_EXP"; + case TrustNodeKind::REWRITE: return "REWRITE"; + default: return "?"; + } +} + +std::ostream& operator<<(std::ostream& out, TrustNodeKind tnk) +{ + out << toString(tnk); + return out; +} + +TrustNode TrustNode::mkTrustConflict(Node conf, ProofGenerator* g) +{ + Node ckey = getConflictProven(conf); + // if a generator is provided, should confirm that it can prove it + Assert(g == nullptr || g->hasProofFor(ckey)); + return TrustNode(TrustNodeKind::CONFLICT, ckey, g); +} + +TrustNode TrustNode::mkTrustLemma(Node lem, ProofGenerator* g) +{ + Node lkey = getLemmaProven(lem); + // if a generator is provided, should confirm that it can prove it + Assert(g == nullptr || g->hasProofFor(lkey)); + return TrustNode(TrustNodeKind::LEMMA, lkey, g); +} + +TrustNode TrustNode::mkTrustPropExp(TNode lit, Node exp, ProofGenerator* g) +{ + Node pekey = getPropExpProven(lit, exp); + Assert(g == nullptr || g->hasProofFor(pekey)); + return TrustNode(TrustNodeKind::PROP_EXP, pekey, g); +} + +TrustNode TrustNode::mkTrustRewrite(TNode n, Node nr, ProofGenerator* g) +{ + Node rkey = getRewriteProven(n, nr); + Assert(g == nullptr || g->hasProofFor(rkey)); + return TrustNode(TrustNodeKind::REWRITE, rkey, g); +} + +TrustNode TrustNode::null() +{ + return TrustNode(TrustNodeKind::INVALID, Node::null()); +} + +TrustNode::TrustNode(TrustNodeKind tnk, Node p, ProofGenerator* g) + : d_tnk(tnk), d_proven(p), d_gen(g) +{ + // does not make sense to provide null node with generator + Assert(!d_proven.isNull() || d_gen == nullptr); +} + +TrustNodeKind TrustNode::getKind() const { return d_tnk; } + +Node TrustNode::getNode() const +{ + switch (d_tnk) + { + // the node of lemma is the node itself + case TrustNodeKind::LEMMA: return d_proven; + // the node of the rewrite is the right hand side of EQUAL + case TrustNodeKind::REWRITE: return d_proven[1]; + // the node of an explained propagation is the antecendant of an IMPLIES + // the node of a conflict is underneath a NOT + default: return d_proven[0]; + } +} + +Node TrustNode::getProven() const { return d_proven; } + +ProofGenerator* TrustNode::getGenerator() const { return d_gen; } + +bool TrustNode::isNull() const { return d_proven.isNull(); } + +std::shared_ptr TrustNode::toProofNode() const +{ + if (d_gen == nullptr) + { + return nullptr; + } + return d_gen->getProofFor(d_proven); +} + +Node TrustNode::getConflictProven(Node conf) { return conf.notNode(); } + +Node TrustNode::getLemmaProven(Node lem) { return lem; } + +Node TrustNode::getPropExpProven(TNode lit, Node exp) +{ + return NodeManager::currentNM()->mkNode(kind::IMPLIES, exp, lit); +} + +Node TrustNode::getRewriteProven(TNode n, Node nr) { return n.eqNode(nr); } + +void TrustNode::debugCheckClosed(const char* c, + const char* ctx, + bool reqNullGen) +{ + pfgEnsureClosed(d_proven, d_gen, c, ctx, reqNullGen); +} + +std::string TrustNode::identifyGenerator() const +{ + if (d_gen == nullptr) + { + return "null"; + } + return d_gen->identify(); +} + +std::ostream& operator<<(std::ostream& out, TrustNode n) +{ + out << "(" << n.getKind() << " " << n.getProven() << " " + << n.identifyGenerator() << ")"; + return out; +} + +} // namespace theory +} // namespace cvc5 diff --git a/src/proof/trust_node.h b/src/proof/trust_node.h new file mode 100644 index 000000000..200dececd --- /dev/null +++ b/src/proof/trust_node.h @@ -0,0 +1,178 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Gereon Kremer + * + * This file is part of the cvc5 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. + * **************************************************************************** + * + * The trust node utility. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__TRUST_NODE_H +#define CVC5__PROOF__TRUST_NODE_H + +#include "expr/node.h" + +namespace cvc5 { + +class ProofGenerator; +class ProofNode; + +namespace theory { + +/** A kind for trust nodes */ +enum class TrustNodeKind : uint32_t +{ + CONFLICT, + LEMMA, + PROP_EXP, + REWRITE, + INVALID +}; +/** + * Converts a proof rule to a string. Note: This function is also used in + * `safe_print()`. Changing this function name or signature will result in + * `safe_print()` printing "" instead of the proper strings for + * the enum values. + * + * Returns a string with static lifetime: it should not be freed. + * + * @param tnk The trust node kind + * @return The name of the trust node kind + */ +const char* toString(TrustNodeKind tnk); + +/** + * Writes a trust node kind name to a stream. + * + * @param out The stream to write to + * @param tnk The trust node kind to write to the stream + * @return The stream + */ +std::ostream& operator<<(std::ostream& out, TrustNodeKind tnk); + +/** + * A trust node is a pair (F, G) where F is a formula and G is a proof + * generator that can construct a proof for F if asked. + * + * More generally, a trust node is any node that can be used for a specific + * purpose that requires justification, such as being passed to + * OutputChannel::lemma. That justification is intended to be given by the + * generator that is required to construct this object. + * + * They are intended to be constructed by ProofGenerator objects themselves (a + * proof generator wraps itself in TrustNode it returns) and passed + * to ProofOutputChannel by theory solvers. + * + * The static functions for constructing them check that the generator, if + * provided, is capable of proving the given conflict or lemma, or an assertion + * failure occurs. Otherwise an assertion error is given. + * + * While this is not enforced, a `TrustNode` generally encapsulates a **closed** + * proof of the formula: one without free assumptions. + */ +class TrustNode +{ + public: + TrustNode() : d_tnk(TrustNodeKind::INVALID), d_gen(nullptr) {} + /** Make a proven node for conflict */ + static TrustNode mkTrustConflict(Node conf, ProofGenerator* g = nullptr); + /** Make a proven node for lemma */ + static TrustNode mkTrustLemma(Node lem, ProofGenerator* g = nullptr); + /** Make a proven node for explanation of propagated literal */ + static TrustNode mkTrustPropExp(TNode lit, + Node exp, + ProofGenerator* g = nullptr); + /** Make a proven node for rewrite */ + static TrustNode mkTrustRewrite(TNode n, + Node nr, + ProofGenerator* g = nullptr); + /** The null proven node */ + static TrustNode null(); + ~TrustNode() {} + /** get kind */ + TrustNodeKind getKind() const; + /** get node + * + * This is the node that is used in a common interface, either: + * (1) A T-unsat conjunction conf to pass to OutputChannel::conflict, + * (2) A valid T-formula lem to pass to OutputChannel::lemma, + * (3) A conjunction of literals exp to return in Theory::explain(lit), or + * (4) A result of rewriting a term n into an equivalent one nr. + * + * Notice that this node does not necessarily correspond to a valid formula. + * The call getProven() below retrieves a valid formula corresponding to + * the above calls. + */ + Node getNode() const; + /** get proven + * + * This is the corresponding formula that is proven by the proof generator + * for the above cases: + * (1) (not conf), for conflicts, + * (2) lem, for lemmas, + * (3) (=> exp lit), for propagations from explanations, + * (4) (= n nr), for results of rewriting. + * + * When constructing this trust node, the proof generator should be able to + * provide a proof for this fact. + */ + Node getProven() const; + /** get generator */ + ProofGenerator* getGenerator() const; + /** is null? */ + bool isNull() const; + /** + * Gets the proof node for this trust node, which is obtained by + * calling the generator's getProofFor method on the proven node. + */ + std::shared_ptr toProofNode() const; + + /** Get the proven formula corresponding to a conflict call */ + static Node getConflictProven(Node conf); + /** Get the proven formula corresponding to a lemma call */ + static Node getLemmaProven(Node lem); + /** Get the proven formula corresponding to explanations for propagation */ + static Node getPropExpProven(TNode lit, Node exp); + /** Get the proven formula corresponding to a rewrite */ + static Node getRewriteProven(TNode n, Node nr); + /** For debugging */ + std::string identifyGenerator() const; + + /** + * debug check closed on Trace c, context ctx is string for debugging + * + * @param reqNullGen Whether we consider a null generator to be a failure. + */ + void debugCheckClosed(const char* c, const char* ctx, bool reqNullGen = true); + + private: + TrustNode(TrustNodeKind tnk, Node p, ProofGenerator* g = nullptr); + /** The kind */ + TrustNodeKind d_tnk; + /** The proven node */ + Node d_proven; + /** The generator */ + ProofGenerator* d_gen; +}; + +/** + * Writes a trust node to a stream. + * + * @param out The stream to write to + * @param n The trust node to write to the stream + * @return The stream + */ +std::ostream& operator<<(std::ostream& out, TrustNode n); + +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__PROOF__TRUST_NODE_H */ diff --git a/src/prop/minisat/core/Solver.h b/src/prop/minisat/core/Solver.h index bed637904..d0f5006e1 100644 --- a/src/prop/minisat/core/Solver.h +++ b/src/prop/minisat/core/Solver.h @@ -27,8 +27,8 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "base/output.h" #include "context/context.h" #include "cvc5_private.h" -#include "expr/proof_node_manager.h" #include "proof/clause_id.h" +#include "proof/proof_node_manager.h" #include "prop/minisat/core/SolverTypes.h" #include "prop/minisat/mtl/Alg.h" #include "prop/minisat/mtl/Heap.h" diff --git a/src/prop/proof_cnf_stream.h b/src/prop/proof_cnf_stream.h index 708441b0c..97abdb077 100644 --- a/src/prop/proof_cnf_stream.h +++ b/src/prop/proof_cnf_stream.h @@ -19,14 +19,14 @@ #define CVC5__PROP__PROOF_CNF_STREAM_H #include "context/cdhashmap.h" -#include "expr/lazy_proof.h" #include "expr/node.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" +#include "proof/eager_proof_generator.h" +#include "proof/lazy_proof.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" +#include "proof/theory_proof_step_buffer.h" #include "prop/cnf_stream.h" #include "prop/sat_proof_manager.h" -#include "theory/eager_proof_generator.h" -#include "theory/theory_proof_step_buffer.h" namespace cvc5 { namespace prop { diff --git a/src/prop/proof_post_processor.h b/src/prop/proof_post_processor.h index b4a8d1b17..0065ed99e 100644 --- a/src/prop/proof_post_processor.h +++ b/src/prop/proof_post_processor.h @@ -21,7 +21,7 @@ #include #include -#include "expr/proof_node_updater.h" +#include "proof/proof_node_updater.h" #include "prop/proof_cnf_stream.h" namespace cvc5 { diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index a12816906..8903d4bc3 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -22,9 +22,9 @@ #include "context/cdlist.h" #include "expr/node.h" +#include "proof/trust_node.h" #include "prop/skolem_def_manager.h" #include "theory/output_channel.h" -#include "theory/trust_node.h" #include "util/result.h" namespace cvc5 { diff --git a/src/prop/prop_proof_manager.cpp b/src/prop/prop_proof_manager.cpp index 000cebb72..09c5fb915 100644 --- a/src/prop/prop_proof_manager.cpp +++ b/src/prop/prop_proof_manager.cpp @@ -15,8 +15,8 @@ #include "prop/prop_proof_manager.h" -#include "expr/proof_ensure_closed.h" -#include "expr/proof_node_algorithm.h" +#include "proof/proof_ensure_closed.h" +#include "proof/proof_node_algorithm.h" #include "prop/prop_proof_manager.h" #include "prop/sat_solver.h" diff --git a/src/prop/prop_proof_manager.h b/src/prop/prop_proof_manager.h index 1374acf8d..e415ed441 100644 --- a/src/prop/prop_proof_manager.h +++ b/src/prop/prop_proof_manager.h @@ -19,8 +19,8 @@ #define CVC5__PROP_PROOF_MANAGER_H #include "context/cdlist.h" -#include "expr/proof.h" -#include "expr/proof_node_manager.h" +#include "proof/proof.h" +#include "proof/proof_node_manager.h" #include "prop/proof_post_processor.h" #include "prop/sat_proof_manager.h" diff --git a/src/prop/sat_proof_manager.cpp b/src/prop/sat_proof_manager.cpp index 60a4c9704..5bec41e2b 100644 --- a/src/prop/sat_proof_manager.cpp +++ b/src/prop/sat_proof_manager.cpp @@ -15,11 +15,11 @@ #include "prop/sat_proof_manager.h" -#include "expr/proof_node_algorithm.h" #include "options/proof_options.h" +#include "proof/proof_node_algorithm.h" +#include "proof/theory_proof_step_buffer.h" #include "prop/cnf_stream.h" #include "prop/minisat/minisat.h" -#include "theory/theory_proof_step_buffer.h" namespace cvc5 { namespace prop { diff --git a/src/prop/sat_proof_manager.h b/src/prop/sat_proof_manager.h index a53f66cec..a224f3a28 100644 --- a/src/prop/sat_proof_manager.h +++ b/src/prop/sat_proof_manager.h @@ -19,9 +19,9 @@ #define CVC5__SAT_PROOF_MANAGER_H #include "context/cdhashset.h" -#include "expr/buffered_proof_generator.h" -#include "expr/lazy_proof_chain.h" #include "expr/node.h" +#include "proof/buffered_proof_generator.h" +#include "proof/lazy_proof_chain.h" #include "prop/minisat/core/SolverTypes.h" #include "prop/sat_solver_types.h" diff --git a/src/prop/sat_solver.h b/src/prop/sat_solver.h index 963810594..9ad54e292 100644 --- a/src/prop/sat_solver.h +++ b/src/prop/sat_solver.h @@ -23,8 +23,8 @@ #include "context/cdlist.h" #include "context/context.h" #include "expr/node.h" -#include "expr/proof_node_manager.h" #include "proof/clause_id.h" +#include "proof/proof_node_manager.h" #include "prop/bv_sat_solver_notify.h" #include "prop/sat_solver_types.h" #include "util/statistics_stats.h" diff --git a/src/prop/theory_proxy.h b/src/prop/theory_proxy.h index afd99ae83..d6ae1f975 100644 --- a/src/prop/theory_proxy.h +++ b/src/prop/theory_proxy.h @@ -26,11 +26,11 @@ #include "context/cdqueue.h" #include "expr/node.h" +#include "proof/trust_node.h" #include "prop/registrar.h" #include "prop/sat_solver_types.h" #include "theory/theory.h" #include "theory/theory_preprocessor.h" -#include "theory/trust_node.h" #include "util/resource_manager.h" namespace cvc5 { diff --git a/src/smt/env.cpp b/src/smt/env.cpp index f9e2a8458..647981ab3 100644 --- a/src/smt/env.cpp +++ b/src/smt/env.cpp @@ -18,9 +18,9 @@ #include "context/context.h" #include "expr/node.h" -#include "expr/term_conversion_proof_generator.h" #include "options/base_options.h" #include "printer/printer.h" +#include "proof/conv_proof_generator.h" #include "smt/dump_manager.h" #include "smt/smt_engine_stats.h" #include "theory/rewriter.h" diff --git a/src/smt/expand_definitions.cpp b/src/smt/expand_definitions.cpp index 91287e5f9..293c46906 100644 --- a/src/smt/expand_definitions.cpp +++ b/src/smt/expand_definitions.cpp @@ -19,8 +19,8 @@ #include #include "expr/node_manager_attributes.h" -#include "expr/term_conversion_proof_generator.h" #include "preprocessing/assertion_pipeline.h" +#include "proof/conv_proof_generator.h" #include "smt/env.h" #include "smt/smt_engine.h" #include "smt/smt_engine_stats.h" diff --git a/src/smt/expand_definitions.h b/src/smt/expand_definitions.h index 801622e27..ba89a1063 100644 --- a/src/smt/expand_definitions.h +++ b/src/smt/expand_definitions.h @@ -21,7 +21,7 @@ #include #include "expr/node.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/smt/preprocess_proof_generator.cpp b/src/smt/preprocess_proof_generator.cpp index 3ae03e58f..3f657db63 100644 --- a/src/smt/preprocess_proof_generator.cpp +++ b/src/smt/preprocess_proof_generator.cpp @@ -18,10 +18,10 @@ #include -#include "expr/proof.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" #include "options/proof_options.h" +#include "proof/proof.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" #include "theory/rewriter.h" namespace cvc5 { diff --git a/src/smt/preprocess_proof_generator.h b/src/smt/preprocess_proof_generator.h index 140a7c91a..347f4af3b 100644 --- a/src/smt/preprocess_proof_generator.h +++ b/src/smt/preprocess_proof_generator.h @@ -19,11 +19,11 @@ #define CVC5__SMT__PREPROCESS_PROOF_GENERATOR_H #include "context/cdhashmap.h" -#include "expr/lazy_proof.h" -#include "expr/proof.h" -#include "expr/proof_set.h" -#include "expr/proof_generator.h" -#include "theory/trust_node.h" +#include "proof/lazy_proof.h" +#include "proof/proof.h" +#include "proof/proof_generator.h" +#include "proof/proof_set.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/smt/proof_manager.cpp b/src/smt/proof_manager.cpp index 0968aa1f9..ad1e40c77 100644 --- a/src/smt/proof_manager.cpp +++ b/src/smt/proof_manager.cpp @@ -15,13 +15,13 @@ #include "smt/proof_manager.h" -#include "expr/proof_checker.h" -#include "expr/proof_node_algorithm.h" -#include "expr/proof_node_manager.h" #include "options/base_options.h" #include "options/proof_options.h" #include "options/smt_options.h" #include "proof/dot/dot_printer.h" +#include "proof/proof_checker.h" +#include "proof/proof_node_algorithm.h" +#include "proof/proof_node_manager.h" #include "smt/assertions.h" #include "smt/preprocess_proof_generator.h" #include "smt/proof_post_processor.h" diff --git a/src/smt/proof_post_processor.cpp b/src/smt/proof_post_processor.cpp index f98d1d727..62e18c3b2 100644 --- a/src/smt/proof_post_processor.cpp +++ b/src/smt/proof_post_processor.cpp @@ -15,11 +15,11 @@ #include "smt/proof_post_processor.h" -#include "expr/proof_node_manager.h" #include "expr/skolem_manager.h" #include "options/proof_options.h" #include "options/smt_options.h" #include "preprocessing/assertion_pipeline.h" +#include "proof/proof_node_manager.h" #include "smt/smt_engine.h" #include "smt/smt_statistics_registry.h" #include "theory/builtin/proof_checker.h" diff --git a/src/smt/proof_post_processor.h b/src/smt/proof_post_processor.h index 72fa3581d..f9e93fa91 100644 --- a/src/smt/proof_post_processor.h +++ b/src/smt/proof_post_processor.h @@ -22,7 +22,7 @@ #include #include -#include "expr/proof_node_updater.h" +#include "proof/proof_node_updater.h" #include "smt/witness_form.h" #include "util/statistics_stats.h" diff --git a/src/smt/term_formula_removal.cpp b/src/smt/term_formula_removal.cpp index eaf5cf82f..3bb998688 100644 --- a/src/smt/term_formula_removal.cpp +++ b/src/smt/term_formula_removal.cpp @@ -17,12 +17,12 @@ #include #include "expr/attribute.h" -#include "expr/lazy_proof.h" #include "expr/node_algorithm.h" #include "expr/skolem_manager.h" #include "expr/term_context_stack.h" -#include "expr/term_conversion_proof_generator.h" #include "options/smt_options.h" +#include "proof/conv_proof_generator.h" +#include "proof/lazy_proof.h" using namespace std; diff --git a/src/smt/term_formula_removal.h b/src/smt/term_formula_removal.h index 6a3a1c019..c2b1f27f3 100644 --- a/src/smt/term_formula_removal.h +++ b/src/smt/term_formula_removal.h @@ -24,7 +24,7 @@ #include "context/context.h" #include "expr/node.h" #include "expr/term_context.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" #include "util/hash.h" namespace cvc5 { diff --git a/src/smt/unsat_core_manager.cpp b/src/smt/unsat_core_manager.cpp index df8f2f9d9..ba2c07326 100644 --- a/src/smt/unsat_core_manager.cpp +++ b/src/smt/unsat_core_manager.cpp @@ -15,7 +15,7 @@ #include "unsat_core_manager.h" -#include "expr/proof_node_algorithm.h" +#include "proof/proof_node_algorithm.h" #include "smt/assertions.h" namespace cvc5 { diff --git a/src/smt/unsat_core_manager.h b/src/smt/unsat_core_manager.h index dcd246f2e..e064b83a7 100644 --- a/src/smt/unsat_core_manager.h +++ b/src/smt/unsat_core_manager.h @@ -20,7 +20,7 @@ #include "context/cdlist.h" #include "expr/node.h" -#include "expr/proof_node.h" +#include "proof/proof_node.h" namespace cvc5 { diff --git a/src/smt/witness_form.h b/src/smt/witness_form.h index 980cbcadb..8522d41f0 100644 --- a/src/smt/witness_form.h +++ b/src/smt/witness_form.h @@ -20,9 +20,9 @@ #include -#include "expr/proof.h" -#include "expr/proof_generator.h" -#include "expr/term_conversion_proof_generator.h" +#include "proof/conv_proof_generator.h" +#include "proof/proof.h" +#include "proof/proof_generator.h" namespace cvc5 { namespace smt { diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp index f343ce56a..33860d2d9 100644 --- a/src/theory/arith/approx_simplex.cpp +++ b/src/theory/arith/approx_simplex.cpp @@ -25,12 +25,12 @@ #include "base/cvc5config.h" #include "base/output.h" +#include "proof/eager_proof_generator.h" #include "smt/smt_statistics_registry.h" #include "theory/arith/constraint.h" #include "theory/arith/cut_log.h" #include "theory/arith/matrix.h" #include "theory/arith/normal_form.h" -#include "theory/eager_proof_generator.h" #ifdef CVC5_USE_GLPK #include "theory/arith/partial_model.h" diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp index 2edc7b210..0c0a4959d 100644 --- a/src/theory/arith/callbacks.cpp +++ b/src/theory/arith/callbacks.cpp @@ -18,8 +18,8 @@ #include "theory/arith/callbacks.h" -#include "expr/proof_node.h" #include "expr/skolem_manager.h" +#include "proof/proof_node.h" #include "theory/arith/proof_macros.h" #include "theory/arith/theory_arith_private.h" diff --git a/src/theory/arith/congruence_manager.cpp b/src/theory/arith/congruence_manager.cpp index 3d5e7270b..96272f939 100644 --- a/src/theory/arith/congruence_manager.cpp +++ b/src/theory/arith/congruence_manager.cpp @@ -19,8 +19,9 @@ #include "theory/arith/congruence_manager.h" #include "base/output.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" +#include "options/arith_options.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" #include "smt/smt_statistics_registry.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/constraint.h" @@ -29,7 +30,6 @@ #include "theory/rewriter.h" #include "theory/uf/equality_engine.h" #include "theory/uf/proof_equality_engine.h" -#include "options/arith_options.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h index e45b7bf29..8ada48cfe 100644 --- a/src/theory/arith/congruence_manager.h +++ b/src/theory/arith/congruence_manager.h @@ -24,11 +24,11 @@ #include "context/cdlist.h" #include "context/cdmaybe.h" #include "context/cdtrail_queue.h" +#include "proof/trust_node.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/arithvar.h" #include "theory/arith/callbacks.h" #include "theory/arith/constraint_forward.h" -#include "theory/trust_node.h" #include "theory/uf/equality_engine_notify.h" #include "util/dense_map.h" #include "util/statistics_stats.h" diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp index 15dfe4791..d8fc1c578 100644 --- a/src/theory/arith/constraint.cpp +++ b/src/theory/arith/constraint.cpp @@ -22,16 +22,15 @@ #include #include "base/output.h" -#include "expr/proof_node_manager.h" +#include "proof/eager_proof_generator.h" +#include "proof/proof_node_manager.h" #include "smt/smt_statistics_registry.h" -#include "theory/eager_proof_generator.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/congruence_manager.h" #include "theory/arith/normal_form.h" #include "theory/arith/partial_model.h" #include "theory/rewriter.h" - using namespace std; using namespace cvc5::kind; diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h index 0d3c96f89..fce071e6e 100644 --- a/src/theory/arith/constraint.h +++ b/src/theory/arith/constraint.h @@ -86,12 +86,12 @@ #include "context/cdlist.h" #include "context/cdqueue.h" #include "expr/node.h" +#include "proof/trust_node.h" #include "theory/arith/arithvar.h" #include "theory/arith/callbacks.h" #include "theory/arith/constraint_forward.h" #include "theory/arith/delta_rational.h" #include "theory/arith/proof_macros.h" -#include "theory/trust_node.h" #include "util/statistics_stats.h" namespace cvc5 { diff --git a/src/theory/arith/nl/cad/proof_checker.h b/src/theory/arith/nl/cad/proof_checker.h index d7ce05b60..8cc544fc4 100644 --- a/src/theory/arith/nl/cad/proof_checker.h +++ b/src/theory/arith/nl/cad/proof_checker.h @@ -19,7 +19,7 @@ #define CVC5__THEORY__ARITH__NL__CAD__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" +#include "proof/proof_checker.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/nl/cad/proof_generator.cpp b/src/theory/arith/nl/cad/proof_generator.cpp index b0d785b2a..374a7e4ef 100644 --- a/src/theory/arith/nl/cad/proof_generator.cpp +++ b/src/theory/arith/nl/cad/proof_generator.cpp @@ -17,7 +17,7 @@ #ifdef CVC5_POLY_IMP -#include "theory/lazy_tree_proof_generator.h" +#include "proof/lazy_tree_proof_generator.h" #include "theory/arith/nl/poly_conversion.h" namespace cvc5 { diff --git a/src/theory/arith/nl/cad/proof_generator.h b/src/theory/arith/nl/cad/proof_generator.h index b493455a6..613db7565 100644 --- a/src/theory/arith/nl/cad/proof_generator.h +++ b/src/theory/arith/nl/cad/proof_generator.h @@ -25,9 +25,9 @@ #include #include "expr/node.h" -#include "expr/proof_set.h" +#include "proof/lazy_tree_proof_generator.h" +#include "proof/proof_set.h" #include "theory/arith/nl/cad/cdcac_utils.h" -#include "theory/lazy_tree_proof_generator.h" namespace cvc5 { diff --git a/src/theory/arith/nl/ext/ext_state.cpp b/src/theory/arith/nl/ext/ext_state.cpp index 9ebd42d55..c1937797e 100644 --- a/src/theory/arith/nl/ext/ext_state.cpp +++ b/src/theory/arith/nl/ext/ext_state.cpp @@ -18,11 +18,11 @@ #include #include "expr/node.h" -#include "expr/proof.h" +#include "proof/proof.h" #include "theory/arith/inference_manager.h" #include "theory/arith/nl/ext/monomial.h" -#include "theory/arith/nl/nl_model.h" #include "theory/arith/nl/nl_lemma_utils.h" +#include "theory/arith/nl/nl_model.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/nl/ext/ext_state.h b/src/theory/arith/nl/ext/ext_state.h index b0a4fd859..7c2cc1b9b 100644 --- a/src/theory/arith/nl/ext/ext_state.h +++ b/src/theory/arith/nl/ext/ext_state.h @@ -19,7 +19,7 @@ #include #include "expr/node.h" -#include "expr/proof_set.h" +#include "proof/proof_set.h" #include "theory/arith/nl/ext/monomial.h" namespace cvc5 { diff --git a/src/theory/arith/nl/ext/factoring_check.cpp b/src/theory/arith/nl/ext/factoring_check.cpp index c4bba6ec8..d6b732eb9 100644 --- a/src/theory/arith/nl/ext/factoring_check.cpp +++ b/src/theory/arith/nl/ext/factoring_check.cpp @@ -16,12 +16,12 @@ #include "theory/arith/nl/ext/factoring_check.h" #include "expr/node.h" -#include "expr/proof.h" #include "expr/skolem_manager.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/inference_manager.h" -#include "theory/arith/nl/nl_model.h" #include "theory/arith/nl/ext/ext_state.h" +#include "theory/arith/nl/nl_model.h" #include "theory/rewriter.h" namespace cvc5 { diff --git a/src/theory/arith/nl/ext/monomial_bounds_check.cpp b/src/theory/arith/nl/ext/monomial_bounds_check.cpp index 44f59c521..0deb2c99d 100644 --- a/src/theory/arith/nl/ext/monomial_bounds_check.cpp +++ b/src/theory/arith/nl/ext/monomial_bounds_check.cpp @@ -16,8 +16,8 @@ #include "theory/arith/nl/ext/monomial_bounds_check.h" #include "expr/node.h" -#include "expr/proof.h" #include "options/arith_options.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/inference_manager.h" diff --git a/src/theory/arith/nl/ext/monomial_check.cpp b/src/theory/arith/nl/ext/monomial_check.cpp index 404916453..bf38bc8f0 100644 --- a/src/theory/arith/nl/ext/monomial_check.cpp +++ b/src/theory/arith/nl/ext/monomial_check.cpp @@ -16,12 +16,12 @@ #include "theory/arith/nl/ext/monomial_check.h" #include "expr/node.h" -#include "expr/proof.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/inference_manager.h" -#include "theory/arith/nl/nl_model.h" -#include "theory/arith/nl/nl_lemma_utils.h" #include "theory/arith/nl/ext/ext_state.h" +#include "theory/arith/nl/nl_lemma_utils.h" +#include "theory/arith/nl/nl_model.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/nl/ext/proof_checker.h b/src/theory/arith/nl/ext/proof_checker.h index 400126510..bf28cea59 100644 --- a/src/theory/arith/nl/ext/proof_checker.h +++ b/src/theory/arith/nl/ext/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__ARITH__NL__EXT__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/nl/ext/split_zero_check.cpp b/src/theory/arith/nl/ext/split_zero_check.cpp index ace7e0868..6d9faa356 100644 --- a/src/theory/arith/nl/ext/split_zero_check.cpp +++ b/src/theory/arith/nl/ext/split_zero_check.cpp @@ -16,11 +16,11 @@ #include "theory/arith/nl/ext/split_zero_check.h" #include "expr/node.h" -#include "expr/proof.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/inference_manager.h" -#include "theory/arith/nl/nl_model.h" #include "theory/arith/nl/ext/ext_state.h" +#include "theory/arith/nl/nl_model.h" #include "theory/rewriter.h" namespace cvc5 { diff --git a/src/theory/arith/nl/ext/tangent_plane_check.cpp b/src/theory/arith/nl/ext/tangent_plane_check.cpp index a66f1396c..9efa9c0f2 100644 --- a/src/theory/arith/nl/ext/tangent_plane_check.cpp +++ b/src/theory/arith/nl/ext/tangent_plane_check.cpp @@ -16,11 +16,11 @@ #include "theory/arith/nl/ext/tangent_plane_check.h" #include "expr/node.h" -#include "expr/proof.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/inference_manager.h" -#include "theory/arith/nl/nl_model.h" #include "theory/arith/nl/ext/ext_state.h" +#include "theory/arith/nl/nl_model.h" #include "theory/rewriter.h" namespace cvc5 { diff --git a/src/theory/arith/nl/transcendental/exponential_solver.cpp b/src/theory/arith/nl/transcendental/exponential_solver.cpp index 11e2b9cc8..73279a782 100644 --- a/src/theory/arith/nl/transcendental/exponential_solver.cpp +++ b/src/theory/arith/nl/transcendental/exponential_solver.cpp @@ -20,8 +20,8 @@ #include "expr/node_algorithm.h" #include "expr/node_builder.h" -#include "expr/proof.h" #include "options/arith_options.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/inference_manager.h" diff --git a/src/theory/arith/nl/transcendental/proof_checker.h b/src/theory/arith/nl/transcendental/proof_checker.h index 8675afe39..06359e62a 100644 --- a/src/theory/arith/nl/transcendental/proof_checker.h +++ b/src/theory/arith/nl/transcendental/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__ARITH__NL__TRANSCENDENTAL__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/nl/transcendental/sine_solver.cpp b/src/theory/arith/nl/transcendental/sine_solver.cpp index 26d959878..6fdd4cc15 100644 --- a/src/theory/arith/nl/transcendental/sine_solver.cpp +++ b/src/theory/arith/nl/transcendental/sine_solver.cpp @@ -20,9 +20,9 @@ #include "expr/node_algorithm.h" #include "expr/node_builder.h" -#include "expr/proof.h" #include "expr/skolem_manager.h" #include "options/arith_options.h" +#include "proof/proof.h" #include "theory/arith/arith_msum.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/inference_manager.h" diff --git a/src/theory/arith/nl/transcendental/transcendental_state.cpp b/src/theory/arith/nl/transcendental/transcendental_state.cpp index c0f5d23b2..db20713f1 100644 --- a/src/theory/arith/nl/transcendental/transcendental_state.cpp +++ b/src/theory/arith/nl/transcendental/transcendental_state.cpp @@ -15,7 +15,7 @@ #include "theory/arith/nl/transcendental/transcendental_state.h" -#include "expr/proof.h" +#include "proof/proof.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/inference_manager.h" #include "theory/arith/nl/nl_model.h" diff --git a/src/theory/arith/nl/transcendental/transcendental_state.h b/src/theory/arith/nl/transcendental/transcendental_state.h index 74f005294..56cbec79a 100644 --- a/src/theory/arith/nl/transcendental/transcendental_state.h +++ b/src/theory/arith/nl/transcendental/transcendental_state.h @@ -17,7 +17,7 @@ #define CVC5__THEORY__ARITH__NL__TRANSCENDENTAL__TRANSCENDENTAL_STATE_H #include "expr/node.h" -#include "expr/proof_set.h" +#include "proof/proof_set.h" #include "theory/arith/nl/nl_lemma_utils.h" #include "theory/arith/nl/transcendental/proof_checker.h" #include "theory/arith/nl/transcendental/taylor_generator.h" diff --git a/src/theory/arith/operator_elim.cpp b/src/theory/arith/operator_elim.cpp index f39be2c59..7b4e920fb 100644 --- a/src/theory/arith/operator_elim.cpp +++ b/src/theory/arith/operator_elim.cpp @@ -19,8 +19,8 @@ #include "expr/attribute.h" #include "expr/bound_var_manager.h" -#include "expr/term_conversion_proof_generator.h" #include "options/arith_options.h" +#include "proof/conv_proof_generator.h" #include "smt/logic_exception.h" #include "theory/arith/arith_utilities.h" #include "theory/rewriter.h" diff --git a/src/theory/arith/operator_elim.h b/src/theory/arith/operator_elim.h index 829601827..27a3d293e 100644 --- a/src/theory/arith/operator_elim.h +++ b/src/theory/arith/operator_elim.h @@ -19,7 +19,7 @@ #include "expr/node.h" #include "expr/skolem_manager.h" -#include "theory/eager_proof_generator.h" +#include "proof/eager_proof_generator.h" #include "theory/logic_info.h" #include "theory/skolem_lemma.h" diff --git a/src/theory/arith/proof_checker.h b/src/theory/arith/proof_checker.h index 402656154..8fa9f21c1 100644 --- a/src/theory/arith/proof_checker.h +++ b/src/theory/arith/proof_checker.h @@ -19,7 +19,7 @@ #define CVC5__THEORY__ARITH__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" +#include "proof/proof_checker.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index 1843ddb8a..5c4678cb4 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -15,9 +15,9 @@ #include "theory/arith/theory_arith.h" -#include "expr/proof_checker.h" -#include "expr/proof_rule.h" #include "options/smt_options.h" +#include "proof/proof_checker.h" +#include "proof/proof_rule.h" #include "smt/smt_statistics_registry.h" #include "theory/arith/arith_rewriter.h" #include "theory/arith/infer_bounds.h" diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 911954a5a..d12553e90 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -33,13 +33,13 @@ #include "expr/node.h" #include "expr/node_algorithm.h" #include "expr/node_builder.h" -#include "expr/proof_generator.h" -#include "expr/proof_node_manager.h" -#include "expr/proof_rule.h" #include "expr/skolem_manager.h" #include "options/arith_options.h" #include "options/smt_options.h" // for incrementalSolving() #include "preprocessing/util/ite_utilities.h" +#include "proof/proof_generator.h" +#include "proof/proof_node_manager.h" +#include "proof/proof_rule.h" #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" #include "smt_util/boolean_simplification.h" diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h index e029e3c41..e3094ae88 100644 --- a/src/theory/arith/theory_arith_private.h +++ b/src/theory/arith/theory_arith_private.h @@ -28,6 +28,7 @@ #include "expr/kind.h" #include "expr/node.h" #include "expr/node_builder.h" +#include "proof/trust_node.h" #include "theory/arith/arith_static_learner.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/arithvar.h" @@ -47,7 +48,6 @@ #include "theory/arith/proof_checker.h" #include "theory/arith/soi_simplex.h" #include "theory/arith/theory_arith.h" -#include "theory/trust_node.h" #include "theory/valuation.h" #include "util/dense_map.h" #include "util/integer.h" diff --git a/src/theory/arrays/inference_manager.h b/src/theory/arrays/inference_manager.h index 93feb0a53..9b219c9b7 100644 --- a/src/theory/arrays/inference_manager.h +++ b/src/theory/arrays/inference_manager.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__ARRAYS__INFERENCE_MANAGER_H #include "expr/node.h" -#include "expr/proof_rule.h" -#include "theory/eager_proof_generator.h" +#include "proof/eager_proof_generator.h" +#include "proof/proof_rule.h" #include "theory/theory_inference_manager.h" namespace cvc5 { diff --git a/src/theory/arrays/proof_checker.h b/src/theory/arrays/proof_checker.h index 9064dbfca..0b7bef163 100644 --- a/src/theory/arrays/proof_checker.h +++ b/src/theory/arrays/proof_checker.h @@ -19,7 +19,7 @@ #define CVC5__THEORY__ARRAYS__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" +#include "proof/proof_checker.h" namespace cvc5 { namespace theory { diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 9b5676d65..0bdccfd17 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -20,9 +20,9 @@ #include "expr/kind.h" #include "expr/node_algorithm.h" -#include "expr/proof_checker.h" #include "options/arrays_options.h" #include "options/smt_options.h" +#include "proof/proof_checker.h" #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" #include "theory/arrays/skolem_cache.h" diff --git a/src/theory/bags/theory_bags.cpp b/src/theory/bags/theory_bags.cpp index d5917f91c..c2aa9eb1e 100644 --- a/src/theory/bags/theory_bags.cpp +++ b/src/theory/bags/theory_bags.cpp @@ -15,7 +15,7 @@ #include "theory/bags/theory_bags.h" -#include "expr/proof_checker.h" +#include "proof/proof_checker.h" #include "smt/logic_exception.h" #include "theory/bags/normal_form.h" #include "theory/rewriter.h" diff --git a/src/theory/booleans/circuit_propagator.cpp b/src/theory/booleans/circuit_propagator.cpp index 7920f5b7f..2fa2890c2 100644 --- a/src/theory/booleans/circuit_propagator.cpp +++ b/src/theory/booleans/circuit_propagator.cpp @@ -20,10 +20,10 @@ #include #include "expr/node_algorithm.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" +#include "proof/eager_proof_generator.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" #include "theory/booleans/proof_circuit_propagator.h" -#include "theory/eager_proof_generator.h" #include "theory/theory.h" #include "util/hash.h" #include "util/utility.h" diff --git a/src/theory/booleans/circuit_propagator.h b/src/theory/booleans/circuit_propagator.h index 1d4f6e9ee..74e1a7cd8 100644 --- a/src/theory/booleans/circuit_propagator.h +++ b/src/theory/booleans/circuit_propagator.h @@ -26,9 +26,9 @@ #include "context/cdhashset.h" #include "context/cdo.h" #include "context/context.h" -#include "expr/lazy_proof_chain.h" #include "expr/node.h" -#include "theory/trust_node.h" +#include "proof/lazy_proof_chain.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/theory/booleans/proof_checker.h b/src/theory/booleans/proof_checker.h index 47a1ce633..114ab2e9b 100644 --- a/src/theory/booleans/proof_checker.h +++ b/src/theory/booleans/proof_checker.h @@ -19,7 +19,7 @@ #define CVC5__THEORY__BOOLEANS__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" +#include "proof/proof_checker.h" namespace cvc5 { namespace theory { diff --git a/src/theory/booleans/proof_circuit_propagator.cpp b/src/theory/booleans/proof_circuit_propagator.cpp index d1f8bb854..f7e788e9a 100644 --- a/src/theory/booleans/proof_circuit_propagator.cpp +++ b/src/theory/booleans/proof_circuit_propagator.cpp @@ -17,8 +17,8 @@ #include -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" namespace cvc5 { namespace theory { diff --git a/src/theory/booleans/proof_circuit_propagator.h b/src/theory/booleans/proof_circuit_propagator.h index 624a17f2f..0bed57078 100644 --- a/src/theory/booleans/proof_circuit_propagator.h +++ b/src/theory/booleans/proof_circuit_propagator.h @@ -21,7 +21,7 @@ #include #include "expr/node.h" -#include "expr/proof_rule.h" +#include "proof/proof_rule.h" namespace cvc5 { diff --git a/src/theory/booleans/theory_bool.cpp b/src/theory/booleans/theory_bool.cpp index bac2be70c..3aac5ecfb 100644 --- a/src/theory/booleans/theory_bool.cpp +++ b/src/theory/booleans/theory_bool.cpp @@ -18,7 +18,7 @@ #include #include -#include "expr/proof_node_manager.h" +#include "proof/proof_node_manager.h" #include "smt_util/boolean_simplification.h" #include "theory/booleans/circuit_propagator.h" #include "theory/booleans/theory_bool_rewriter.h" diff --git a/src/theory/builtin/proof_checker.h b/src/theory/builtin/proof_checker.h index dd6bf82e7..892fc7a4b 100644 --- a/src/theory/builtin/proof_checker.h +++ b/src/theory/builtin/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__BUILTIN__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" #include "theory/quantifiers/extended_rewrite.h" namespace cvc5 { diff --git a/src/theory/builtin/theory_builtin.cpp b/src/theory/builtin/theory_builtin.cpp index c5bd77e36..80c5dba3d 100644 --- a/src/theory/builtin/theory_builtin.cpp +++ b/src/theory/builtin/theory_builtin.cpp @@ -16,7 +16,7 @@ #include "theory/builtin/theory_builtin.h" #include "expr/kind.h" -#include "expr/proof_node_manager.h" +#include "proof/proof_node_manager.h" #include "theory/builtin/theory_builtin_rewriter.h" #include "theory/theory_model.h" #include "theory/valuation.h" diff --git a/src/theory/bv/bitblast/proof_bitblaster.cpp b/src/theory/bv/bitblast/proof_bitblaster.cpp index 6082a7128..a55adaace 100644 --- a/src/theory/bv/bitblast/proof_bitblaster.cpp +++ b/src/theory/bv/bitblast/proof_bitblaster.cpp @@ -16,8 +16,8 @@ #include -#include "expr/term_conversion_proof_generator.h" #include "options/proof_options.h" +#include "proof/conv_proof_generator.h" #include "theory/theory_model.h" namespace cvc5 { diff --git a/src/theory/bv/bv_solver_bitblast.h b/src/theory/bv/bv_solver_bitblast.h index 3da08f868..31d9443c8 100644 --- a/src/theory/bv/bv_solver_bitblast.h +++ b/src/theory/bv/bv_solver_bitblast.h @@ -21,12 +21,12 @@ #include #include "context/cdqueue.h" +#include "proof/eager_proof_generator.h" #include "prop/cnf_stream.h" #include "prop/sat_solver.h" #include "theory/bv/bitblast/simple_bitblaster.h" #include "theory/bv/bv_solver.h" #include "theory/bv/proof_checker.h" -#include "theory/eager_proof_generator.h" namespace cvc5 { diff --git a/src/theory/bv/bv_solver_simple.cpp b/src/theory/bv/bv_solver_simple.cpp index 3faad29a9..a16bf7eb8 100644 --- a/src/theory/bv/bv_solver_simple.cpp +++ b/src/theory/bv/bv_solver_simple.cpp @@ -15,7 +15,7 @@ #include "theory/bv/bv_solver_simple.h" -#include "expr/term_conversion_proof_generator.h" +#include "proof/conv_proof_generator.h" #include "theory/bv/theory_bv.h" #include "theory/bv/theory_bv_utils.h" #include "theory/theory_model.h" diff --git a/src/theory/bv/proof_checker.h b/src/theory/bv/proof_checker.h index 674678ff8..c89dd359c 100644 --- a/src/theory/bv/proof_checker.h +++ b/src/theory/bv/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__BV__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" #include "theory/bv/bitblast/simple_bitblaster.h" namespace cvc5 { diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index c5ab08f0c..22b05b026 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -15,9 +15,9 @@ #include "theory/bv/theory_bv.h" -#include "expr/proof_checker.h" #include "options/bv_options.h" #include "options/smt_options.h" +#include "proof/proof_checker.h" #include "smt/smt_statistics_registry.h" #include "theory/bv/bv_solver_bitblast.h" #include "theory/bv/bv_solver_lazy.h" diff --git a/src/theory/combination_engine.cpp b/src/theory/combination_engine.cpp index 33300ead8..2b35fb97d 100644 --- a/src/theory/combination_engine.cpp +++ b/src/theory/combination_engine.cpp @@ -16,8 +16,8 @@ #include "theory/combination_engine.h" #include "expr/node_visitor.h" +#include "proof/eager_proof_generator.h" #include "theory/care_graph.h" -#include "theory/eager_proof_generator.h" #include "theory/ee_manager_distributed.h" #include "theory/model_manager.h" #include "theory/model_manager_distributed.h" diff --git a/src/theory/datatypes/infer_proof_cons.cpp b/src/theory/datatypes/infer_proof_cons.cpp index 5ab8a563f..19f396a56 100644 --- a/src/theory/datatypes/infer_proof_cons.cpp +++ b/src/theory/datatypes/infer_proof_cons.cpp @@ -15,8 +15,8 @@ #include "theory/datatypes/infer_proof_cons.h" -#include "expr/proof.h" -#include "expr/proof_checker.h" +#include "proof/proof.h" +#include "proof/proof_checker.h" #include "theory/datatypes/theory_datatypes_utils.h" #include "theory/model_manager.h" #include "theory/rewriter.h" diff --git a/src/theory/datatypes/infer_proof_cons.h b/src/theory/datatypes/infer_proof_cons.h index f8e7eea09..6c1663a09 100644 --- a/src/theory/datatypes/infer_proof_cons.h +++ b/src/theory/datatypes/infer_proof_cons.h @@ -20,7 +20,7 @@ #include "context/cdhashmap.h" #include "expr/node.h" -#include "expr/proof_generator.h" +#include "proof/proof_generator.h" #include "theory/datatypes/inference.h" namespace cvc5 { diff --git a/src/theory/datatypes/inference.h b/src/theory/datatypes/inference.h index db1d2b4b0..2dac0d7aa 100644 --- a/src/theory/datatypes/inference.h +++ b/src/theory/datatypes/inference.h @@ -19,9 +19,9 @@ #define CVC5__THEORY__DATATYPES__INFERENCE_H #include "expr/node.h" +#include "proof/trust_node.h" #include "theory/inference_id.h" #include "theory/theory_inference.h" -#include "theory/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/datatypes/inference_manager.cpp b/src/theory/datatypes/inference_manager.cpp index 393813590..014255a7c 100644 --- a/src/theory/datatypes/inference_manager.cpp +++ b/src/theory/datatypes/inference_manager.cpp @@ -17,8 +17,8 @@ #include "expr/dtype.h" #include "options/datatypes_options.h" +#include "proof/eager_proof_generator.h" #include "smt/smt_statistics_registry.h" -#include "theory/eager_proof_generator.h" #include "theory/rewriter.h" #include "theory/theory.h" #include "theory/theory_state.h" diff --git a/src/theory/datatypes/proof_checker.h b/src/theory/datatypes/proof_checker.h index 76fcee411..5949441d8 100644 --- a/src/theory/datatypes/proof_checker.h +++ b/src/theory/datatypes/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__DATATYPES__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index f9d08dfc2..dc57e4165 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -21,12 +21,12 @@ #include "expr/dtype.h" #include "expr/dtype_cons.h" #include "expr/kind.h" -#include "expr/proof_node_manager.h" #include "expr/skolem_manager.h" #include "options/datatypes_options.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" #include "options/theory_options.h" +#include "proof/proof_node_manager.h" #include "smt/logic_exception.h" #include "theory/datatypes/datatypes_rewriter.h" #include "theory/datatypes/theory_datatypes_type_rules.h" diff --git a/src/theory/eager_proof_generator.cpp b/src/theory/eager_proof_generator.cpp deleted file mode 100644 index c9a8823aa..000000000 --- a/src/theory/eager_proof_generator.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Alex Ozdemir - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of the abstract proof generator class. - */ - -#include "theory/eager_proof_generator.h" - -#include "expr/proof.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" - -namespace cvc5 { -namespace theory { - -EagerProofGenerator::EagerProofGenerator(ProofNodeManager* pnm, - context::Context* c, - std::string name) - : d_pnm(pnm), d_name(name), d_proofs(c == nullptr ? &d_context : c) -{ -} - -void EagerProofGenerator::setProofFor(Node f, std::shared_ptr pf) -{ - // pf should prove f - Assert(pf->getResult() == f) - << "EagerProofGenerator::setProofFor: unexpected result" << std::endl - << "Expected: " << f << std::endl - << "Actual: " << pf->getResult() << std::endl; - d_proofs[f] = pf; -} -void EagerProofGenerator::setProofForConflict(Node conf, - std::shared_ptr pf) -{ - // Normalize based on key - Node ckey = TrustNode::getConflictProven(conf); - setProofFor(ckey, pf); -} - -void EagerProofGenerator::setProofForLemma(Node lem, - std::shared_ptr pf) -{ - // Normalize based on key - Node lkey = TrustNode::getLemmaProven(lem); - setProofFor(lkey, pf); -} - -void EagerProofGenerator::setProofForPropExp(TNode lit, - Node exp, - std::shared_ptr pf) -{ - // Normalize based on key - Node pekey = TrustNode::getPropExpProven(lit, exp); - setProofFor(pekey, pf); -} - -std::shared_ptr EagerProofGenerator::getProofFor(Node f) -{ - NodeProofNodeMap::iterator it = d_proofs.find(f); - if (it == d_proofs.end()) - { - return nullptr; - } - return (*it).second; -} - -bool EagerProofGenerator::hasProofFor(Node f) -{ - return d_proofs.find(f) != d_proofs.end(); -} - -TrustNode EagerProofGenerator::mkTrustNode(Node n, - std::shared_ptr pf, - bool isConflict) -{ - if (pf == nullptr) - { - return TrustNode::null(); - } - if (isConflict) - { - // this shouldnt modify the key - setProofForConflict(n, pf); - // we can now return the trust node - return TrustNode::mkTrustConflict(n, this); - } - // this shouldnt modify the key - setProofForLemma(n, pf); - // we can now return the trust node - return TrustNode::mkTrustLemma(n, this); -} - -TrustNode EagerProofGenerator::mkTrustNode(Node conc, - PfRule id, - const std::vector& exp, - const std::vector& args, - bool isConflict) -{ - // if no children, its easy - if (exp.empty()) - { - std::shared_ptr pf = d_pnm->mkNode(id, {}, args, conc); - return mkTrustNode(conc, pf, isConflict); - } - // otherwise, we use CDProof + SCOPE - CDProof cdp(d_pnm); - cdp.addStep(conc, id, exp, args); - std::shared_ptr pf = cdp.getProofFor(conc); - // We use mkNode instead of mkScope, since there is no reason to check - // whether the free assumptions of pf are in exp, since they are by the - // construction above. - std::shared_ptr pfs = d_pnm->mkNode(PfRule::SCOPE, {pf}, exp); - return mkTrustNode(pfs->getResult(), pfs, isConflict); -} - -TrustNode EagerProofGenerator::mkTrustedRewrite( - Node a, Node b, std::shared_ptr pf) -{ - if (pf == nullptr) - { - return TrustNode::null(); - } - Node eq = a.eqNode(b); - setProofFor(eq, pf); - return TrustNode::mkTrustRewrite(a, b, this); -} - -TrustNode EagerProofGenerator::mkTrustedPropagation( - Node n, Node exp, std::shared_ptr pf) -{ - if (pf == nullptr) - { - return TrustNode::null(); - } - setProofForPropExp(n, exp, pf); - return TrustNode::mkTrustPropExp(n, exp, this); -} - -TrustNode EagerProofGenerator::mkTrustNodeSplit(Node f) -{ - // make the lemma - Node lem = f.orNode(f.notNode()); - return mkTrustNode(lem, PfRule::SPLIT, {}, {f}, false); -} - -std::string EagerProofGenerator::identify() const { return d_name; } - -} // namespace theory -} // namespace cvc5 diff --git a/src/theory/eager_proof_generator.h b/src/theory/eager_proof_generator.h deleted file mode 100644 index c0b368e6e..000000000 --- a/src/theory/eager_proof_generator.h +++ /dev/null @@ -1,198 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Alex Ozdemir, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * The eager proof generator class. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__THEORY__EAGER_PROOF_GENERATOR_H -#define CVC5__THEORY__EAGER_PROOF_GENERATOR_H - -#include "context/cdhashmap.h" -#include "expr/node.h" -#include "expr/proof_generator.h" -#include "expr/proof_rule.h" -#include "theory/trust_node.h" - -namespace cvc5 { - -class ProofNode; -class ProofNodeManager; - -namespace theory { - -/** - * An eager proof generator, with explicit proof caching. - * - * The intended use of this class is to store proofs for lemmas and conflicts - * at the time they are sent out on the ProofOutputChannel. This means that the - * getProofForConflict and getProofForLemma methods are lookups in a - * (user-context depedent) map, the field d_proofs below. - * - * In detail, the method setProofForConflict(conf, pf) should be called prior to - * calling ProofOutputChannel(TrustNode(conf,X)), where X is this generator. - * Similarly for setProofForLemma. - * - * The intended usage of this class in combination with OutputChannel is - * the following: - * //----------------------------------------------------------- - * class MyEagerProofGenerator : public EagerProofGenerator - * { - * public: - * TrustNode getProvenConflictByMethodX(...) - * { - * // construct a conflict - * Node conf = [construct conflict]; - * // construct a proof for conf - * std::shared_ptr pf = [construct the proof for conf]; - * // wrap the conflict in a trust node - * return mkTrustNode(conf,pf); - * } - * }; - * // [1] Make objects given user context u and output channel out. - * - * MyEagerProofGenerator epg(u); - * OutputChannel out; - * - * // [2] Assume epg realizes there is a conflict. We have it store the proof - * // internally and return the conflict node paired with epg. - * - * TrustNode pconf = epg.getProvenConflictByMethodX(...); - * - * // [3] Send the conflict on the output channel. - * - * out.trustedConflict(pconf); - * - * // [4] The trust node has information about what is proven and who can - * // prove it, where this association is valid in the remainder of the user - * // context. - * - * Node conf = pconf.getProven(); - * ProofGenerator * pg = pconf.getGenerator(); - * std::shared_ptr pf = pg->getProofForConflict(conf); - * //----------------------------------------------------------- - * In other words, the proof generator epg is responsible for creating and - * storing the proof internally, and the proof output channel is responsible for - * maintaining the map that epg is who to ask for the proof of the conflict. - */ -class EagerProofGenerator : public ProofGenerator -{ - typedef context::CDHashMap> NodeProofNodeMap; - - public: - EagerProofGenerator(ProofNodeManager* pnm, - context::Context* c = nullptr, - std::string name = "EagerProofGenerator"); - ~EagerProofGenerator() {} - /** Get the proof for formula f. */ - std::shared_ptr getProofFor(Node f) override; - /** Can we give the proof for formula f? */ - bool hasProofFor(Node f) override; - /** - * Set proof for fact f, called when pf is a proof of f. - * - * @param f The fact proven by pf, - * @param pf The proof to store in this class. - */ - void setProofFor(Node f, std::shared_ptr pf); - /** - * Make trust node: wrap n in a trust node with this generator, and have it - * store the proof pf to lemma or conflict n. - * - * @param n The proven node, - * @param pf The proof of n, - * @param isConflict Whether the returned trust node is a conflict (otherwise - * it is a lemma), - * @return The trust node corresponding to the fact that this generator has - * a proof of n. - */ - TrustNode mkTrustNode(Node n, - std::shared_ptr pf, - bool isConflict = false); - /** - * Make trust node from a single step proof. This is a convenience function - * that avoids the need to explictly construct ProofNode by the caller. - * - * @param conc The conclusion of the rule, - * @param id The rule of the proof concluding conc - * @param exp The explanation (premises) to the proof concluding conc, - * @param args The arguments to the proof concluding conc, - * @param isConflict Whether the returned trust node is a conflict (otherwise - * it is a lemma), - * @return The trust node corresponding to the fact that this generator has - * a proof of (exp => conc), or of conc if exp is empty. - */ - TrustNode mkTrustNode(Node conc, - PfRule id, - const std::vector& exp, - const std::vector& args, - bool isConflict = false); - /** - * Make trust node: wrap `exp => n` in a trust node with this generator, and - * have it store the proof `pf` too. - * - * @param n The implication - * @param exp A conjunction of literals that imply it - * @param pf The proof of exp => n, - * @return The trust node corresponding to the fact that this generator has - * a proof of exp => n. - */ - TrustNode mkTrustedPropagation(Node n, - Node exp, - std::shared_ptr pf); - /** - * Make trust node: `a = b` as a Rewrite trust node - * - * @param a the original - * @param b what is rewrites to - * @param pf The proof of a = b, - * @return The trust node corresponding to the fact that this generator has - * a proof of a = b - */ - TrustNode mkTrustedRewrite( - Node a, Node b, std::shared_ptr pf); - //--------------------------------------- common proofs - /** - * This returns the trust node corresponding to the splitting lemma - * (or f (not f)) and this generator. The method registers its proof in the - * map maintained by this class. - */ - TrustNode mkTrustNodeSplit(Node f); - //--------------------------------------- end common proofs - /** identify */ - std::string identify() const override; - - protected: - /** Set that pf is the proof for conflict conf */ - void setProofForConflict(Node conf, std::shared_ptr pf); - /** Set that pf is the proof for lemma lem */ - void setProofForLemma(Node lem, std::shared_ptr pf); - /** Set that pf is the proof for explained propagation */ - void setProofForPropExp(TNode lit, Node exp, std::shared_ptr pf); - /** The proof node manager */ - ProofNodeManager* d_pnm; - /** Name identifier */ - std::string d_name; - /** A dummy context used by this class if none is provided */ - context::Context d_context; - /** - * A user-context-dependent map from lemmas and conflicts to proofs provided - * by calls to setProofForConflict and setProofForLemma above. - */ - NodeProofNodeMap d_proofs; -}; - -} // namespace theory -} // namespace cvc5 - -#endif /* CVC5__THEORY__PROOF_GENERATOR_H */ diff --git a/src/theory/fp/fp_expand_defs.h b/src/theory/fp/fp_expand_defs.h index a9edc8b02..8240f1f8e 100644 --- a/src/theory/fp/fp_expand_defs.h +++ b/src/theory/fp/fp_expand_defs.h @@ -20,7 +20,7 @@ #include "context/cdhashmap.h" #include "expr/node.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/lazy_tree_proof_generator.cpp b/src/theory/lazy_tree_proof_generator.cpp deleted file mode 100644 index 3fd3795c1..000000000 --- a/src/theory/lazy_tree_proof_generator.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of the lazy tree proof generator class. - */ - -#include "theory/lazy_tree_proof_generator.h" - -#include - -#include "expr/node.h" -#include "expr/proof_generator.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" - -namespace cvc5 { -namespace theory { - -LazyTreeProofGenerator::LazyTreeProofGenerator(ProofNodeManager* pnm, - const std::string& name) - : d_pnm(pnm), d_name(name) -{ - d_stack.emplace_back(&d_proof); -} -void LazyTreeProofGenerator::openChild() -{ - detail::TreeProofNode& pn = getCurrent(); - pn.d_children.emplace_back(); - d_stack.emplace_back(&pn.d_children.back()); -} -void LazyTreeProofGenerator::closeChild() -{ - Assert(getCurrent().d_rule != PfRule::UNKNOWN); - d_stack.pop_back(); -} -detail::TreeProofNode& LazyTreeProofGenerator::getCurrent() -{ - Assert(!d_stack.empty()) << "Proof construction has already been finished."; - return *d_stack.back(); -} -void LazyTreeProofGenerator::setCurrent(PfRule rule, - const std::vector& premise, - std::vector args, - Node proven) -{ - detail::TreeProofNode& pn = getCurrent(); - pn.d_rule = rule; - pn.d_premise = premise; - pn.d_args = args; - pn.d_proven = proven; -} -std::shared_ptr LazyTreeProofGenerator::getProof() const -{ - // Check cache - if (d_cached) return d_cached; - Assert(d_stack.empty()) << "Proof construction has not yet been finished."; - std::vector> scope; - d_cached = getProof(scope, d_proof); - return d_cached; -} - -std::shared_ptr LazyTreeProofGenerator::getProofFor(Node f) -{ - Assert(hasProofFor(f)); - return getProof(); -} - -bool LazyTreeProofGenerator::hasProofFor(Node f) -{ - return f == getProof()->getResult(); -} - -std::shared_ptr LazyTreeProofGenerator::getProof( - std::vector>& scope, - const detail::TreeProofNode& pn) const -{ - // Store scope size to reset scope afterwards - std::size_t before = scope.size(); - std::vector> children; - if (pn.d_rule == PfRule::SCOPE) - { - // Extend scope for all but the root node - if (&pn != &d_proof) - { - for (const auto& a : pn.d_args) - { - scope.emplace_back(d_pnm->mkAssume(a)); - } - } - } - else - { - // Initialize the children with the scope - children = scope; - } - for (auto& c : pn.d_children) - { - // Recurse into tree - children.emplace_back(getProof(scope, c)); - } - for (const auto& p : pn.d_premise) - { - // Add premises as assumptions - children.emplace_back(d_pnm->mkAssume(p)); - } - // Reset scope - scope.resize(before); - return d_pnm->mkNode(pn.d_rule, children, pn.d_args); -} - -void LazyTreeProofGenerator::print(std::ostream& os, - const std::string& prefix, - const detail::TreeProofNode& pn) const -{ - os << prefix << pn.d_rule << ": "; - container_to_stream(os, pn.d_premise); - os << " ==> " << pn.d_proven << std::endl; - if (!pn.d_args.empty()) - { - os << prefix << ":args "; - container_to_stream(os, pn.d_args); - std::cout << std::endl; - } - for (const auto& c : pn.d_children) - { - print(os, prefix + '\t', c); - } -} - -std::ostream& operator<<(std::ostream& os, const LazyTreeProofGenerator& ltpg) -{ - ltpg.print(os, "", ltpg.d_proof); - return os; -} - -} // namespace theory -} // namespace cvc5 diff --git a/src/theory/lazy_tree_proof_generator.h b/src/theory/lazy_tree_proof_generator.h deleted file mode 100644 index 7ab921a70..000000000 --- a/src/theory/lazy_tree_proof_generator.h +++ /dev/null @@ -1,223 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * The lazy tree proof generator class. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__THEORY__LAZY_TREE_PROOF_GENERATOR_H -#define CVC5__THEORY__LAZY_TREE_PROOF_GENERATOR_H - -#include - -#include "expr/node.h" -#include "expr/proof_generator.h" -#include "expr/proof_node_manager.h" - -namespace cvc5 { -namespace theory { -namespace detail { -/** - * A single node in the proof tree created by the LazyTreeProofGenerator. - * A node directly represents a ProofNode that is eventually constructed from - * it. The Nodes of the additional field d_premise are added to d_children as - * new assumptions via ASSUME. - */ -struct TreeProofNode -{ - /** The proof rule */ - PfRule d_rule = PfRule::UNKNOWN; - /** Assumptions used as premise for this proof step */ - std::vector d_premise; - /** Arguments for this proof step */ - std::vector d_args; - /** Conclusion of this proof step */ - Node d_proven; - /** Children of this proof step */ - std::vector d_children; -}; -} // namespace detail - -/** - * This class supports the lazy creation of a tree-shaped proof. - * The core idea is that the lazy creation is necessary because proof rules - * depend on assumptions that are only known after the whole subtree has been - * finished. - * - * We indend to argue about proofs that roughly go as follows: - * - we assume a set of assumptions - * - we do a case split - * - for every case, we derive false, possibly by nested case splits - * - * Every case is represented by a SCOPE whose child derives false. When - * composing the final proof, we explicitly extend the premise of every proof - * step with the scope (the currently selected case) that this proof step lives - * in. When doing this, we ignore the outermost scope (which assumes the - * assertion set) to allow for having conflicts that are only a subset of the - * assertion set. - * - * Consider the example x*x<1 and x>2 and the intended proof - * (SCOPE - * (ARITH_NL_CAD_SPLIT - * (SCOPE - * (ARITH_NL_CAD_DIRECT (x<=2 and x>2) ==> false) - * :args [x<=2] - * ) - * (SCOPE - * (ARITH_NL_CAD_DIRECT (x>=1 and x*x<1) ==> false) - * :args [x>=1] - * ) - * ) - * :args [ x*x<1, x>2 ] - * ) - * We obtain this proof in a way that (in general) gives us the assumptions - * for the scopes (x<=2, x>=1) only when the scope children have been fully - * computed. Thus, we store the proof in an intermediate form and add the scope - * arguments when we learn them. Once we have completed the proof, we can easily - * transform it into proper ProofNodes. - * - * This class is stateful in the sense that the interface (in particular - * openChild() and closeChild()) navigates the proof tree (and creating it - * on-the-fly). Initially, and after reset() has been called, the proof consists - * of one empty proof node (with proof rule UNKNOWN). It stores the current - * position in the proof tree internally to make the interface easier to use. - * - * THIS MAKES THIS CLASS INHERENTLY NOT THREAD-SAFE! - * - * To construct the above proof, use this class roughly as follows: - * setCurrent(SCOPE, {}, assertions, false); - * openChild(); - * // First nested scope - * openChild(); - * setCurrent(SCOPE, {}, {}, false); - * openChild(); - * setCurrent(CAD_DIRECT, {x>2}, {}, false); - * closeChild(); - * getCurrent().args = {x<=2}; - * closeChild(); - * // Second nested scope - * openChild(); - * setCurrent(SCOPE, {}, {}, false); - * openChild(); - * setCurrent(CAD_DIRECT, {x*x<1}, {}, false); - * closeChild(); - * getCurrent().args = {x>=1}; - * closeChild(); - * // Finish split - * setCurrent(CAD_SPLIT, {}, {}, false); - * closeChild(); - * closeChild(); - * - * To explicitly finish proof construction, we need to call closeChild() one - * additional time. - */ -class LazyTreeProofGenerator : public ProofGenerator -{ - public: - friend std::ostream& operator<<(std::ostream& os, - const LazyTreeProofGenerator& ltpg); - - LazyTreeProofGenerator(ProofNodeManager* pnm, - const std::string& name = "LazyTreeProofGenerator"); - - std::string identify() const override { return d_name; } - /** Create a new child and make it the current node */ - void openChild(); - /** - * Finish the current node and return to its parent - * Checks that the current node has a proof rule different from UNKNOWN. - * When called on the root node, this finishes the proof construction: we can - * no longer call getCurrent(), setCurrent() openChild() and closeChild(), but - * instead can call getProof() now. - */ - void closeChild(); - /** - * Return a reference to the current node - */ - detail::TreeProofNode& getCurrent(); - /** Set the current node / proof step */ - void setCurrent(PfRule rule, - const std::vector& premise, - std::vector args, - Node proven); - /** Construct the proof as a ProofNode */ - std::shared_ptr getProof() const; - /** Return the constructed proof. Checks that we have proven f */ - std::shared_ptr getProofFor(Node f) override; - /** Checks whether we have proven f */ - bool hasProofFor(Node f) override; - - /** - * Removes children from the current node based on the given predicate. - * It can be used for cases where facts (and their proofs) are eagerly - * generated and then later pruned, for example to produce smaller conflicts. - * The predicate is given as a Callable f that is called for every child with - * the id of the child and the child itself. - * f should return true if the child should be kept, fals if the child should - * be removed. - * @param f a Callable bool(std::size_t, const detail::TreeProofNode&) - */ - template - void pruneChildren(F&& f) - { - auto& children = getCurrent().d_children; - std::size_t cur = 0; - std::size_t pos = 0; - for (std::size_t size = children.size(); cur < size; ++cur) - { - if (f(cur, children[pos])) - { - if (cur != pos) - { - children[pos] = std::move(children[cur]); - } - ++pos; - } - } - children.resize(pos); - } - - private: - /** recursive proof construction used by getProof() */ - std::shared_ptr getProof( - std::vector>& scope, - const detail::TreeProofNode& pn) const; - - /** recursive debug printing used by operator<<() */ - void print(std::ostream& os, - const std::string& prefix, - const detail::TreeProofNode& pn) const; - - /** The ProofNodeManager used for constructing ProofNodes */ - ProofNodeManager* d_pnm; - /** The trace to the current node */ - std::vector d_stack; - /** The root node of the proof tree */ - detail::TreeProofNode d_proof; - /** Caches the result of getProof() */ - mutable std::shared_ptr d_cached; - /** Name of this proof generator */ - std::string d_name; -}; - -/** - * Prints the current state of a LazyTreeProofGenerator. Can also be used on a - * partially constructed proof. It is intended for debugging only and attempts - * to pretty-print itself using indentation. - */ -std::ostream& operator<<(std::ostream& os, const LazyTreeProofGenerator& ltpg); - -} // namespace theory -} // namespace cvc5 - -#endif diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index 8e802f875..b681dad17 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -18,8 +18,8 @@ #ifndef CVC5__THEORY__OUTPUT_CHANNEL_H #define CVC5__THEORY__OUTPUT_CHANNEL_H +#include "proof/trust_node.h" #include "theory/incomplete_id.h" -#include "theory/trust_node.h" #include "util/resource_manager.h" namespace cvc5 { diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp index 9effdbec7..71f4028ec 100644 --- a/src/theory/quantifiers/instantiate.cpp +++ b/src/theory/quantifiers/instantiate.cpp @@ -15,12 +15,12 @@ #include "theory/quantifiers/instantiate.h" -#include "expr/lazy_proof.h" #include "expr/node_algorithm.h" -#include "expr/proof_node_manager.h" #include "options/printer_options.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" +#include "proof/lazy_proof.h" +#include "proof/proof_node_manager.h" #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" #include "theory/quantifiers/cegqi/inst_strategy_cegqi.h" diff --git a/src/theory/quantifiers/instantiate.h b/src/theory/quantifiers/instantiate.h index 9b410dd08..95a396d51 100644 --- a/src/theory/quantifiers/instantiate.h +++ b/src/theory/quantifiers/instantiate.h @@ -22,7 +22,7 @@ #include "context/cdhashset.h" #include "expr/node.h" -#include "expr/proof.h" +#include "proof/proof.h" #include "theory/inference_id.h" #include "theory/quantifiers/inst_match_trie.h" #include "theory/quantifiers/quant_util.h" diff --git a/src/theory/quantifiers/proof_checker.h b/src/theory/quantifiers/proof_checker.h index 618ac8301..23441772e 100644 --- a/src/theory/quantifiers/proof_checker.h +++ b/src/theory/quantifiers/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__QUANTIFIERS__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index cfc4eca2e..ae7f75f34 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -18,8 +18,8 @@ #ifndef CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H #define CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H +#include "proof/trust_node.h" #include "theory/theory_rewriter.h" -#include "theory/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/quantifiers/skolemize.cpp b/src/theory/quantifiers/skolemize.cpp index c9234db2c..fa91b1782 100644 --- a/src/theory/quantifiers/skolemize.cpp +++ b/src/theory/quantifiers/skolemize.cpp @@ -17,11 +17,11 @@ #include "expr/dtype.h" #include "expr/dtype_cons.h" -#include "expr/proof.h" -#include "expr/proof_node_manager.h" #include "expr/skolem_manager.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" +#include "proof/proof.h" +#include "proof/proof_node_manager.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/quantifiers_state.h" #include "theory/quantifiers/term_registry.h" diff --git a/src/theory/quantifiers/skolemize.h b/src/theory/quantifiers/skolemize.h index 2a09913a9..c8e6ec7dd 100644 --- a/src/theory/quantifiers/skolemize.h +++ b/src/theory/quantifiers/skolemize.h @@ -24,8 +24,8 @@ #include "context/cdhashmap.h" #include "expr/node.h" #include "expr/type_node.h" -#include "theory/eager_proof_generator.h" -#include "theory/trust_node.h" +#include "proof/eager_proof_generator.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index 6bfedb0a2..c74146c9c 100644 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -15,8 +15,8 @@ #include "theory/quantifiers/theory_quantifiers.h" -#include "expr/proof_node_manager.h" #include "options/quantifiers_options.h" +#include "proof/proof_node_manager.h" #include "theory/quantifiers/quantifiers_macros.h" #include "theory/quantifiers/quantifiers_modules.h" #include "theory/quantifiers/quantifiers_rewriter.h" diff --git a/src/theory/rewriter.cpp b/src/theory/rewriter.cpp index 1094d5920..eeb2f166d 100644 --- a/src/theory/rewriter.cpp +++ b/src/theory/rewriter.cpp @@ -15,8 +15,8 @@ #include "theory/rewriter.h" -#include "expr/term_conversion_proof_generator.h" #include "options/theory_options.h" +#include "proof/conv_proof_generator.h" #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "smt/smt_statistics_registry.h" diff --git a/src/theory/sets/term_registry.h b/src/theory/sets/term_registry.h index 87f25341e..f88596ffe 100644 --- a/src/theory/sets/term_registry.h +++ b/src/theory/sets/term_registry.h @@ -22,7 +22,7 @@ #include #include "context/cdhashmap.h" -#include "theory/eager_proof_generator.h" +#include "proof/eager_proof_generator.h" #include "theory/sets/inference_manager.h" #include "theory/sets/skolem_cache.h" #include "theory/sets/solver_state.h" diff --git a/src/theory/shared_terms_database.h b/src/theory/shared_terms_database.h index 40f6080e5..b684ff56f 100644 --- a/src/theory/shared_terms_database.h +++ b/src/theory/shared_terms_database.h @@ -22,10 +22,10 @@ #include "context/cdhashset.h" #include "expr/node.h" -#include "expr/proof_node_manager.h" +#include "proof/proof_node_manager.h" +#include "proof/trust_node.h" #include "theory/ee_setup_info.h" #include "theory/theory_id.h" -#include "theory/trust_node.h" #include "theory/uf/equality_engine.h" #include "theory/uf/proof_equality_engine.h" #include "util/statistics_stats.h" diff --git a/src/theory/skolem_lemma.h b/src/theory/skolem_lemma.h index 2cb1c6738..277daf88c 100644 --- a/src/theory/skolem_lemma.h +++ b/src/theory/skolem_lemma.h @@ -19,7 +19,7 @@ #define CVC5__THEORY__SKOLEM_LEMMA_H #include "expr/node.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/strings/infer_proof_cons.cpp b/src/theory/strings/infer_proof_cons.cpp index 29a4187ec..d214babae 100644 --- a/src/theory/strings/infer_proof_cons.cpp +++ b/src/theory/strings/infer_proof_cons.cpp @@ -15,10 +15,10 @@ #include "theory/strings/infer_proof_cons.h" -#include "expr/proof_node_manager.h" #include "expr/skolem_manager.h" #include "options/smt_options.h" #include "options/strings_options.h" +#include "proof/proof_node_manager.h" #include "theory/builtin/proof_checker.h" #include "theory/rewriter.h" #include "theory/strings/regexp_operation.h" diff --git a/src/theory/strings/infer_proof_cons.h b/src/theory/strings/infer_proof_cons.h index af905cbad..bba03dd28 100644 --- a/src/theory/strings/infer_proof_cons.h +++ b/src/theory/strings/infer_proof_cons.h @@ -21,12 +21,12 @@ #include #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_rule.h" +#include "proof/proof_checker.h" +#include "proof/proof_rule.h" +#include "proof/theory_proof_step_buffer.h" #include "theory/builtin/proof_checker.h" #include "theory/strings/infer_info.h" #include "theory/strings/sequences_stats.h" -#include "theory/theory_proof_step_buffer.h" #include "theory/uf/proof_equality_engine.h" namespace cvc5 { diff --git a/src/theory/strings/inference_manager.h b/src/theory/strings/inference_manager.h index cf9c64bb1..da44100f9 100644 --- a/src/theory/strings/inference_manager.h +++ b/src/theory/strings/inference_manager.h @@ -24,7 +24,7 @@ #include "context/cdhashset.h" #include "context/context.h" #include "expr/node.h" -#include "expr/proof_node_manager.h" +#include "proof/proof_node_manager.h" #include "theory/ext_theory.h" #include "theory/inference_manager_buffered.h" #include "theory/output_channel.h" diff --git a/src/theory/strings/proof_checker.h b/src/theory/strings/proof_checker.h index d3ede53ec..56afaed84 100644 --- a/src/theory/strings/proof_checker.h +++ b/src/theory/strings/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__STRINGS__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/strings/regexp_elim.cpp b/src/theory/strings/regexp_elim.cpp index b9af7a23f..c3580d963 100644 --- a/src/theory/strings/regexp_elim.cpp +++ b/src/theory/strings/regexp_elim.cpp @@ -15,8 +15,8 @@ #include "theory/strings/regexp_elim.h" -#include "expr/proof_node_manager.h" #include "options/strings_options.h" +#include "proof/proof_node_manager.h" #include "theory/rewriter.h" #include "theory/strings/regexp_entail.h" #include "theory/strings/theory_strings_utils.h" diff --git a/src/theory/strings/regexp_elim.h b/src/theory/strings/regexp_elim.h index 5387548de..6187a7137 100644 --- a/src/theory/strings/regexp_elim.h +++ b/src/theory/strings/regexp_elim.h @@ -18,8 +18,8 @@ #define CVC5__THEORY__STRINGS__REGEXP_ELIM_H #include "expr/node.h" -#include "theory/eager_proof_generator.h" -#include "theory/trust_node.h" +#include "proof/eager_proof_generator.h" +#include "proof/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/strings/term_registry.h b/src/theory/strings/term_registry.h index 7c399759b..7d660e019 100644 --- a/src/theory/strings/term_registry.h +++ b/src/theory/strings/term_registry.h @@ -20,8 +20,8 @@ #include "context/cdhashset.h" #include "context/cdlist.h" -#include "expr/proof_node_manager.h" -#include "theory/eager_proof_generator.h" +#include "proof/eager_proof_generator.h" +#include "proof/proof_node_manager.h" #include "theory/output_channel.h" #include "theory/strings/infer_info.h" #include "theory/strings/sequences_stats.h" diff --git a/src/theory/theory.h b/src/theory/theory.h index 8f69d4cb2..d71348ce3 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -28,12 +28,12 @@ #include "context/context.h" #include "expr/node.h" #include "options/theory_options.h" +#include "proof/trust_node.h" #include "theory/assertion.h" #include "theory/care_graph.h" #include "theory/logic_info.h" #include "theory/skolem_lemma.h" #include "theory/theory_id.h" -#include "theory/trust_node.h" #include "theory/valuation.h" #include "util/statistics_stats.h" diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index abbca451a..676351dd5 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -20,15 +20,15 @@ #include "base/map_util.h" #include "decision/decision_engine.h" #include "expr/attribute.h" -#include "expr/lazy_proof.h" #include "expr/node_builder.h" #include "expr/node_visitor.h" -#include "expr/proof_checker.h" -#include "expr/proof_ensure_closed.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" #include "options/theory_options.h" #include "printer/printer.h" +#include "proof/lazy_proof.h" +#include "proof/proof_checker.h" +#include "proof/proof_ensure_closed.h" #include "prop/prop_engine.h" #include "smt/dump.h" #include "smt/env.h" diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 6887959ed..ffcaf392f 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -25,6 +25,7 @@ #include "context/cdhashmap.h" #include "expr/node.h" #include "options/theory_options.h" +#include "proof/trust_node.h" #include "theory/atom_requests.h" #include "theory/engine_output_channel.h" #include "theory/interrupted.h" @@ -32,7 +33,6 @@ #include "theory/sort_inference.h" #include "theory/theory.h" #include "theory/theory_preprocessor.h" -#include "theory/trust_node.h" #include "theory/trust_substitutions.h" #include "theory/uf/equality_engine.h" #include "theory/valuation.h" diff --git a/src/theory/theory_engine_proof_generator.cpp b/src/theory/theory_engine_proof_generator.cpp index 609d7ae8a..f10716bd9 100644 --- a/src/theory/theory_engine_proof_generator.cpp +++ b/src/theory/theory_engine_proof_generator.cpp @@ -17,7 +17,7 @@ #include -#include "expr/proof_node.h" +#include "proof/proof_node.h" using namespace cvc5::kind; diff --git a/src/theory/theory_engine_proof_generator.h b/src/theory/theory_engine_proof_generator.h index 48969aa21..ab0f616fe 100644 --- a/src/theory/theory_engine_proof_generator.h +++ b/src/theory/theory_engine_proof_generator.h @@ -22,10 +22,10 @@ #include "context/cdhashmap.h" #include "context/context.h" -#include "expr/lazy_proof.h" -#include "expr/proof_generator.h" -#include "expr/proof_node_manager.h" -#include "theory/trust_node.h" +#include "proof/lazy_proof.h" +#include "proof/proof_generator.h" +#include "proof/proof_node_manager.h" +#include "proof/trust_node.h" namespace cvc5 { diff --git a/src/theory/theory_inference_manager.h b/src/theory/theory_inference_manager.h index 2cb7d9d30..06806f8d4 100644 --- a/src/theory/theory_inference_manager.h +++ b/src/theory/theory_inference_manager.h @@ -22,10 +22,10 @@ #include "context/cdhashset.h" #include "expr/node.h" -#include "expr/proof_rule.h" +#include "proof/proof_rule.h" +#include "proof/trust_node.h" #include "theory/inference_id.h" #include "theory/output_channel.h" -#include "theory/trust_node.h" #include "util/statistics_stats.h" namespace cvc5 { diff --git a/src/theory/theory_preprocessor.cpp b/src/theory/theory_preprocessor.cpp index 65bd160fc..230c23424 100644 --- a/src/theory/theory_preprocessor.cpp +++ b/src/theory/theory_preprocessor.cpp @@ -15,8 +15,8 @@ #include "theory/theory_preprocessor.h" -#include "expr/lazy_proof.h" #include "expr/skolem_manager.h" +#include "proof/lazy_proof.h" #include "smt/logic_exception.h" #include "theory/logic_info.h" #include "theory/rewriter.h" diff --git a/src/theory/theory_preprocessor.h b/src/theory/theory_preprocessor.h index 58dd763e0..6015f2b07 100644 --- a/src/theory/theory_preprocessor.h +++ b/src/theory/theory_preprocessor.h @@ -22,13 +22,13 @@ #include "context/cdhashmap.h" #include "context/context.h" -#include "expr/lazy_proof.h" #include "expr/node.h" -#include "expr/tconv_seq_proof_generator.h" -#include "expr/term_conversion_proof_generator.h" +#include "proof/conv_proof_generator.h" +#include "proof/conv_seq_proof_generator.h" +#include "proof/lazy_proof.h" +#include "proof/trust_node.h" #include "smt/term_formula_removal.h" #include "theory/skolem_lemma.h" -#include "theory/trust_node.h" namespace cvc5 { diff --git a/src/theory/theory_proof_step_buffer.cpp b/src/theory/theory_proof_step_buffer.cpp deleted file mode 100644 index 667c8d114..000000000 --- a/src/theory/theory_proof_step_buffer.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Haniel Barbosa, Andrew Reynolds, Aina Niemetz - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of theory proof step buffer utility. - */ - -#include "theory/theory_proof_step_buffer.h" - -#include "expr/proof.h" - -using namespace cvc5::kind; - -namespace cvc5 { -namespace theory { - -TheoryProofStepBuffer::TheoryProofStepBuffer(ProofChecker* pc) - : ProofStepBuffer(pc) -{ -} - -bool TheoryProofStepBuffer::applyEqIntro(Node src, - Node tgt, - const std::vector& exp, - MethodId ids, - MethodId ida, - MethodId idr) -{ - std::vector args; - args.push_back(src); - builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); - Node res = tryStep(PfRule::MACRO_SR_EQ_INTRO, exp, args); - if (res.isNull()) - { - // failed to apply - return false; - } - // should have concluded the expected equality - Node expected = src.eqNode(tgt); - if (res != expected) - { - // did not provide the correct target - popStep(); - return false; - } - // successfully proved src == tgt. - return true; -} - -bool TheoryProofStepBuffer::applyPredTransform(Node src, - Node tgt, - const std::vector& exp, - MethodId ids, - MethodId ida, - MethodId idr) -{ - // symmetric equalities - if (CDProof::isSame(src, tgt)) - { - return true; - } - std::vector children; - children.push_back(src); - std::vector args; - // try to prove that tgt rewrites to src - children.insert(children.end(), exp.begin(), exp.end()); - args.push_back(tgt); - builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); - Node res = tryStep(PfRule::MACRO_SR_PRED_TRANSFORM, children, args); - if (res.isNull()) - { - // failed to apply - return false; - } - // should definitely have concluded tgt - Assert(res == tgt); - return true; -} - -bool TheoryProofStepBuffer::applyPredIntro(Node tgt, - const std::vector& exp, - MethodId ids, - MethodId ida, - MethodId idr) -{ - std::vector args; - args.push_back(tgt); - builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); - Node res = tryStep(PfRule::MACRO_SR_PRED_INTRO, exp, args); - if (res.isNull()) - { - return false; - } - Assert(res == tgt); - return true; -} - -Node TheoryProofStepBuffer::applyPredElim(Node src, - const std::vector& exp, - MethodId ids, - MethodId ida, - MethodId idr) -{ - std::vector children; - children.push_back(src); - children.insert(children.end(), exp.begin(), exp.end()); - std::vector args; - builtin::BuiltinProofRuleChecker::addMethodIds(args, ids, ida, idr); - Node srcRew = tryStep(PfRule::MACRO_SR_PRED_ELIM, children, args); - if (CDProof::isSame(src, srcRew)) - { - popStep(); - } - return srcRew; -} - -Node TheoryProofStepBuffer::factorReorderElimDoubleNeg(Node n) -{ - if (n.getKind() != kind::OR) - { - return elimDoubleNegLit(n); - } - NodeManager* nm = NodeManager::currentNM(); - std::vector children{n.begin(), n.end()}; - std::vector childrenEqs; - // eliminate double neg for each lit. Do it first because it may expose - // duplicates - bool hasDoubleNeg = false; - for (unsigned i = 0; i < children.size(); ++i) - { - if (children[i].getKind() == kind::NOT - && children[i][0].getKind() == kind::NOT) - { - hasDoubleNeg = true; - childrenEqs.push_back(children[i].eqNode(children[i][0][0])); - addStep(PfRule::MACRO_SR_PRED_INTRO, - {}, - {childrenEqs.back()}, - childrenEqs.back()); - // update child - children[i] = children[i][0][0]; - } - else - { - childrenEqs.push_back(children[i].eqNode(children[i])); - addStep(PfRule::REFL, {}, {children[i]}, childrenEqs.back()); - } - } - if (hasDoubleNeg) - { - Node oldn = n; - n = nm->mkNode(kind::OR, children); - // Create a congruence step to justify replacement of each doubly negated - // literal. This is done to avoid having to use MACRO_SR_PRED_TRANSFORM - // from the old clause to the new one, which, under the standard rewriter, - // may not hold. An example is - // - // --------------------------------------------------------------------- - // (or (or (not x2) x1 x2) (not (not x2))) = (or (or (not x2) x1 x2) x2) - // - // which fails due to factoring not happening after flattening. - // - // Using congruence only the - // - // ------------------ MACRO_SR_PRED_INTRO - // (not (not t)) = t - // - // steps are added, which, since double negation is eliminated in a - // pre-rewrite in the Boolean rewriter, will always hold under the - // standard rewriter. - Node congEq = oldn.eqNode(n); - addStep(PfRule::CONG, - childrenEqs, - {ProofRuleChecker::mkKindNode(kind::OR)}, - congEq); - // add an equality resolution step to derive normalize clause - addStep(PfRule::EQ_RESOLVE, {oldn, congEq}, {}, n); - } - children.clear(); - // remove duplicates while keeping the order of children - std::unordered_set clauseSet; - unsigned size = n.getNumChildren(); - for (unsigned i = 0; i < size; ++i) - { - if (clauseSet.count(n[i])) - { - continue; - } - children.push_back(n[i]); - clauseSet.insert(n[i]); - } - // if factoring changed - if (children.size() < size) - { - Node factored = children.empty() - ? nm->mkConst(false) - : children.size() == 1 ? children[0] - : nm->mkNode(kind::OR, children); - // don't overwrite what already has a proof step to avoid cycles - addStep(PfRule::FACTORING, {n}, {}, factored); - n = factored; - } - // nothing to order - if (children.size() < 2) - { - return n; - } - // order - std::sort(children.begin(), children.end()); - Node ordered = nm->mkNode(kind::OR, children); - // if ordering changed - if (ordered != n) - { - // don't overwrite what already has a proof step to avoid cycles - addStep(PfRule::REORDERING, {n}, {ordered}, ordered); - } - return ordered; -} - -Node TheoryProofStepBuffer::elimDoubleNegLit(Node n) -{ - // eliminate double neg - if (n.getKind() == kind::NOT && n[0].getKind() == kind::NOT) - { - addStep(PfRule::NOT_NOT_ELIM, {n}, {}, n[0][0]); - return n[0][0]; - } - return n; -} - -} // namespace theory -} // namespace cvc5 diff --git a/src/theory/theory_proof_step_buffer.h b/src/theory/theory_proof_step_buffer.h deleted file mode 100644 index 1832ddb08..000000000 --- a/src/theory/theory_proof_step_buffer.h +++ /dev/null @@ -1,120 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Haniel Barbosa - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Theory proof step buffer utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__THEORY__THEORY_PROOF_STEP_BUFFER_H -#define CVC5__THEORY__THEORY_PROOF_STEP_BUFFER_H - -#include - -#include "expr/node.h" -#include "expr/proof_step_buffer.h" -#include "theory/builtin/proof_checker.h" - -namespace cvc5 { -namespace theory { -/** - * Class used to speculatively try and buffer a set of proof steps before - * sending them to a proof object, extended with theory-specfic proof rule - * utilities. - */ -class TheoryProofStepBuffer : public ProofStepBuffer -{ - public: - TheoryProofStepBuffer(ProofChecker* pc = nullptr); - ~TheoryProofStepBuffer() {} - //---------------------------- utilities builtin proof rules - /** - * Apply equality introduction. If this method returns true, it adds proof - * step(s) to the buffer that conclude (= src tgt) from premises exp. In - * particular, it may attempt to apply the rule MACRO_SR_EQ_INTRO. This - * method should be applied when tgt is equivalent to src assuming exp. - */ - bool applyEqIntro(Node src, - Node tgt, - const std::vector& exp, - MethodId ids = MethodId::SB_DEFAULT, - MethodId ida = MethodId::SBA_SEQUENTIAL, - MethodId idr = MethodId::RW_REWRITE); - /** - * Apply predicate transform. If this method returns true, it adds (at most - * one) proof step to the buffer that conclude tgt from premises src, exp. In - * particular, it may attempt to apply MACRO_SR_PRED_TRANSFORM. This method - * should be applied when src and tgt are equivalent formulas assuming exp. - */ - bool applyPredTransform(Node src, - Node tgt, - const std::vector& exp, - MethodId ids = MethodId::SB_DEFAULT, - MethodId ida = MethodId::SBA_SEQUENTIAL, - MethodId idr = MethodId::RW_REWRITE); - /** - * Apply predicate introduction. If this method returns true, it adds proof - * step(s) to the buffer that conclude tgt from premises exp. In particular, - * it may attempt to apply the rule MACRO_SR_PRED_INTRO. This method should be - * applied when tgt is equivalent to true assuming exp. - */ - bool applyPredIntro(Node tgt, - const std::vector& exp, - MethodId ids = MethodId::SB_DEFAULT, - MethodId ida = MethodId::SBA_SEQUENTIAL, - MethodId idr = MethodId::RW_REWRITE); - /** - * Apply predicate elimination. This method returns the result of applying - * the rule MACRO_SR_PRED_ELIM on src, exp. The returned formula is equivalent - * to src assuming exp. If the return value is equivalent to src, then no - * proof step is added to this buffer, since this step is a no-op in this - * case. - * - * Notice that in contrast to the other rules above, predicate elimination - * never fails and proves a formula that is not explicitly given as an - * argument tgt. Thus, the return value of this method is Node not bool. - */ - Node applyPredElim(Node src, - const std::vector& exp, - MethodId ids = MethodId::SB_DEFAULT, - MethodId ida = MethodId::SBA_SEQUENTIAL, - MethodId idr = MethodId::RW_REWRITE); - //---------------------------- end utilities builtin proof rules - - //---------------------------- utility methods for normalizing clauses - /** - * Normalizes a non-unit clause (an OR node) according to factoring and - * reordering, i.e. removes duplicates and reorders literals (according to - * node ids). Moreover it eliminates double negations, which can be done also - * for unit clauses (a arbitrary Boolean node). All normalization steps are - * tracked via proof steps added to this proof step buffer. - * - * @param n the clause to be normalized - * @return the normalized clause node - */ - Node factorReorderElimDoubleNeg(Node n); - - /** - * Eliminates double negation of a literal if it has the form - * (not (not t)) - * If the elimination happens, a step is added to this proof step buffer. - * - * @param n the node to have the top-level double negation eliminated - * @return the normalized clause node - */ - Node elimDoubleNegLit(Node n); -}; - -} // namespace theory -} // namespace cvc5 - -#endif /* CVC5__THEORY__THEORY_PROOF_STEP_BUFFER_H */ diff --git a/src/theory/theory_rewriter.h b/src/theory/theory_rewriter.h index 031e32db4..eca1e4e56 100644 --- a/src/theory/theory_rewriter.h +++ b/src/theory/theory_rewriter.h @@ -21,7 +21,7 @@ #define CVC5__THEORY__THEORY_REWRITER_H #include "expr/node.h" -#include "theory/trust_node.h" +#include "proof/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/trust_node.cpp b/src/theory/trust_node.cpp deleted file mode 100644 index 4086d6e86..000000000 --- a/src/theory/trust_node.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * Implementation of the trust node utility. - */ - -#include "theory/trust_node.h" - -#include "expr/proof_ensure_closed.h" -#include "expr/proof_generator.h" - -namespace cvc5 { -namespace theory { - -const char* toString(TrustNodeKind tnk) -{ - switch (tnk) - { - case TrustNodeKind::CONFLICT: return "CONFLICT"; - case TrustNodeKind::LEMMA: return "LEMMA"; - case TrustNodeKind::PROP_EXP: return "PROP_EXP"; - case TrustNodeKind::REWRITE: return "REWRITE"; - default: return "?"; - } -} - -std::ostream& operator<<(std::ostream& out, TrustNodeKind tnk) -{ - out << toString(tnk); - return out; -} - -TrustNode TrustNode::mkTrustConflict(Node conf, ProofGenerator* g) -{ - Node ckey = getConflictProven(conf); - // if a generator is provided, should confirm that it can prove it - Assert(g == nullptr || g->hasProofFor(ckey)); - return TrustNode(TrustNodeKind::CONFLICT, ckey, g); -} - -TrustNode TrustNode::mkTrustLemma(Node lem, ProofGenerator* g) -{ - Node lkey = getLemmaProven(lem); - // if a generator is provided, should confirm that it can prove it - Assert(g == nullptr || g->hasProofFor(lkey)); - return TrustNode(TrustNodeKind::LEMMA, lkey, g); -} - -TrustNode TrustNode::mkTrustPropExp(TNode lit, Node exp, ProofGenerator* g) -{ - Node pekey = getPropExpProven(lit, exp); - Assert(g == nullptr || g->hasProofFor(pekey)); - return TrustNode(TrustNodeKind::PROP_EXP, pekey, g); -} - -TrustNode TrustNode::mkTrustRewrite(TNode n, Node nr, ProofGenerator* g) -{ - Node rkey = getRewriteProven(n, nr); - Assert(g == nullptr || g->hasProofFor(rkey)); - return TrustNode(TrustNodeKind::REWRITE, rkey, g); -} - -TrustNode TrustNode::null() -{ - return TrustNode(TrustNodeKind::INVALID, Node::null()); -} - -TrustNode::TrustNode(TrustNodeKind tnk, Node p, ProofGenerator* g) - : d_tnk(tnk), d_proven(p), d_gen(g) -{ - // does not make sense to provide null node with generator - Assert(!d_proven.isNull() || d_gen == nullptr); -} - -TrustNodeKind TrustNode::getKind() const { return d_tnk; } - -Node TrustNode::getNode() const -{ - switch (d_tnk) - { - // the node of lemma is the node itself - case TrustNodeKind::LEMMA: return d_proven; - // the node of the rewrite is the right hand side of EQUAL - case TrustNodeKind::REWRITE: return d_proven[1]; - // the node of an explained propagation is the antecendant of an IMPLIES - // the node of a conflict is underneath a NOT - default: return d_proven[0]; - } -} - -Node TrustNode::getProven() const { return d_proven; } - -ProofGenerator* TrustNode::getGenerator() const { return d_gen; } - -bool TrustNode::isNull() const { return d_proven.isNull(); } - -std::shared_ptr TrustNode::toProofNode() const -{ - if (d_gen == nullptr) - { - return nullptr; - } - return d_gen->getProofFor(d_proven); -} - -Node TrustNode::getConflictProven(Node conf) { return conf.notNode(); } - -Node TrustNode::getLemmaProven(Node lem) { return lem; } - -Node TrustNode::getPropExpProven(TNode lit, Node exp) -{ - return NodeManager::currentNM()->mkNode(kind::IMPLIES, exp, lit); -} - -Node TrustNode::getRewriteProven(TNode n, Node nr) { return n.eqNode(nr); } - -void TrustNode::debugCheckClosed(const char* c, - const char* ctx, - bool reqNullGen) -{ - pfgEnsureClosed(d_proven, d_gen, c, ctx, reqNullGen); -} - -std::string TrustNode::identifyGenerator() const -{ - if (d_gen == nullptr) - { - return "null"; - } - return d_gen->identify(); -} - -std::ostream& operator<<(std::ostream& out, TrustNode n) -{ - out << "(" << n.getKind() << " " << n.getProven() << " " - << n.identifyGenerator() << ")"; - return out; -} - -} // namespace theory -} // namespace cvc5 diff --git a/src/theory/trust_node.h b/src/theory/trust_node.h deleted file mode 100644 index 0a4f20c34..000000000 --- a/src/theory/trust_node.h +++ /dev/null @@ -1,178 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Andrew Reynolds, Gereon Kremer - * - * This file is part of the cvc5 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. - * **************************************************************************** - * - * The trust node utility. - */ - -#include "cvc5_private.h" - -#ifndef CVC5__THEORY__TRUST_NODE_H -#define CVC5__THEORY__TRUST_NODE_H - -#include "expr/node.h" - -namespace cvc5 { - -class ProofGenerator; -class ProofNode; - -namespace theory { - -/** A kind for trust nodes */ -enum class TrustNodeKind : uint32_t -{ - CONFLICT, - LEMMA, - PROP_EXP, - REWRITE, - INVALID -}; -/** - * Converts a proof rule to a string. Note: This function is also used in - * `safe_print()`. Changing this function name or signature will result in - * `safe_print()` printing "" instead of the proper strings for - * the enum values. - * - * Returns a string with static lifetime: it should not be freed. - * - * @param tnk The trust node kind - * @return The name of the trust node kind - */ -const char* toString(TrustNodeKind tnk); - -/** - * Writes a trust node kind name to a stream. - * - * @param out The stream to write to - * @param tnk The trust node kind to write to the stream - * @return The stream - */ -std::ostream& operator<<(std::ostream& out, TrustNodeKind tnk); - -/** - * A trust node is a pair (F, G) where F is a formula and G is a proof - * generator that can construct a proof for F if asked. - * - * More generally, a trust node is any node that can be used for a specific - * purpose that requires justification, such as being passed to - * OutputChannel::lemma. That justification is intended to be given by the - * generator that is required to construct this object. - * - * They are intended to be constructed by ProofGenerator objects themselves (a - * proof generator wraps itself in TrustNode it returns) and passed - * to ProofOutputChannel by theory solvers. - * - * The static functions for constructing them check that the generator, if - * provided, is capable of proving the given conflict or lemma, or an assertion - * failure occurs. Otherwise an assertion error is given. - * - * While this is not enforced, a `TrustNode` generally encapsulates a **closed** proof - * of the formula: one without free assumptions. - */ -class TrustNode -{ - public: - TrustNode() : d_tnk(TrustNodeKind::INVALID), d_gen(nullptr) {} - /** Make a proven node for conflict */ - static TrustNode mkTrustConflict(Node conf, ProofGenerator* g = nullptr); - /** Make a proven node for lemma */ - static TrustNode mkTrustLemma(Node lem, ProofGenerator* g = nullptr); - /** Make a proven node for explanation of propagated literal */ - static TrustNode mkTrustPropExp(TNode lit, - Node exp, - ProofGenerator* g = nullptr); - /** Make a proven node for rewrite */ - static TrustNode mkTrustRewrite(TNode n, - Node nr, - ProofGenerator* g = nullptr); - /** The null proven node */ - static TrustNode null(); - ~TrustNode() {} - /** get kind */ - TrustNodeKind getKind() const; - /** get node - * - * This is the node that is used in a common interface, either: - * (1) A T-unsat conjunction conf to pass to OutputChannel::conflict, - * (2) A valid T-formula lem to pass to OutputChannel::lemma, - * (3) A conjunction of literals exp to return in Theory::explain(lit), or - * (4) A result of rewriting a term n into an equivalent one nr. - * - * Notice that this node does not necessarily correspond to a valid formula. - * The call getProven() below retrieves a valid formula corresponding to - * the above calls. - */ - Node getNode() const; - /** get proven - * - * This is the corresponding formula that is proven by the proof generator - * for the above cases: - * (1) (not conf), for conflicts, - * (2) lem, for lemmas, - * (3) (=> exp lit), for propagations from explanations, - * (4) (= n nr), for results of rewriting. - * - * When constructing this trust node, the proof generator should be able to - * provide a proof for this fact. - */ - Node getProven() const; - /** get generator */ - ProofGenerator* getGenerator() const; - /** is null? */ - bool isNull() const; - /** - * Gets the proof node for this trust node, which is obtained by - * calling the generator's getProofFor method on the proven node. - */ - std::shared_ptr toProofNode() const; - - /** Get the proven formula corresponding to a conflict call */ - static Node getConflictProven(Node conf); - /** Get the proven formula corresponding to a lemma call */ - static Node getLemmaProven(Node lem); - /** Get the proven formula corresponding to explanations for propagation */ - static Node getPropExpProven(TNode lit, Node exp); - /** Get the proven formula corresponding to a rewrite */ - static Node getRewriteProven(TNode n, Node nr); - /** For debugging */ - std::string identifyGenerator() const; - - /** - * debug check closed on Trace c, context ctx is string for debugging - * - * @param reqNullGen Whether we consider a null generator to be a failure. - */ - void debugCheckClosed(const char* c, const char* ctx, bool reqNullGen = true); - - private: - TrustNode(TrustNodeKind tnk, Node p, ProofGenerator* g = nullptr); - /** The kind */ - TrustNodeKind d_tnk; - /** The proven node */ - Node d_proven; - /** The generator */ - ProofGenerator* d_gen; -}; - -/** - * Writes a trust node to a stream. - * - * @param out The stream to write to - * @param n The trust node to write to the stream - * @return The stream - */ -std::ostream& operator<<(std::ostream& out, TrustNode n); - -} // namespace theory -} // namespace cvc5 - -#endif /* CVC5__THEORY__TRUST_NODE_H */ diff --git a/src/theory/trust_substitutions.h b/src/theory/trust_substitutions.h index 136992fbe..2a6997d1d 100644 --- a/src/theory/trust_substitutions.h +++ b/src/theory/trust_substitutions.h @@ -21,14 +21,14 @@ #include "context/cdhashmap.h" #include "context/cdlist.h" #include "context/context.h" -#include "expr/lazy_proof.h" -#include "expr/proof_node_manager.h" -#include "expr/proof_set.h" -#include "expr/term_conversion_proof_generator.h" -#include "theory/eager_proof_generator.h" +#include "proof/conv_proof_generator.h" +#include "proof/eager_proof_generator.h" +#include "proof/lazy_proof.h" +#include "proof/proof_node_manager.h" +#include "proof/proof_set.h" +#include "proof/theory_proof_step_buffer.h" +#include "proof/trust_node.h" #include "theory/substitutions.h" -#include "theory/theory_proof_step_buffer.h" -#include "theory/trust_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/uf/eq_proof.cpp b/src/theory/uf/eq_proof.cpp index c63f8b51d..fbed7ffb5 100644 --- a/src/theory/uf/eq_proof.cpp +++ b/src/theory/uf/eq_proof.cpp @@ -16,9 +16,9 @@ #include "theory/uf/eq_proof.h" #include "base/configuration.h" -#include "expr/proof.h" -#include "expr/proof_checker.h" #include "options/uf_options.h" +#include "proof/proof.h" +#include "proof/proof_checker.h" namespace cvc5 { namespace theory { diff --git a/src/theory/uf/proof_checker.h b/src/theory/uf/proof_checker.h index 55f7db3ba..caeda828e 100644 --- a/src/theory/uf/proof_checker.h +++ b/src/theory/uf/proof_checker.h @@ -19,8 +19,8 @@ #define CVC5__THEORY__UF__PROOF_CHECKER_H #include "expr/node.h" -#include "expr/proof_checker.h" -#include "expr/proof_node.h" +#include "proof/proof_checker.h" +#include "proof/proof_node.h" namespace cvc5 { namespace theory { diff --git a/src/theory/uf/proof_equality_engine.cpp b/src/theory/uf/proof_equality_engine.cpp index ab36cd9df..b4522d9df 100644 --- a/src/theory/uf/proof_equality_engine.cpp +++ b/src/theory/uf/proof_equality_engine.cpp @@ -15,9 +15,9 @@ #include "theory/uf/proof_equality_engine.h" -#include "expr/lazy_proof_chain.h" -#include "expr/proof_node.h" -#include "expr/proof_node_manager.h" +#include "proof/lazy_proof_chain.h" +#include "proof/proof_node.h" +#include "proof/proof_node_manager.h" #include "theory/rewriter.h" #include "theory/uf/eq_proof.h" #include "theory/uf/equality_engine.h" diff --git a/src/theory/uf/proof_equality_engine.h b/src/theory/uf/proof_equality_engine.h index 4ca13d93f..0c093d4b2 100644 --- a/src/theory/uf/proof_equality_engine.h +++ b/src/theory/uf/proof_equality_engine.h @@ -22,10 +22,10 @@ #include "context/cdhashmap.h" #include "context/cdhashset.h" -#include "expr/buffered_proof_generator.h" -#include "expr/lazy_proof.h" #include "expr/node.h" -#include "theory/eager_proof_generator.h" +#include "proof/buffered_proof_generator.h" +#include "proof/eager_proof_generator.h" +#include "proof/lazy_proof.h" namespace cvc5 { diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 9cf47bab3..1afb8cc31 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -21,11 +21,11 @@ #include #include "expr/node_algorithm.h" -#include "expr/proof_node_manager.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" #include "options/theory_options.h" #include "options/uf_options.h" +#include "proof/proof_node_manager.h" #include "smt/logic_exception.h" #include "theory/theory_model.h" #include "theory/type_enumerator.h" diff --git a/test/unit/test_smt.h b/test/unit/test_smt.h index 1aed7ce3f..a81deabc2 100644 --- a/test/unit/test_smt.h +++ b/test/unit/test_smt.h @@ -19,8 +19,8 @@ #include "expr/dtype_cons.h" #include "expr/node.h" #include "expr/node_manager.h" -#include "expr/proof_checker.h" #include "expr/skolem_manager.h" +#include "proof/proof_checker.h" #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "test.h" diff --git a/test/unit/util/stats_black.cpp b/test/unit/util/stats_black.cpp index 7fdf44298..669857e96 100644 --- a/test/unit/util/stats_black.cpp +++ b/test/unit/util/stats_black.cpp @@ -20,8 +20,8 @@ #include #include -#include "expr/proof_rule.h" #include "lib/clock_gettime.h" +#include "proof/proof_rule.h" #include "test.h" #include "util/statistics_registry.h" #include "util/statistics_stats.h"