proof: Add eqrange expansion rule. (#6936)
authorMathias Preiner <mathias.preiner@gmail.com>
Tue, 27 Jul 2021 19:39:53 +0000 (12:39 -0700)
committerGitHub <noreply@github.com>
Tue, 27 Jul 2021 19:39:53 +0000 (19:39 +0000)
Adds proof support for the eqrange operator.

Co-authored-by: Andrew Reynolds <andrew.j.reynolds@gmail.com>
src/proof/proof_rule.cpp
src/proof/proof_rule.h
src/theory/arrays/proof_checker.cpp
src/theory/arrays/skolem_cache.cpp
src/theory/arrays/skolem_cache.h
src/theory/arrays/theory_arrays.cpp
src/theory/arrays/theory_arrays_rewriter.cpp
src/theory/arrays/theory_arrays_rewriter.h

index 3991305ca128a918b8232e862f77a704ce986534..7e1cdf8d34b1436846d54738d74ecfd3e49614e8 100644 (file)
@@ -120,6 +120,7 @@ const char* toString(PfRule id)
     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";
index 173e4df9abda59aecc0e0c72af1dcf01ab8e210f..78e773bdc4d1f492b1449d69bd4c6850b33cd7fb 100644 (file)
@@ -713,6 +713,15 @@ enum class PfRule : uint32_t
   // 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)
index f8c5b2669069c8bafbca482757b429b8d2873312..6d546d7461306c3a8796d700a38b293a47f167a9 100644 (file)
  */
 
 #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 {
@@ -28,6 +30,7 @@ void ArraysProofRuleChecker::registerTo(ProofChecker* pc)
   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);
 }
@@ -103,6 +106,11 @@ Node ArraysProofRuleChecker::checkInternal(PfRule id,
     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
index 19ab1aaae18c959160fda11c9878ab862417cc87..7cf192b7f9435c2e3c85cf4f9061fea516b55e8c 100644 (file)
@@ -35,6 +35,14 @@ struct ExtIndexVarAttributeId
 };
 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)
@@ -66,6 +74,13 @@ 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];
index 12578c01f1d409005643dc28afe1099d2d10bc21..17a5c6975b3f537c8e9bac1d3293d681a832c491 100644 (file)
@@ -43,6 +43,13 @@ class SkolemCache
    */
   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
index be8e1a08e76af8312df000cd8b84bf21c6529126..8fa7e0fd38cd6e30b5299b3022cfc4c6f293c81a 100644 (file)
@@ -85,6 +85,7 @@ TheoryArrays::TheoryArrays(context::Context* c,
           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),
index 1072ffaf4bb846ed08244c0e5f0f5f46293a2df9..dd7a56d3324543ee13a4b17e1b39dbc6dd67465e 100644 (file)
@@ -20,6 +20,9 @@
 
 #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 {
@@ -48,6 +51,11 @@ void setMostFrequentValueCount(TNode store, uint64_t count) {
   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());
@@ -271,6 +279,48 @@ Node TheoryArraysRewriter::normalizeConstant(TNode node, Cardinality indexCard)
   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")
@@ -610,57 +660,22 @@ RewriteResponse TheoryArraysRewriter::preRewrite(TNode node)
 
 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();
 }
 
index 498266ce3376310c093e13c12b11b981679f19b2..95a19004e03f7658feac9bfbbc40f782ac6b6a70 100644 (file)
@@ -29,6 +29,9 @@
 #include "theory/type_enumerator.h"
 
 namespace cvc5 {
+
+class EagerProofGenerator;
+
 namespace theory {
 namespace arrays {
 
@@ -43,16 +46,18 @@ static inline Node mkEqNode(Node a, Node b) {
 
 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;
@@ -62,6 +67,14 @@ class TheoryArraysRewriter : public TheoryRewriter
   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