summaryrefslogtreecommitdiff
path: root/src/proof
diff options
context:
space:
mode:
Diffstat (limited to 'src/proof')
-rw-r--r--src/proof/alethe/alethe_post_processor.cpp505
-rw-r--r--src/proof/lazy_proof.cpp7
-rw-r--r--src/proof/lazy_proof.h6
-rw-r--r--src/proof/proof_node_algorithm.cpp88
-rw-r--r--src/proof/proof_node_algorithm.h28
5 files changed, 537 insertions, 97 deletions
diff --git a/src/proof/alethe/alethe_post_processor.cpp b/src/proof/alethe/alethe_post_processor.cpp
index 920ca7dcb..2eb3d8d1d 100644
--- a/src/proof/alethe/alethe_post_processor.cpp
+++ b/src/proof/alethe/alethe_post_processor.cpp
@@ -18,6 +18,7 @@
#include "expr/node_algorithm.h"
#include "proof/proof.h"
#include "proof/proof_checker.h"
+#include "proof/proof_node_algorithm.h"
#include "theory/builtin/proof_checker.h"
#include "util/rational.h"
@@ -621,100 +622,7 @@ bool AletheProofPostprocessCallback::update(Node res,
}
}
- // If res is not an or node, then it's necessarily a singleton clause.
- bool isSingletonClause = res.getKind() != kind::OR;
- // Otherwise, we need to determine if res, which is of the form (or t1 ...
- // tn), corresponds to the clause (cl t1 ... tn) or to (cl (OR t1 ...
- // tn)). The only way in which the latter can happen is if res occurs as a
- // child in one of the premises, and is not eliminated afterwards. So we
- // search for res as a subterm of some children, which would mark its last
- // insertion into the resolution result. If res does not occur as the
- // pivot to be eliminated in a subsequent premise, then, and only then, it
- // is a singleton clause.
- if (!isSingletonClause)
- {
- size_t i;
- // Find out the last child to introduced res, if any. We only need to
- // look at the last one because any previous introduction would have
- // been eliminated.
- //
- // After the loop finishes i is the index of the child C_i that
- // introduced res. If i=0 none of the children introduced res as a
- // subterm and therefore it cannot be a singleton clause.
- for (i = children.size(); i > 0; --i)
- {
- // only non-singleton clauses may be introducing
- // res, so we only care about non-singleton or nodes. We check then
- // against the kind and whether the whole or node occurs as a pivot of
- // the respective resolution
- if (children[i - 1].getKind() != kind::OR)
- {
- continue;
- }
- size_t pivotIndex = (i != 1) ? 2 * (i - 1) - 1 : 1;
- if (args[pivotIndex] == children[i - 1]
- || args[pivotIndex].notNode() == children[i - 1])
- {
- continue;
- }
- // if res occurs as a subterm of a non-singleton premise
- if (std::find(children[i - 1].begin(), children[i - 1].end(), res)
- != children[i - 1].end())
- {
- break;
- }
- }
-
- // If res is a subterm of one of the children we still need to check if
- // that subterm is eliminated
- if (i > 0)
- {
- bool posFirst = (i == 1) ? (args[0] == trueNode)
- : (args[(2 * (i - 1)) - 2] == trueNode);
- Node pivot = (i == 1) ? args[1] : args[(2 * (i - 1)) - 1];
-
- // Check if it is eliminated by the previous resolution step
- if ((res == pivot && !posFirst)
- || (res.notNode() == pivot && posFirst)
- || (pivot.notNode() == res && posFirst))
- {
- // We decrease i by one, since it could have been the case that i
- // was equal to children.size(), so that isSingletonClause is set to
- // false
- --i;
- }
- else
- {
- // Otherwise check if any subsequent premise eliminates it
- for (; i < children.size(); ++i)
- {
- posFirst = args[(2 * i) - 2] == trueNode;
- pivot = args[(2 * i) - 1];
- // To eliminate res, the clause must contain it with opposite
- // polarity. There are three successful cases, according to the
- // pivot and its sign
- //
- // - res is the same as the pivot and posFirst is true, which
- // means that the clause contains its negation and eliminates it
- //
- // - res is the negation of the pivot and posFirst is false, so
- // the clause contains the node whose negation is res. Note that
- // this case may either be res.notNode() == pivot or res ==
- // pivot.notNode().
- if ((res == pivot && posFirst)
- || (res.notNode() == pivot && !posFirst)
- || (pivot.notNode() == res && !posFirst))
- {
- break;
- }
- }
- }
- }
- // if not eliminated (loop went to the end), then it's a singleton
- // clause
- isSingletonClause = i == children.size();
- }
- if (!isSingletonClause)
+ if (!expr::isSingletonClause(res, children, args))
{
return addAletheStepFromOr(
AletheRule::RESOLUTION, res, new_children, {}, *cdp);
@@ -760,6 +668,415 @@ bool AletheProofPostprocessCallback::update(Node res,
{},
*cdp);
}
+ // ======== Split
+ // See proof_rule.h for documentation on the SPLIT rule. This comment
+ // uses variable names as introduced there.
+ //
+ // --------- NOT_NOT --------- NOT_NOT
+ // VP1 VP2
+ // -------------------------------- RESOLUTION
+ // (cl F (not F))*
+ //
+ // VP1: (cl (not (not (not F))) F)
+ // VP2: (cl (not (not (not (not F)))) (not F))
+ //
+ // * the corresponding proof node is (or F (not F))
+ case PfRule::SPLIT:
+ {
+ Node vp1 = nm->mkNode(
+ kind::SEXPR, d_cl, args[0].notNode().notNode().notNode(), args[0]);
+ Node vp2 = nm->mkNode(kind::SEXPR,
+ d_cl,
+ args[0].notNode().notNode().notNode().notNode(),
+ args[0].notNode());
+
+ return addAletheStep(AletheRule::NOT_NOT, vp2, vp2, {}, {}, *cdp)
+ && addAletheStep(AletheRule::NOT_NOT, vp1, vp1, {}, {}, *cdp)
+ && addAletheStepFromOr(AletheRule::RESOLUTION, res, {vp1, vp2}, {}, *cdp);
+ }
+ // ======== Equality resolution
+ // See proof_rule.h for documentation on the EQ_RESOLVE rule. This
+ // comment uses variable names as introduced there.
+ //
+ // If F1 = (or G1 ... Gn), then P1 will be printed as (cl G1 ... Gn) but
+ // needs to be printed as (cl (or G1 ... Gn)). The only exception to this
+ // are ASSUME steps that are always printed as (cl (or G1 ... Gn)) and
+ // EQ_RESOLVE steps themselves.
+ //
+ // ------ ... ------ OR_NEG
+ // P1 VP21 ... VP2n
+ // ---------------------------- RESOLUTION
+ // VP3
+ // ---------------------------- DUPLICATED_LITERALS
+ // VP4
+ //
+ // for i=1 to n, VP2i: (cl (or G1 ... Gn) (not Gi))
+ // VP3: (cl (or G1 ... Gn)^n)
+ // VP4: (cl (or (G1 ... Gn))
+ //
+ // Let child1 = VP4.
+ //
+ //
+ // Otherwise, child1 = P1.
+ //
+ //
+ // Then, if F2 = false:
+ //
+ // ------ EQUIV_POS2
+ // VP1 P2 child1
+ // --------------------------------- RESOLUTION
+ // (cl)*
+ //
+ // Otherwise:
+ //
+ // ------ EQUIV_POS2
+ // VP1 P2 child1
+ // --------------------------------- RESOLUTION
+ // (cl F2)*
+ //
+ // VP1: (cl (not (= F1 F2)) (not F1) F2)
+ //
+ // * the corresponding proof node is F2
+ case PfRule::EQ_RESOLVE:
+ {
+ bool success = true;
+ Node vp1 =
+ nm->mkNode(kind::SEXPR,
+ {d_cl, children[1].notNode(), children[0].notNode(), res});
+ Node child1 = children[0];
+
+ // Transform (cl F1 ... Fn) into (cl (or F1 ... Fn))
+ if (children[0].notNode() != children[1].notNode()
+ && children[0].getKind() == kind::OR)
+ {
+ PfRule pr = cdp->getProofFor(child1)->getRule();
+ if (pr != PfRule::ASSUME && pr != PfRule::EQ_RESOLVE)
+ {
+ std::vector<Node> clauses{d_cl};
+ clauses.insert(clauses.end(),
+ children[0].begin(),
+ children[0].end()); //(cl G1 ... Gn)
+
+ std::vector<Node> vp2Nodes{children[0]};
+ std::vector<Node> resNodes{d_cl};
+ for (size_t i = 0, size = children[0].getNumChildren(); i < size; i++)
+ {
+ Node vp2i = nm->mkNode(
+ kind::SEXPR,
+ d_cl,
+ children[0],
+ children[0][i].notNode()); //(cl (or G1 ... Gn) (not Gi))
+ success &=
+ addAletheStep(AletheRule::OR_NEG, vp2i, vp2i, {}, {}, *cdp);
+ vp2Nodes.push_back(vp2i);
+ resNodes.push_back(children[0]);
+ }
+ Node vp3 = nm->mkNode(kind::SEXPR, resNodes);
+ success &= addAletheStep(
+ AletheRule::RESOLUTION, vp3, vp3, vp2Nodes, {}, *cdp);
+
+ Node vp4 = nm->mkNode(kind::SEXPR, d_cl, children[0]);
+ success &= addAletheStep(
+ AletheRule::DUPLICATED_LITERALS, vp4, vp4, {vp3}, {}, *cdp);
+ child1 = vp4;
+ }
+ }
+
+ success &= addAletheStep(AletheRule::EQUIV_POS2, vp1, vp1, {}, {}, *cdp);
+
+ return success &= addAletheStep(AletheRule::RESOLUTION,
+ res,
+ res == nm->mkConst(false)
+ ? nm->mkNode(kind::SEXPR, d_cl)
+ : nm->mkNode(kind::SEXPR, d_cl, res),
+ {vp1, children[1], child1},
+ {},
+ *cdp);
+ }
+ // ======== Modus ponens
+ // See proof_rule.h for documentation on the MODUS_PONENS rule. This comment
+ // uses variable names as introduced there.
+ //
+ // (P2:(=> F1 F2))
+ // ------------------------ IMPLIES
+ // (VP1:(cl (not F1) F2)) (P1:F1)
+ // -------------------------------------------- RESOLUTION
+ // (cl F2)*
+ //
+ // * the corresponding proof node is F2
+ case PfRule::MODUS_PONENS:
+ {
+ Node vp1 = nm->mkNode(kind::SEXPR, d_cl, children[0].notNode(), res);
+
+ return addAletheStep(
+ AletheRule::IMPLIES, vp1, vp1, {children[1]}, {}, *cdp)
+ && addAletheStep(AletheRule::RESOLUTION,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ {vp1, children[0]},
+ {},
+ *cdp);
+ }
+ // ======== Double negation elimination
+ // See proof_rule.h for documentation on the NOT_NOT_ELIM rule. This comment
+ // uses variable names as introduced there.
+ //
+ // ---------------------------------- NOT_NOT
+ // (VP1:(cl (not (not (not F))) F)) (P:(not (not F)))
+ // ------------------------------------------------------------- RESOLUTION
+ // (cl F)*
+ //
+ // * the corresponding proof node is F
+ case PfRule::NOT_NOT_ELIM:
+ {
+ Node vp1 = nm->mkNode(kind::SEXPR, d_cl, children[0].notNode(), res);
+
+ return addAletheStep(AletheRule::NOT_NOT, vp1, vp1, {}, {}, *cdp)
+ && addAletheStep(AletheRule::RESOLUTION,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ {vp1, children[0]},
+ {},
+ *cdp);
+ }
+ // ======== Contradiction
+ // See proof_rule.h for documentation on the CONTRA rule. This
+ // comment uses variable names as introduced there.
+ //
+ // P1 P2
+ // --------- RESOLUTION
+ // (cl)*
+ //
+ // * the corresponding proof node is false
+ case PfRule::CONTRA:
+ {
+ return addAletheStep(AletheRule::RESOLUTION,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl),
+ children,
+ {},
+ *cdp);
+ }
+ // ======== And elimination
+ // This rule is translated according to the singleton pattern.
+ case PfRule::AND_ELIM:
+ {
+ return addAletheStep(AletheRule::AND,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ children,
+ {},
+ *cdp);
+ }
+ // ======== And introduction
+ // See proof_rule.h for documentation on the AND_INTRO rule. This
+ // comment uses variable names as introduced there.
+ //
+ //
+ // ----- AND_NEG
+ // VP1 P1 ... Pn
+ // -------------------------- RESOLUTION
+ // (cl (and F1 ... Fn))*
+ //
+ // VP1:(cl (and F1 ... Fn) (not F1) ... (not Fn))
+ //
+ // * the corresponding proof node is (and F1 ... Fn)
+ case PfRule::AND_INTRO:
+ {
+ std::vector<Node> neg_Nodes = {d_cl,res};
+ for (size_t i = 0, size = children.size(); i < size; i++)
+ {
+ neg_Nodes.push_back(children[i].notNode());
+ }
+ Node vp1 = nm->mkNode(kind::SEXPR, neg_Nodes);
+
+ std::vector<Node> new_children = {vp1};
+ new_children.insert(new_children.end(), children.begin(), children.end());
+
+ return addAletheStep(AletheRule::AND_NEG, vp1, vp1, {}, {}, *cdp)
+ && addAletheStep(AletheRule::RESOLUTION,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ new_children,
+ {},
+ *cdp);
+ }
+ // ======== Not Or elimination
+ // This rule is translated according to the singleton pattern.
+ case PfRule::NOT_OR_ELIM:
+ {
+ return addAletheStep(AletheRule::NOT_OR,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ children,
+ {},
+ *cdp);
+ }
+ // ======== Implication elimination
+ // This rule is translated according to the clause pattern.
+ case PfRule::IMPLIES_ELIM:
+ {
+ return addAletheStepFromOr(AletheRule::IMPLIES, res, children, {}, *cdp);
+ }
+ // ======== Not Implication elimination version 1
+ // This rule is translated according to the singleton pattern.
+ case PfRule::NOT_IMPLIES_ELIM1:
+ {
+ return addAletheStep(AletheRule::NOT_IMPLIES1,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ children,
+ {},
+ *cdp);
+ }
+ // ======== Not Implication elimination version 2
+ // This rule is translated according to the singleton pattern.
+ case PfRule::NOT_IMPLIES_ELIM2:
+ {
+ return addAletheStep(AletheRule::NOT_IMPLIES2,
+ res,
+ nm->mkNode(kind::SEXPR, d_cl, res),
+ children,
+ {},
+ *cdp);
+ }
+ // ======== Various elimination rules
+ // The following rules are all translated according to the clause pattern.
+ case PfRule::EQUIV_ELIM1:
+ {
+ return addAletheStepFromOr(AletheRule::EQUIV1, res, children, {}, *cdp);
+ }
+ case PfRule::EQUIV_ELIM2:
+ {
+ return addAletheStepFromOr(AletheRule::EQUIV2, res, children, {}, *cdp);
+ }
+ case PfRule::NOT_EQUIV_ELIM1:
+ {
+ return addAletheStepFromOr(
+ AletheRule::NOT_EQUIV1, res, children, {}, *cdp);
+ }
+ case PfRule::NOT_EQUIV_ELIM2:
+ {
+ return addAletheStepFromOr(
+ AletheRule::NOT_EQUIV2, res, children, {}, *cdp);
+ }
+ case PfRule::XOR_ELIM1:
+ {
+ return addAletheStepFromOr(AletheRule::XOR1, res, children, {}, *cdp);
+ }
+ case PfRule::XOR_ELIM2:
+ {
+ return addAletheStepFromOr(AletheRule::XOR2, res, children, {}, *cdp);
+ }
+ case PfRule::NOT_XOR_ELIM1:
+ {
+ return addAletheStepFromOr(AletheRule::NOT_XOR1, res, children, {}, *cdp);
+ }
+ case PfRule::NOT_XOR_ELIM2:
+ {
+ return addAletheStepFromOr(AletheRule::NOT_XOR2, res, children, {}, *cdp);
+ }
+ case PfRule::ITE_ELIM1:
+ {
+ return addAletheStepFromOr(AletheRule::ITE2, res, children, {}, *cdp);
+ }
+ case PfRule::ITE_ELIM2:
+ {
+ return addAletheStepFromOr(AletheRule::ITE1, res, children, {}, *cdp);
+ }
+ case PfRule::NOT_ITE_ELIM1:
+ {
+ return addAletheStepFromOr(AletheRule::NOT_ITE2, res, children, {}, *cdp);
+ }
+ case PfRule::NOT_ITE_ELIM2:
+ {
+ return addAletheStepFromOr(AletheRule::NOT_ITE1, res, children, {}, *cdp);
+ }
+ //================================================= De Morgan rules
+ // ======== Not And
+ // This rule is translated according to the clause pattern.
+ case PfRule::NOT_AND:
+ {
+ return addAletheStepFromOr(AletheRule::NOT_AND, res, children, {}, *cdp);
+ }
+
+ //================================================= CNF rules
+ // The following rules are all translated according to the clause pattern.
+ case PfRule::CNF_AND_POS:
+ {
+ return addAletheStepFromOr(AletheRule::AND_POS, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_AND_NEG:
+ {
+ return addAletheStepFromOr(AletheRule::AND_NEG, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_OR_POS:
+ {
+ return addAletheStepFromOr(AletheRule::OR_POS, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_OR_NEG:
+ {
+ return addAletheStepFromOr(AletheRule::OR_NEG, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_IMPLIES_POS:
+ {
+ return addAletheStepFromOr(
+ AletheRule::IMPLIES_POS, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_IMPLIES_NEG1:
+ {
+ return addAletheStepFromOr(
+ AletheRule::IMPLIES_NEG1, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_IMPLIES_NEG2:
+ {
+ return addAletheStepFromOr(
+ AletheRule::IMPLIES_NEG2, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_EQUIV_POS1:
+ {
+ return addAletheStepFromOr(
+ AletheRule::EQUIV_POS2, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_EQUIV_POS2:
+ {
+ return addAletheStepFromOr(
+ AletheRule::EQUIV_POS1, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_EQUIV_NEG1:
+ {
+ return addAletheStepFromOr(
+ AletheRule::EQUIV_NEG2, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_EQUIV_NEG2:
+ {
+ return addAletheStepFromOr(
+ AletheRule::EQUIV_NEG1, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_XOR_POS1:
+ {
+ return addAletheStepFromOr(AletheRule::XOR_POS1, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_XOR_POS2:
+ {
+ return addAletheStepFromOr(AletheRule::XOR_POS2, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_XOR_NEG1:
+ {
+ return addAletheStepFromOr(AletheRule::XOR_NEG2, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_XOR_NEG2:
+ {
+ return addAletheStepFromOr(AletheRule::XOR_NEG1, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_ITE_POS1:
+ {
+ return addAletheStepFromOr(AletheRule::ITE_POS2, res, children, {}, *cdp);
+ }
+ case PfRule::CNF_ITE_POS2:
+ {
+ return addAletheStepFromOr(AletheRule::ITE_POS1, res, children, {}, *cdp);
+ }
default:
{
return addAletheStep(AletheRule::UNDEFINED,
diff --git a/src/proof/lazy_proof.cpp b/src/proof/lazy_proof.cpp
index eab452d49..f6c9d8eb2 100644
--- a/src/proof/lazy_proof.cpp
+++ b/src/proof/lazy_proof.cpp
@@ -26,8 +26,11 @@ namespace cvc5 {
LazyCDProof::LazyCDProof(ProofNodeManager* pnm,
ProofGenerator* dpg,
context::Context* c,
- const std::string& name)
- : CDProof(pnm, c, name), d_gens(c ? c : &d_context), d_defaultGen(dpg)
+ const std::string& name,
+ bool autoSym)
+ : CDProof(pnm, c, name, autoSym),
+ d_gens(c ? c : &d_context),
+ d_defaultGen(dpg)
{
}
diff --git a/src/proof/lazy_proof.h b/src/proof/lazy_proof.h
index bf5bc229c..e14dc8a1c 100644
--- a/src/proof/lazy_proof.h
+++ b/src/proof/lazy_proof.h
@@ -41,11 +41,15 @@ class LazyCDProof : public CDProof
* 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.
+ * @param name The name of this proof generator (for debugging)
+ * @param autoSym Whether symmetry steps are automatically added when adding
+ * steps to this proof
*/
LazyCDProof(ProofNodeManager* pnm,
ProofGenerator* dpg = nullptr,
context::Context* c = nullptr,
- const std::string& name = "LazyCDProof");
+ const std::string& name = "LazyCDProof",
+ bool autoSym = true);
~LazyCDProof();
/**
* Get lazy proof for fact, or nullptr if it does not exist. This may
diff --git a/src/proof/proof_node_algorithm.cpp b/src/proof/proof_node_algorithm.cpp
index 5f56d785e..ce8ca55c3 100644
--- a/src/proof/proof_node_algorithm.cpp
+++ b/src/proof/proof_node_algorithm.cpp
@@ -237,5 +237,93 @@ bool containsSubproof(ProofNode* pn,
return false;
}
+bool isSingletonClause(TNode res,
+ const std::vector<Node>& children,
+ const std::vector<Node>& args)
+{
+ if (res.getKind() != kind::OR)
+ {
+ return true;
+ }
+ size_t i;
+ Node trueNode = NodeManager::currentNM()->mkConst(true);
+ // Find out the last child to introduced res, if any. We only need to
+ // look at the last one because any previous introduction would have
+ // been eliminated.
+ //
+ // After the loop finishes i is the index of the child C_i that
+ // introduced res. If i=0 none of the children introduced res as a
+ // subterm and therefore it cannot be a singleton clause.
+ for (i = children.size(); i > 0; --i)
+ {
+ // only non-singleton clauses may be introducing
+ // res, so we only care about non-singleton or nodes. We check then
+ // against the kind and whether the whole or node occurs as a pivot of
+ // the respective resolution
+ if (children[i - 1].getKind() != kind::OR)
+ {
+ continue;
+ }
+ size_t pivotIndex = (i != 1) ? 2 * (i - 1) - 1 : 1;
+ if (args[pivotIndex] == children[i - 1]
+ || args[pivotIndex].notNode() == children[i - 1])
+ {
+ continue;
+ }
+ // if res occurs as a subterm of a non-singleton premise
+ if (std::find(children[i - 1].begin(), children[i - 1].end(), res)
+ != children[i - 1].end())
+ {
+ break;
+ }
+ }
+
+ // If res is a subterm of one of the children we still need to check if
+ // that subterm is eliminated
+ if (i > 0)
+ {
+ bool posFirst = (i == 1) ? (args[0] == trueNode)
+ : (args[(2 * (i - 1)) - 2] == trueNode);
+ Node pivot = (i == 1) ? args[1] : args[(2 * (i - 1)) - 1];
+
+ // Check if it is eliminated by the previous resolution step
+ if ((res == pivot && !posFirst) || (res.notNode() == pivot && posFirst)
+ || (pivot.notNode() == res && posFirst))
+ {
+ // We decrease i by one, since it could have been the case that i
+ // was equal to children.size(), so that we return false in the end
+ --i;
+ }
+ else
+ {
+ // Otherwise check if any subsequent premise eliminates it
+ for (; i < children.size(); ++i)
+ {
+ posFirst = args[(2 * i) - 2] == trueNode;
+ pivot = args[(2 * i) - 1];
+ // To eliminate res, the clause must contain it with opposite
+ // polarity. There are three successful cases, according to the
+ // pivot and its sign
+ //
+ // - res is the same as the pivot and posFirst is true, which
+ // means that the clause contains its negation and eliminates it
+ //
+ // - res is the negation of the pivot and posFirst is false, so
+ // the clause contains the node whose negation is res. Note that
+ // this case may either be res.notNode() == pivot or res ==
+ // pivot.notNode().
+ if ((res == pivot && posFirst) || (res.notNode() == pivot && !posFirst)
+ || (pivot.notNode() == res && !posFirst))
+ {
+ break;
+ }
+ }
+ }
+ }
+ // if not eliminated (loop went to the end), then it's a singleton
+ // clause
+ return i == children.size();
+}
+
} // namespace expr
} // namespace cvc5
diff --git a/src/proof/proof_node_algorithm.h b/src/proof/proof_node_algorithm.h
index f35a84c87..b2e20c478 100644
--- a/src/proof/proof_node_algorithm.h
+++ b/src/proof/proof_node_algorithm.h
@@ -91,6 +91,34 @@ bool containsSubproof(ProofNode* pn,
ProofNode* pnc,
std::unordered_set<const ProofNode*>& visited);
+/** Whether the result of a resolution corresponds to a singleton clause
+ *
+ * Viewing a node as a clause (i.e., as a list of literals), whether a node of
+ * the form (or t1 ... tn) corresponds to the clause [t1, ..., tn]) or to the
+ * clause [(or t1 ... tn)] can be ambiguous in different settings.
+ *
+ * This method determines whether a node `res`, corresponding to the result of a
+ * resolution inference with premises `children` and arguments `args` (see
+ * proof_rule.h for more details on the inference), is a singleton clause (i.e.,
+ * a clause with a single literal).
+ *
+ * It does so relying on the fact that `res` is only a singleton if it occurs as
+ * a child in one of the premises and is not eliminated afterwards. So we search
+ * for `res` as a subterm of some child, which would mark its last insertion
+ * into the resolution result. If `res` does not occur as the pivot to be
+ * eliminated in a subsequent premise, then, and only then, it is a singleton
+ * clause.
+ *
+ * @param res the result of a resolution inference
+ * @param children the premises for the resolution inference
+ * @param args the arguments, i.e., the pivots and their polarities, for the
+ * resolution inference
+ * @return whether `res` is a singleton clause
+ */
+bool isSingletonClause(TNode res,
+ const std::vector<Node>& children,
+ const std::vector<Node>& args);
+
} // namespace expr
} // namespace cvc5
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback