Always purify universe from set minus (#8201)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 2 Mar 2022 21:55:52 +0000 (15:55 -0600)
committerGitHub <noreply@github.com>
Wed, 2 Mar 2022 21:55:52 +0000 (15:55 -0600)
This fixes a bug in our handling of set cardinality + set universe (also used for set complement).

The bug was caused by using the rewritten form of terms to compute siblings in the cardinality graph. This is problematic for
(set.minus set.universe t) whose sibling was computed to be rewrite( (set.inter set.universe t) ) = t.

To avoid the issue, we now purify the argument of set.minus to avoid this behavior.

Fixes #5400.
Fixes the first benchmark on #5402.

This also eliminates a spurious lemma schema for set universe that was leftover from handling set subtyping.

src/theory/inference_id.cpp
src/theory/inference_id.h
src/theory/sets/cardinality_extension.cpp
src/theory/sets/cardinality_extension.h
src/theory/sets/term_registry.cpp
src/theory/sets/theory_sets_private.cpp
test/regress/CMakeLists.txt
test/regress/regress0/sets/issue5400-2-card-minus-univ.smt2 [new file with mode: 0644]
test/regress/regress0/sets/issue5400-card-minus-univ.smt2 [new file with mode: 0644]
test/regress/regress0/sets/issue5402-1-card.smt2 [new file with mode: 0644]

index 5968a2ff2315b941ab3fddd0049199356f256b46..b5980d781a6da6956f1af42107bc25ad35cf6e5c 100644 (file)
@@ -330,7 +330,6 @@ const char* toString(InferenceId i)
     case InferenceId::SETS_UP_CLOSURE: return "SETS_UP_CLOSURE";
     case InferenceId::SETS_UP_CLOSURE_2: return "SETS_UP_CLOSURE_2";
     case InferenceId::SETS_UP_UNIV: return "SETS_UP_UNIV";
-    case InferenceId::SETS_UNIV_TYPE: return "SETS_UNIV_TYPE";
     case InferenceId::SETS_CARD_SPLIT_EMPTY: return "SETS_CARD_SPLIT_EMPTY";
     case InferenceId::SETS_CARD_CYCLE: return "SETS_CARD_CYCLE";
     case InferenceId::SETS_CARD_EQUAL: return "SETS_CARD_EQUAL";
index 76c330cae8758fc5e4644f418c41913e0835664b..4734e0d607336d196573ea4f4e278cd1f82b7e57 100644 (file)
@@ -477,7 +477,6 @@ enum class InferenceId
   SETS_UP_CLOSURE,
   SETS_UP_CLOSURE_2,
   SETS_UP_UNIV,
-  SETS_UNIV_TYPE,
   //-------------------- sets cardinality solver
   // split on emptyset
   SETS_CARD_SPLIT_EMPTY,
index 7bb752acd0db3b87d065e19fb90aae79854abcc5..5abbaab5cdfd1edeb033d6b2f1ad1898e37a17cc 100644 (file)
@@ -386,10 +386,19 @@ void CardinalityExtension::checkCardCyclesRec(Node eqc,
     {
       continue;
     }
+    // should not have universe as children here, since this is either
+    // rewritten, or eliminated via purification from the first argument of
+    // set minus.
+    Assert(n[0].getKind() != SET_UNIVERSE && n[1].getKind() != SET_UNIVERSE);
     Trace("sets-debug") << "Build cardinality parents for " << n << "..."
                         << std::endl;
     std::vector<Node> sib;
     unsigned true_sib = 0;
+    // Note that we use the rewriter to get the form of the siblings here.
+    // This is required to ensure that the lookups in the equality engine are
+    // accurate. However, it may lead to issues if the rewritten form of a
+    // node leads to unexpected relationships in the graph. To avoid this,
+    // we ensure that universe is not a child of a set in the assertions above.
     if (n.getKind() == SET_INTER)
     {
       d_localBase[n] = n;
@@ -883,7 +892,8 @@ void CardinalityExtension::checkNormalForm(Node eqc,
   }
   if (!success)
   {
-    Assert(d_im.hasSent());
+    Assert(d_im.hasSent())
+        << "failed to send a lemma to resolve why Venn regions are different";
     return;
   }
   // Send to parents (a parent is a set that contains a term in this equivalence
@@ -925,7 +935,9 @@ void CardinalityExtension::checkNormalForm(Node eqc,
       {
         if (std::find(ffpc.begin(), ffpc.end(), nfeqci) == ffpc.end())
         {
-          ffpc.insert(ffpc.end(), nfeqc.begin(), nfeqc.end());
+          Trace("sets-nf-debug") << "Add to flat form " << nfeqci << " to "
+                                 << cbase << " in " << p << std::endl;
+          ffpc.push_back(nfeqci);
         }
         else
         {
index 21d5e6e37171320f196000ce146189252a45d3e2..ac8ebfb663f403973c13a4e732b1828031bcfa07 100644 (file)
@@ -366,8 +366,8 @@ class CardinalityExtension : protected EnvObj
    */
   std::map<Node, std::vector<std::pair<Node, Node>>> d_cardParent;
   /**
-   * Maps equivalence classes + set terms in that equivalence class to their
-   * "flat form" (see checkNormalForms).
+   * Maps equivalence classes + "base" terms of set terms in that equivalence
+   * class to their "flat form" (see checkNormalForms).
    */
   std::map<Node, std::map<Node, std::vector<Node> > > d_ff;
   /** Maps equivalence classes to their "normal form" (see checkNormalForms). */
index d4b5aa824b8eeb86eb762508d83d6b53070d1560..93a700a5f520a3ebf79cc5f5e8c813ff53369ada 100644 (file)
@@ -91,28 +91,6 @@ Node TermRegistry::getUnivSet(TypeNode tn)
   }
   NodeManager* nm = NodeManager::currentNM();
   Node n = nm->mkNullaryOperator(tn, SET_UNIVERSE);
-  for (it = d_univset.begin(); it != d_univset.end(); ++it)
-  {
-    Node n1;
-    Node n2;
-    if (tn.isSubtypeOf(it->first))
-    {
-      n1 = n;
-      n2 = it->second;
-    }
-    else if (it->first.isSubtypeOf(tn))
-    {
-      n1 = it->second;
-      n2 = n;
-    }
-    if (!n1.isNull())
-    {
-      Node ulem = nm->mkNode(SET_SUBSET, n1, n2);
-      Trace("sets-lemma") << "Sets::Lemma : " << ulem << " by univ-type"
-                          << std::endl;
-      d_im.lemma(ulem, InferenceId::SETS_UNIV_TYPE);
-    }
-  }
   d_univset[tn] = n;
   return n;
 }
index a08c051f664d8433e05fab5b8c6b36687848b339..b465c41623ad6f71f10505e98a5faedbd64e3042 100644 (file)
@@ -1320,8 +1320,35 @@ TrustNode TheorySetsPrivate::ppRewrite(Node node,
   {
     case kind::SET_CHOOSE: return expandChooseOperator(node, lems);
     case kind::SET_IS_SINGLETON: return expandIsSingletonOperator(node);
-    default: return TrustNode::null();
+    case kind::SET_MINUS:
+    {
+      if (node[0].getKind() == kind::SET_UNIVERSE)
+      {
+        // Due to complications involving the cardinality graph, we must purify
+        // universe from argument of set minus, so that
+        //   (set.minus set.universe x)
+        // is replaced by
+        //   (set.minus univ x)
+        // along with the lemma (= univ set.universe), where univ is the
+        // purification skolem for set.universe. We require this purification
+        // since the cardinality graph incorrectly thinks that
+        // rewrite( (set.inter set.universe x) ), which evaluates to x, is
+        // a sibling of (set.minus set.universe x).
+        NodeManager* nm = NodeManager::currentNM();
+        SkolemManager* sm = nm->getSkolemManager();
+        Node sk = sm->mkPurifySkolem(node[0], "univ");
+        Trace("ajr-temp") << "PURIFY " << node[0] << " returns " << sk
+                          << std::endl;
+        Node eq = sk.eqNode(node[0]);
+        lems.push_back(SkolemLemma(TrustNode::mkTrustLemma(eq), sk));
+        Node ret = nm->mkNode(kind::SET_MINUS, sk, node[1]);
+        return TrustNode::mkTrustRewrite(node, ret, nullptr);
+      }
+    }
+    break;
+    default: break;
   }
+  return TrustNode::null();
 }
 
 TrustNode TheorySetsPrivate::expandChooseOperator(
index 6bafac256015ba575a9481fa39eaf2f51bfa2297..129fbb85b10f8d315f080f4deb40bf6de5e8caec 100644 (file)
@@ -1214,6 +1214,9 @@ set(regress_0_tests
   regress0/sets/insert.smt2
   regress0/sets/int-real-univ-unsat.smt2
   regress0/sets/int-real-univ.smt2
+  regress0/sets/issue5400-card-minus-univ.smt2
+  regress0/sets/issue5400-2-card-minus-univ.smt2
+  regress0/sets/issue5402-1-card.smt2
   regress0/sets/jan24/deepmeas0.hs.fqout.cvc4.47.smt2
   regress0/sets/jan24/deepmeas0.hs.fqout.small.smt2
   regress0/sets/jan27/ListConcat.hs.fqout.177minimized.smt2
diff --git a/test/regress/regress0/sets/issue5400-2-card-minus-univ.smt2 b/test/regress/regress0/sets/issue5400-2-card-minus-univ.smt2
new file mode 100644 (file)
index 0000000..a9ad6ed
--- /dev/null
@@ -0,0 +1,7 @@
+; COMMAND-LINE: --sets-ext
+; EXPECT: sat
+(set-logic ALL)
+(set-info :status sat)
+(declare-fun s () (Set Int))
+(assert (or (set.is_singleton (set.complement s)) (and (= 0 (set.card s)) (= 1 (set.card s)))))
+(check-sat)
diff --git a/test/regress/regress0/sets/issue5400-card-minus-univ.smt2 b/test/regress/regress0/sets/issue5400-card-minus-univ.smt2
new file mode 100644 (file)
index 0000000..4925abf
--- /dev/null
@@ -0,0 +1,10 @@
+; COMMAND-LINE: --sets-ext
+; EXPECT: sat
+(set-logic ALL)
+(set-info :status sat)
+(declare-fun st5 () (Set Int))
+(declare-fun st6 () (Set Int))
+(declare-fun st11 () (Set Int))
+(assert (or (set.is_singleton (set.complement st6)) (>= 10 (set.card st11) 65 0 0)))
+(assert (set.is_singleton st5))
+(check-sat)
diff --git a/test/regress/regress0/sets/issue5402-1-card.smt2 b/test/regress/regress0/sets/issue5402-1-card.smt2
new file mode 100644 (file)
index 0000000..2752bce
--- /dev/null
@@ -0,0 +1,7 @@
+; COMMAND-LINE: --sets-ext
+; EXPECT: sat
+(set-logic ALL)
+(declare-fun s () (Set Int))
+(assert (set.is_singleton (set.complement (set.singleton 0))))
+(assert (= 2 (set.card s)))
+(check-sat)