Adds proof support for the eqrange operator.
Co-authored-by: Andrew Reynolds <andrew.j.reynolds@gmail.com>
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";
+ case PfRule::ARRAYS_EQ_RANGE_EXPAND: return "ARRAYS_EQ_RANGE_EXPAND";
//================================================= Bit-Vector rules
case PfRule::BV_BITBLAST: return "BV_BITBLAST";
case PfRule::BV_BITBLAST_STEP: return "BV_BITBLAST_STEP";
// Conclusion: (not (= (select a k) (select b k)))
// where k is arrays::SkolemCache::getExtIndexSkolem((not (= a b))).
ARRAYS_EXT,
+ // ======== EQ_RANGE expansion
+ // Children: none
+ // Arguments: ((eqrange a b i j))
+ // ----------------------------------------
+ // Conclusion: (=
+ // (eqrange a b i j)
+ // (forall ((x T))
+ // (=> (and (<= i x) (<= x j)) (= (select a x) (select b x)))))
+ ARRAYS_EQ_RANGE_EXPAND,
// ======== Array Trust
// Children: (P1 ... Pn)
// Arguments: (F)
*/
#include "theory/arrays/proof_checker.h"
+
#include "expr/skolem_manager.h"
#include "theory/arrays/skolem_cache.h"
+#include "theory/arrays/theory_arrays_rewriter.h"
#include "theory/rewriter.h"
namespace cvc5 {
pc->registerChecker(PfRule::ARRAYS_READ_OVER_WRITE_CONTRA, this);
pc->registerChecker(PfRule::ARRAYS_READ_OVER_WRITE_1, this);
pc->registerChecker(PfRule::ARRAYS_EXT, this);
+ pc->registerChecker(PfRule::ARRAYS_EQ_RANGE_EXPAND, this);
// trusted rules
pc->registerTrustedChecker(PfRule::ARRAYS_TRUST, this, 2);
}
Node bs = nm->mkNode(kind::SELECT, b, k);
return as.eqNode(bs).notNode();
}
+ if (id == PfRule::ARRAYS_EQ_RANGE_EXPAND)
+ {
+ Node expandedEqRange = TheoryArraysRewriter::expandEqRange(args[0]);
+ return args[0].eqNode(expandedEqRange);
+ }
if (id == PfRule::ARRAYS_TRUST)
{
// "trusted" rules
};
typedef expr::Attribute<ExtIndexVarAttributeId, Node> ExtIndexVarAttribute;
+/**
+ * A bound variable corresponding to the index used in the eqrange expansion.
+ */
+struct EqRangeVarAttributeId
+{
+};
+typedef expr::Attribute<EqRangeVarAttributeId, Node> EqRangeVarAttribute;
+
SkolemCache::SkolemCache() {}
Node SkolemCache::getExtIndexSkolem(Node deq)
"an extensional lemma index variable from the theory of arrays");
}
+Node SkolemCache::getEqRangeVar(TNode eqr)
+{
+ Assert(eqr.getKind() == kind::EQ_RANGE);
+ BoundVarManager* bvm = NodeManager::currentNM()->getBoundVarManager();
+ return bvm->mkBoundVar<EqRangeVarAttribute>(eqr, eqr[2].getType());
+}
+
Node SkolemCache::getExtIndexVar(Node deq)
{
Node a = deq[0][0];
*/
static Node getExtIndexSkolem(Node deq);
+ /**
+ * Get the bound variable for given EQ_RANGE operator. This bound variable
+ * is unique for `eqr`. Calling this method will always return the same bound
+ * variable over the lifetime of `eqr`.
+ */
+ static Node getEqRangeVar(TNode eqr);
+
private:
/**
* Get the bound variable x of the witness term above for disequality deq
name + "number of setModelVal conflicts")),
d_ppEqualityEngine(u, name + "pp", true),
d_ppFacts(u),
+ d_rewriter(pnm),
d_state(c, u, valuation),
d_im(*this, d_state, pnm),
d_literalsToPropagate(c),
#include "expr/array_store_all.h"
#include "expr/attribute.h"
+#include "proof/conv_proof_generator.h"
+#include "proof/eager_proof_generator.h"
+#include "theory/arrays/skolem_cache.h"
#include "util/cardinality.h"
namespace cvc5 {
return store.setAttribute(ArrayConstantMostFrequentValueCountAttr(), count);
}
+TheoryArraysRewriter::TheoryArraysRewriter(ProofNodeManager* pnm)
+ : d_epg(pnm ? new EagerProofGenerator(pnm) : nullptr)
+{
+}
+
Node TheoryArraysRewriter::normalizeConstant(TNode node)
{
return normalizeConstant(node, node[1].getType().getCardinality());
return n;
}
+Node TheoryArraysRewriter::expandEqRange(TNode node)
+{
+ Assert(node.getKind() == kind::EQ_RANGE);
+
+ NodeManager* nm = NodeManager::currentNM();
+ TNode a = node[0];
+ TNode b = node[1];
+ TNode i = node[2];
+ TNode j = node[3];
+ Node k = SkolemCache::getEqRangeVar(node);
+ Node bvl = nm->mkNode(kind::BOUND_VAR_LIST, k);
+ TypeNode type = k.getType();
+
+ Kind kle;
+ Node range;
+ if (type.isBitVector())
+ {
+ kle = kind::BITVECTOR_ULE;
+ }
+ else if (type.isFloatingPoint())
+ {
+ kle = kind::FLOATINGPOINT_LEQ;
+ }
+ else if (type.isInteger() || type.isReal())
+ {
+ kle = kind::LEQ;
+ }
+ else
+ {
+ Unimplemented() << "Type " << type << " is not supported for predicate "
+ << node.getKind();
+ }
+
+ range = nm->mkNode(kind::AND, nm->mkNode(kle, i, k), nm->mkNode(kle, k, j));
+
+ Node eq = nm->mkNode(kind::EQUAL,
+ nm->mkNode(kind::SELECT, a, k),
+ nm->mkNode(kind::SELECT, b, k));
+ Node implies = nm->mkNode(kind::IMPLIES, range, eq);
+ return nm->mkNode(kind::FORALL, bvl, implies);
+}
+
RewriteResponse TheoryArraysRewriter::postRewrite(TNode node)
{
Trace("arrays-postrewrite")
TrustNode TheoryArraysRewriter::expandDefinition(Node node)
{
- NodeManager* nm = NodeManager::currentNM();
Kind kind = node.getKind();
- /* Expand
- *
- * (eqrange a b i j)
- *
- * to
- *
- * forall k . i <= k <= j => a[k] = b[k]
- *
- */
if (kind == kind::EQ_RANGE)
{
- TNode a = node[0];
- TNode b = node[1];
- TNode i = node[2];
- TNode j = node[3];
- Node k = nm->mkBoundVar(i.getType());
- Node bvl = nm->mkNode(kind::BOUND_VAR_LIST, k);
- TypeNode type = k.getType();
-
- Kind kle;
- Node range;
- if (type.isBitVector())
- {
- kle = kind::BITVECTOR_ULE;
- }
- else if (type.isFloatingPoint())
+ Node expandedEqRange = expandEqRange(node);
+ if (d_epg)
{
- kle = kind::FLOATINGPOINT_LEQ;
+ TrustNode tn = d_epg->mkTrustNode(node.eqNode(expandedEqRange),
+ PfRule::ARRAYS_EQ_RANGE_EXPAND,
+ {},
+ {node});
+ return TrustNode::mkTrustRewrite(node, expandedEqRange, d_epg.get());
}
- else if (type.isInteger() || type.isReal())
- {
- kle = kind::LEQ;
- }
- else
- {
- Unimplemented() << "Type " << type << " is not supported for predicate "
- << kind;
- }
-
- range = nm->mkNode(kind::AND, nm->mkNode(kle, i, k), nm->mkNode(kle, k, j));
-
- Node eq = nm->mkNode(kind::EQUAL,
- nm->mkNode(kind::SELECT, a, k),
- nm->mkNode(kind::SELECT, b, k));
- Node implies = nm->mkNode(kind::IMPLIES, range, eq);
- Node ret = nm->mkNode(kind::FORALL, bvl, implies);
- return TrustNode::mkTrustRewrite(node, ret, nullptr);
+ return TrustNode::mkTrustRewrite(node, expandedEqRange, nullptr);
}
+
return TrustNode::null();
}
#include "theory/type_enumerator.h"
namespace cvc5 {
+
+class EagerProofGenerator;
+
namespace theory {
namespace arrays {
class TheoryArraysRewriter : public TheoryRewriter
{
- /**
- * Puts array constant node into normal form. This is so that array constants
- * that are distinct nodes are semantically disequal.
- */
- static Node normalizeConstant(TNode node);
-
public:
+ TheoryArraysRewriter(ProofNodeManager* pnm);
+
/** Normalize a constant whose index type has cardinality indexCard */
static Node normalizeConstant(TNode node, Cardinality indexCard);
+ /* Expands the eqrange predicate (eqrange a b i j) to the quantified formula
+ * (forall ((x T))
+ * (=> (and (<= i x) (<= x j)) (= (select a x) (select b x)))).
+ */
+ static Node expandEqRange(TNode node);
+
RewriteResponse postRewrite(TNode node) override;
RewriteResponse preRewrite(TNode node) override;
static inline void init() {}
static inline void shutdown() {}
+ private:
+ /**
+ * Puts array constant node into normal form. This is so that array constants
+ * that are distinct nodes are semantically disequal.
+ */
+ static Node normalizeConstant(TNode node);
+
+ std::unique_ptr<EagerProofGenerator> d_epg;
}; /* class TheoryArraysRewriter */
} // namespace arrays