Previously, we were traversing proof node as a tree, now we use a dag traversal.
This also makes sure that proofs work when we have a external proof conversion and we are in incremental mode. In such cases, the final proof must be cloned to ensure that we do not overwrite proof nodes, which may be reused across multiple check-sat.
return assumps.empty();
}
-std::shared_ptr<ProofNode> ProofNode::clone() const
-{
- std::vector<std::shared_ptr<ProofNode>> cchildren;
- for (const std::shared_ptr<ProofNode>& cp : d_children)
- {
- cchildren.push_back(cp->clone());
- }
- std::shared_ptr<ProofNode> thisc =
- std::make_shared<ProofNode>(d_rule, cchildren, d_args);
- thisc->d_proven = d_proven;
- return thisc;
-}
-
void ProofNode::setValue(
PfRule id,
const std::vector<std::shared_ptr<ProofNode>>& children,
bool isClosed();
/** Print debug on output strem os */
void printDebug(std::ostream& os) const;
- /** Clone, create a deep copy of this */
- std::shared_ptr<ProofNode> clone() const;
private:
/**
ProofChecker* ProofNodeManager::getChecker() const { return d_checker; }
+std::shared_ptr<ProofNode> ProofNodeManager::clone(
+ std::shared_ptr<ProofNode> pn)
+{
+ const ProofNode* orig = pn.get();
+ std::unordered_map<const ProofNode*, std::shared_ptr<ProofNode>> visited;
+ std::unordered_map<const ProofNode*, std::shared_ptr<ProofNode>>::iterator it;
+ std::vector<const ProofNode*> visit;
+ std::shared_ptr<ProofNode> 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<std::shared_ptr<ProofNode>>& children =
+ cur->getChildren();
+ for (const std::shared_ptr<ProofNode>& cp : children)
+ {
+ visit.push_back(cp.get());
+ }
+ continue;
+ }
+ visit.pop_back();
+ if (it->second.get() == nullptr)
+ {
+ std::vector<std::shared_ptr<ProofNode>> cchildren;
+ const std::vector<std::shared_ptr<ProofNode>>& children =
+ cur->getChildren();
+ for (const std::shared_ptr<ProofNode>& cp : children)
+ {
+ it = visited.find(cp.get());
+ Assert(it != visited.end());
+ Assert(it->second != nullptr);
+ cchildren.push_back(it->second);
+ }
+ cloned = std::make_shared<ProofNode>(
+ 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,
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<ProofNode> clone(std::shared_ptr<ProofNode> pn);
private:
/** The (optional) proof checker */
#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 "smt/assertions.h"
#include "smt/defined_function.h"
{
Trace("smt-proof") << "PfManager::printProof: start" << std::endl;
std::shared_ptr<ProofNode> fp = getFinalProof(pfn, as, df);
+ // if we are in incremental mode, we don't want to invalidate the proof
+ // nodes in fp, since these may be reused in further check-sat calls
+ if (options::incrementalSolving()
+ && options::proofFormatMode() != options::ProofFormatMode::NONE)
+ {
+ fp = d_pnm->clone(fp);
+ }
// TODO (proj #37) according to the proof format, post process the proof node
// TODO (proj #37) according to the proof format, print the proof node
return TrustNode::null();
}
// clone it so that we have a fresh copy
- pfBody = pfBody->clone();
+ pfBody = d_pnm->clone(pfBody);
Trace("pfee-proof") << "pfee::ensureProofForFact: add scope" << std::endl;
// The free assumptions must be closed by assumps, which should be passed
// as arguments of SCOPE. However, some of the free assumptions may not