Move datatype split inference scheme to its own method (#8664)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Thu, 28 Apr 2022 16:38:28 +0000 (11:38 -0500)
committerGitHub <noreply@github.com>
Thu, 28 Apr 2022 16:38:28 +0000 (11:38 -0500)
src/theory/datatypes/theory_datatypes.cpp
src/theory/datatypes/theory_datatypes.h

index c5ebd0cef017513e85533a2acfe28ec6347f3783..5824b46cfcfe036882ec0388119728b944d9a4db 100644 (file)
@@ -210,195 +210,8 @@ void TheoryDatatypes::postCheck(Effort level)
     Trace("datatypes-debug") << "Check for splits " << endl;
     do {
       d_im.reset();
-      // get the relevant term set, currently all datatype equivalence classes
-      // in the equality engine
-      std::set<Node> termSetReps;
-      eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
-      while (!eqcs_i.isFinished())
-      {
-        Node eqc = (*eqcs_i);
-        ++eqcs_i;
-        if (eqc.getType().isDatatype())
-        {
-          termSetReps.insert(eqc);
-        }
-      }
-      std::map<TypeNode, Node> rec_singletons;
-      for (const Node& n : termSetReps)
-      {
-        Trace("datatypes-debug")
-            << "Process equivalence class " << n << std::endl;
-        EqcInfo* eqc = getOrMakeEqcInfo(n);
-        // if there are more than 1 possible constructors for eqc
-        if (hasLabel(eqc, n))
-        {
-          Trace("datatypes-debug")
-              << "Has constructor " << eqc->d_constructor.get() << std::endl;
-          continue;
-        }
-        Trace("datatypes-debug") << "No constructor..." << std::endl;
-        TypeNode tn = n.getType();
-        const DType& dt = tn.getDType();
-        Trace("datatypes-debug") << "Datatype " << dt.getName() << " is "
-                                 << dt.getCardinalityClass(tn) << " "
-                                 << dt.isRecursiveSingleton(tn) << std::endl;
-        if (dt.isRecursiveSingleton(tn))
-        {
-          Trace("datatypes-debug")
-              << "Check recursive singleton..." << std::endl;
-          bool isQuantifiedLogic = logicInfo().isQuantified();
-          // handle recursive singleton case
-          std::map<TypeNode, Node>::iterator itrs = rec_singletons.find(tn);
-          if (itrs != rec_singletons.end())
-          {
-            Node eq = n.eqNode(itrs->second);
-            if (d_singleton_eq.find(eq) == d_singleton_eq.end())
-            {
-              d_singleton_eq[eq] = true;
-              // get assumptions
-              bool success = true;
-              std::vector<Node> assumptions;
-              // if there is at least one uninterpreted sort occurring within
-              // the datatype and the logic is not quantified, add lemmas
-              // ensuring cardinality is more than one,
-              //  do not infer the equality if at least one sort was processed.
-              // otherwise, if the logic is quantified, under the assumption
-              // that all uninterpreted sorts have cardinality one,
-              //  infer the equality.
-              for (size_t i = 0; i < dt.getNumRecursiveSingletonArgTypes(tn);
-                   i++)
-              {
-                TypeNode type = dt.getRecursiveSingletonArgType(tn, i);
-                if (isQuantifiedLogic)
-                {
-                  // under the assumption that the cardinality of this type is
-                  // one
-                  Node a = getSingletonLemma(type, true);
-                  assumptions.push_back(a.negate());
-                }
-                else
-                {
-                  success = false;
-                  // assert that the cardinality of this type is more than one
-                  getSingletonLemma(type, false);
-                }
-              }
-              if (success)
-              {
-                Node assumption = n.eqNode(itrs->second);
-                assumptions.push_back(assumption);
-                Node lemma =
-                    assumptions.size() == 1
-                        ? assumptions[0]
-                        : NodeManager::currentNM()->mkNode(OR, assumptions);
-                Trace("dt-singleton")
-                    << "*************Singleton equality lemma " << lemma
-                    << std::endl;
-                d_im.lemma(lemma, InferenceId::DATATYPES_REC_SINGLETON_EQ);
-              }
-            }
-          }
-          else
-          {
-            rec_singletons[tn] = n;
-          }
-          // do splitting for quantified logics (incomplete anyways)
-          if (!isQuantifiedLogic)
-          {
-            continue;
-          }
-        }
-        Trace("datatypes-debug") << "Get possible cons..." << std::endl;
-        // all other cases
-        std::vector<bool> pcons;
-        getPossibleCons(eqc, n, pcons);
-        // check if we do not need to resolve the constructor type for this
-        // equivalence class.
-        // this is if there are no selectors for this equivalence class, and its
-        // possible values are infinite,
-        //  then do not split.
-        int consIndex = -1;
-        int fconsIndex = -1;
-        bool needSplit = true;
-        for (size_t j = 0, psize = pcons.size(); j < psize; j++)
-        {
-          if (!pcons[j])
-          {
-            continue;
-          }
-          if (consIndex == -1)
-          {
-            consIndex = j;
-          }
-          Trace("datatypes-debug") << j << " compute finite..." << std::endl;
-          // Notice that we split here on all datatypes except the
-          // truly infinite ones. It is possible to also not split
-          // on those that are interpreted-finite when finite model
-          // finding is disabled, but as a heuristic we choose to split
-          // on those too.
-          bool ifin =
-              dt[j].getCardinalityClass(tn) != CardinalityClass::INFINITE;
-          Trace("datatypes-debug") << "...returned " << ifin << std::endl;
-          if (!ifin)
-          {
-            if (!eqc || !eqc->d_selectors)
-            {
-              needSplit = false;
-              break;
-            }
-          }
-          else if (fconsIndex == -1)
-          {
-            fconsIndex = j;
-          }
-        }
-        if (!needSplit)
-        {
-          Trace("dt-split-debug")
-              << "Do not split constructor for " << n << " : " << n.getType()
-              << " " << dt.getNumConstructors() << std::endl;
-          continue;
-        }
-        if (dt.getNumConstructors() == 1)
-        {
-          // this may not be necessary?
-          // if only one constructor, then this term must be this constructor
-          Node t = utils::mkTester(n, 0, dt);
-          d_im.addPendingInference(t, InferenceId::DATATYPES_SPLIT, d_true);
-          Trace("datatypes-infer")
-              << "DtInfer : 1-cons (full) : " << t << std::endl;
-        }
-        else
-        {
-          Assert(consIndex != -1 || dt.isSygus());
-          if (options().datatypes.dtBinarySplit && consIndex != -1)
-          {
-            Node test = utils::mkTester(n, consIndex, dt);
-            Trace("dt-split") << "*************Split for possible constructor "
-                              << dt[consIndex] << " for " << n << endl;
-            test = rewrite(test);
-            NodeBuilder nb(kind::OR);
-            nb << test << test.notNode();
-            Node lemma = nb;
-            d_im.lemma(lemma, InferenceId::DATATYPES_BINARY_SPLIT);
-            d_im.requirePhase(test, true);
-          }
-          else
-          {
-            Trace("dt-split")
-                << "*************Split for constructors on " << n << endl;
-            Node lemma = utils::mkSplit(n, dt);
-            Trace("dt-split-debug")
-                << "Split lemma is : " << lemma << std::endl;
-            d_im.sendDtLemma(
-                lemma, InferenceId::DATATYPES_SPLIT, LemmaProperty::SEND_ATOMS);
-          }
-          if (!options().datatypes.dtBlastSplits)
-          {
-            break;
-          }
-        }
-      }
+      // check for splits
+      checkSplit();
       if (d_im.hasSentLemma())
       {
         // clear pending facts: we added a lemma, so internal inferences are
@@ -1709,6 +1522,192 @@ Node TheoryDatatypes::searchForCycle(TNode n,
   }
 }
 
+void TheoryDatatypes::checkSplit()
+{
+  // get the relevant term set, currently all datatype equivalence classes
+  // in the equality engine
+  std::set<Node> termSetReps;
+  eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine);
+  while (!eqcs_i.isFinished())
+  {
+    Node eqc = (*eqcs_i);
+    ++eqcs_i;
+    if (eqc.getType().isDatatype())
+    {
+      termSetReps.insert(eqc);
+    }
+  }
+  std::map<TypeNode, Node> rec_singletons;
+  for (const Node& n : termSetReps)
+  {
+    Trace("datatypes-debug") << "Process equivalence class " << n << std::endl;
+    EqcInfo* eqc = getOrMakeEqcInfo(n);
+    // if there are more than 1 possible constructors for eqc
+    if (hasLabel(eqc, n))
+    {
+      Trace("datatypes-debug")
+          << "Has constructor " << eqc->d_constructor.get() << std::endl;
+      continue;
+    }
+    Trace("datatypes-debug") << "No constructor..." << std::endl;
+    TypeNode tn = n.getType();
+    const DType& dt = tn.getDType();
+    Trace("datatypes-debug")
+        << "Datatype " << dt.getName() << " is " << dt.getCardinalityClass(tn)
+        << " " << dt.isRecursiveSingleton(tn) << std::endl;
+    if (dt.isRecursiveSingleton(tn))
+    {
+      Trace("datatypes-debug") << "Check recursive singleton..." << std::endl;
+      bool isQuantifiedLogic = logicInfo().isQuantified();
+      // handle recursive singleton case
+      std::map<TypeNode, Node>::iterator itrs = rec_singletons.find(tn);
+      if (itrs != rec_singletons.end())
+      {
+        Node eq = n.eqNode(itrs->second);
+        if (d_singleton_eq.find(eq) == d_singleton_eq.end())
+        {
+          d_singleton_eq[eq] = true;
+          // get assumptions
+          bool success = true;
+          std::vector<Node> assumptions;
+          // if there is at least one uninterpreted sort occurring within the
+          // datatype and the logic is not quantified, add lemmas ensuring
+          // cardinality is more than one,
+          //  do not infer the equality if at least one sort was processed.
+          // otherwise, if the logic is quantified, under the assumption that
+          // all uninterpreted sorts have cardinality one,
+          //  infer the equality.
+          for (size_t i = 0; i < dt.getNumRecursiveSingletonArgTypes(tn); i++)
+          {
+            TypeNode type = dt.getRecursiveSingletonArgType(tn, i);
+            if (isQuantifiedLogic)
+            {
+              // under the assumption that the cardinality of this type is one
+              Node a = getSingletonLemma(type, true);
+              assumptions.push_back(a.negate());
+            }
+            else
+            {
+              success = false;
+              // assert that the cardinality of this type is more than one
+              getSingletonLemma(type, false);
+            }
+          }
+          if (success)
+          {
+            Node assumption = n.eqNode(itrs->second);
+            assumptions.push_back(assumption);
+            Node lemma =
+                assumptions.size() == 1
+                    ? assumptions[0]
+                    : NodeManager::currentNM()->mkNode(OR, assumptions);
+            Trace("dt-singleton") << "*************Singleton equality lemma "
+                                  << lemma << std::endl;
+            d_im.lemma(lemma, InferenceId::DATATYPES_REC_SINGLETON_EQ);
+          }
+        }
+      }
+      else
+      {
+        rec_singletons[tn] = n;
+      }
+      // do splitting for quantified logics (incomplete anyways)
+      if (!isQuantifiedLogic)
+      {
+        continue;
+      }
+    }
+    Trace("datatypes-debug") << "Get possible cons..." << std::endl;
+    // all other cases
+    std::vector<bool> pcons;
+    getPossibleCons(eqc, n, pcons);
+    // check if we do not need to resolve the constructor type for this
+    // equivalence class.
+    // this is if there are no selectors for this equivalence class, and its
+    // possible values are infinite,
+    //  then do not split.
+    int consIndex = -1;
+    int fconsIndex = -1;
+    bool needSplit = true;
+    for (size_t j = 0, psize = pcons.size(); j < psize; j++)
+    {
+      if (!pcons[j])
+      {
+        continue;
+      }
+      if (consIndex == -1)
+      {
+        consIndex = j;
+      }
+      Trace("datatypes-debug") << j << " compute finite..." << std::endl;
+      // Notice that we split here on all datatypes except the
+      // truly infinite ones. It is possible to also not split
+      // on those that are interpreted-finite when finite model
+      // finding is disabled, but as a heuristic we choose to split
+      // on those too.
+      bool ifin = dt[j].getCardinalityClass(tn) != CardinalityClass::INFINITE;
+      Trace("datatypes-debug") << "...returned " << ifin << std::endl;
+      if (!ifin)
+      {
+        if (!eqc || !eqc->d_selectors)
+        {
+          needSplit = false;
+          break;
+        }
+      }
+      else if (fconsIndex == -1)
+      {
+        fconsIndex = j;
+      }
+    }
+    if (!needSplit)
+    {
+      Trace("dt-split-debug")
+          << "Do not split constructor for " << n << " : " << n.getType() << " "
+          << dt.getNumConstructors() << std::endl;
+      continue;
+    }
+    if (dt.getNumConstructors() == 1)
+    {
+      // this may not be necessary?
+      // if only one constructor, then this term must be this constructor
+      Node t = utils::mkTester(n, 0, dt);
+      d_im.addPendingInference(t, InferenceId::DATATYPES_SPLIT, d_true);
+      Trace("datatypes-infer")
+          << "DtInfer : 1-cons (full) : " << t << std::endl;
+    }
+    else
+    {
+      Assert(consIndex != -1 || dt.isSygus());
+      if (options().datatypes.dtBinarySplit && consIndex != -1)
+      {
+        Node test = utils::mkTester(n, consIndex, dt);
+        Trace("dt-split") << "*************Split for possible constructor "
+                          << dt[consIndex] << " for " << n << endl;
+        test = rewrite(test);
+        NodeBuilder nb(kind::OR);
+        nb << test << test.notNode();
+        Node lemma = nb;
+        d_im.lemma(lemma, InferenceId::DATATYPES_BINARY_SPLIT);
+        d_im.requirePhase(test, true);
+      }
+      else
+      {
+        Trace("dt-split") << "*************Split for constructors on " << n
+                          << endl;
+        Node lemma = utils::mkSplit(n, dt);
+        Trace("dt-split-debug") << "Split lemma is : " << lemma << std::endl;
+        d_im.sendDtLemma(
+            lemma, InferenceId::DATATYPES_SPLIT, LemmaProperty::SEND_ATOMS);
+      }
+      if (!options().datatypes.dtBlastSplits)
+      {
+        return;
+      }
+    }
+  }
+}
+
 bool TheoryDatatypes::hasTerm(TNode a) { return d_equalityEngine->hasTerm(a); }
 
 bool TheoryDatatypes::areEqual( TNode a, TNode b ){
index a9072b419f3a6d7a73d0613f2cd841e34d269b94..ed0501cef675558e31a1a736eeeee9df85700c42 100644 (file)
@@ -249,6 +249,14 @@ private:
                       std::map<TNode, bool>& proc,
                       std::vector<Node>& explanation,
                       bool firstTime = true);
+  /**
+   * Applying splitting.
+   *
+   * This checks if we should add a splitting inference for datatype terms
+   * currently in the equality engine. If so, we add pending lemmas on the
+   * inference manager.
+   */
+  void checkSplit();
   /** for checking whether two codatatype terms must be equal */
   void separateBisimilar(std::vector<Node>& part,
                          std::vector<std::vector<Node> >& part_out,