Make isClosedEnumerable a member of TypeNode (#2434)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Fri, 7 Sep 2018 15:42:41 +0000 (10:42 -0500)
committerHaniel Barbosa <hanielbbarbosa@gmail.com>
Fri, 7 Sep 2018 15:42:41 +0000 (10:42 -0500)
src/expr/type_node.cpp
src/expr/type_node.h
src/theory/quantifiers/cegqi/ceg_instantiator.cpp
src/theory/quantifiers/conjecture_generator.cpp
src/theory/quantifiers/first_order_model.cpp
src/theory/quantifiers/instantiate.cpp
src/theory/quantifiers/term_enumeration.cpp
src/theory/quantifiers/term_enumeration.h

index 6616f470ffd1f0f3807bba3f2a438065f902d38c..b5429061240787ca1b92947eb2de1d0fb97b2e54 100644 (file)
@@ -165,6 +165,62 @@ bool TypeNode::isInterpretedFinite()
   return getAttribute(IsInterpretedFiniteAttr());
 }
 
+/** Attribute true for types that are closed enumerable */
+struct IsClosedEnumerableTag
+{
+};
+struct IsClosedEnumerableComputedTag
+{
+};
+typedef expr::Attribute<IsClosedEnumerableTag, bool> IsClosedEnumerableAttr;
+typedef expr::Attribute<IsClosedEnumerableComputedTag, bool>
+    IsClosedEnumerableComputedAttr;
+
+bool TypeNode::isClosedEnumerable()
+{
+  // check it is already cached
+  if (!getAttribute(IsClosedEnumerableComputedAttr()))
+  {
+    bool ret = true;
+    if (isArray() || isSort() || isCodatatype() || isFunction())
+    {
+      ret = false;
+    }
+    else if (isSet())
+    {
+      ret = getSetElementType().isClosedEnumerable();
+    }
+    else if (isDatatype())
+    {
+      // avoid infinite loops: initially set to true
+      setAttribute(IsClosedEnumerableAttr(), ret);
+      setAttribute(IsClosedEnumerableComputedAttr(), true);
+      TypeNode tn = *this;
+      const Datatype& dt = getDatatype();
+      for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+      {
+        for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
+        {
+          TypeNode ctn = TypeNode::fromType(dt[i][j].getRangeType());
+          if (tn != ctn && !ctn.isClosedEnumerable())
+          {
+            ret = false;
+            break;
+          }
+        }
+        if (!ret)
+        {
+          break;
+        }
+      }
+    }
+    setAttribute(IsClosedEnumerableAttr(), ret);
+    setAttribute(IsClosedEnumerableComputedAttr(), true);
+    return ret;
+  }
+  return getAttribute(IsClosedEnumerableAttr());
+}
+
 bool TypeNode::isFirstClass() const {
   return ( getKind() != kind::FUNCTION_TYPE || options::ufHo() ) && 
          getKind() != kind::CONSTRUCTOR_TYPE &&
index 9cb6efc29a56f93306ed8eaf6deb663a32c222e9..5b0caf65907fd5dcdace4f053d4cb023f7ea4d6a 100644 (file)
@@ -429,6 +429,19 @@ public:
    */
   bool isInterpretedFinite();
 
+  /** is closed enumerable type
+   *
+   * This returns true if this type has an enumerator that produces constants
+   * that are fully handled by CVC4's quantifier-free theory solvers. Examples
+   * of types that are not closed enumerable are:
+   * (1) uninterpreted sorts,
+   * (2) arrays,
+   * (3) codatatypes,
+   * (4) functions,
+   * (5) parametric sorts involving any of the above.
+   */
+  bool isClosedEnumerable();
+
   /**
    * Is this a first-class type?
    * First-class types are types for which:
index b90d98ca62c0933cbe85bf2e5b0a6b881803eea7..46649154e1286ee5936ce19a8b33ac8b58c4cd2e 100644 (file)
@@ -29,7 +29,6 @@
 #include "theory/quantifiers/quantifiers_attributes.h"
 #include "theory/quantifiers/quantifiers_rewriter.h"
 #include "theory/quantifiers/term_database.h"
-#include "theory/quantifiers/term_enumeration.h"
 #include "theory/quantifiers/term_util.h"
 #include "theory/theory_engine.h"
 
@@ -1645,7 +1644,7 @@ void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& lems, st
 
 
 Instantiator::Instantiator( QuantifiersEngine * qe, TypeNode tn ) : d_type( tn ){
-  d_closed_enum_type = qe->getTermEnumeration()->isClosedEnumerableType(tn);
+  d_closed_enum_type = tn.isClosedEnumerable();
 }
 
 bool Instantiator::processEqualTerm(CegInstantiator* ci,
index 69f89021b7fb252ed41cc9c27f6d5f0a158823bf..0dc704219515cfd947cf409f973ce1acf8685a8b 100644 (file)
@@ -1088,7 +1088,7 @@ void ConjectureGenerator::getEnumerateUfTerm( Node n, unsigned num, std::vector<
     for( unsigned i=0; i<n.getNumChildren(); i++ ){
       vec.push_back( 0 );
       TypeNode tn = n[i].getType();
-      if (te->isClosedEnumerableType(tn))
+      if (tn.isClosedEnumerable())
       {
         types.push_back( tn );
       }else{
index 1e3116f43f04c56c8b37602a94a42af165d01573..0eec40de2fa98d9379f8509073ee653448276ca3 100644 (file)
@@ -317,7 +317,7 @@ Node FirstOrderModel::getModelBasisTerm(TypeNode tn)
   if (d_model_basis_term.find(tn) == d_model_basis_term.end())
   {
     Node mbt;
-    if (d_qe->getTermEnumeration()->isClosedEnumerableType(tn))
+    if (tn.isClosedEnumerable())
     {
       mbt = d_qe->getTermEnumeration()->getEnumerateTerm(tn, 0);
     }
index eab05f45404dbc9f6333d36033725fbddad57174..24f418b0c206f14d901e67aa9d5cad71797bf2fd 100644 (file)
@@ -491,7 +491,7 @@ bool Instantiate::removeInstantiationInternal(Node q, std::vector<Node>& terms)
 
 Node Instantiate::getTermForType(TypeNode tn)
 {
-  if (d_qe->getTermEnumeration()->isClosedEnumerableType(tn))
+  if (tn.isClosedEnumerable())
   {
     return d_qe->getTermEnumeration()->getEnumerateTerm(tn, 0);
   }
index 34b9d7e81bde5684aaf36803c75f73086595ebf5..8e321976811400c01c477c9e6961710c336874db 100644 (file)
@@ -53,84 +53,38 @@ Node TermEnumeration::getEnumerateTerm(TypeNode tn, unsigned index)
   return d_enum_terms[tn][index];
 }
 
-bool TermEnumeration::isClosedEnumerableType(TypeNode tn)
-{
-  std::unordered_map<TypeNode, bool, TypeNodeHashFunction>::iterator it =
-      d_typ_closed_enum.find(tn);
-  if (it == d_typ_closed_enum.end())
-  {
-    d_typ_closed_enum[tn] = true;
-    bool ret = true;
-    if (tn.isArray() || tn.isSort() || tn.isCodatatype() || tn.isFunction())
-    {
-      ret = false;
-    }
-    else if (tn.isSet())
-    {
-      ret = isClosedEnumerableType(tn.getSetElementType());
-    }
-    else if (tn.isDatatype())
-    {
-      const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
-      for (unsigned i = 0; i < dt.getNumConstructors(); i++)
-      {
-        for (unsigned j = 0; j < dt[i].getNumArgs(); j++)
-        {
-          TypeNode ctn = TypeNode::fromType(dt[i][j].getRangeType());
-          if (tn != ctn && !isClosedEnumerableType(ctn))
-          {
-            ret = false;
-            break;
-          }
-        }
-        if (!ret)
-        {
-          break;
-        }
-      }
-    }
-    
-    // other parametric sorts go here
-    
-    d_typ_closed_enum[tn] = ret;
-    return ret;
-  }
-  else
-  {
-    return it->second;
-  }
-}
-
-// checks whether a type is closed enumerable and is reasonably small enough (<1000)
-// such that all of its domain elements can be enumerated
 bool TermEnumeration::mayComplete(TypeNode tn)
 {
   std::unordered_map<TypeNode, bool, TypeNodeHashFunction>::iterator it =
       d_may_complete.find(tn);
   if (it == d_may_complete.end())
   {
-    bool mc = false;
-    if (isClosedEnumerableType(tn) && tn.isInterpretedFinite())
-    {
-      Cardinality c = tn.getCardinality();
-      if (!c.isLargeFinite())
-      {
-        NodeManager* nm = NodeManager::currentNM();
-        Node card = nm->mkConst(Rational(c.getFiniteCardinality()));
-        // check if less than fixed upper bound, default 1000
-        Node oth = nm->mkConst(Rational(options::fmfTypeCompletionThresh()));
-        Node eq = nm->mkNode(LEQ, card, oth);
-        eq = Rewriter::rewrite(eq);
-        mc = eq.isConst() && eq.getConst<bool>();
-      }
-    }
+    // cache
+    bool mc = mayComplete(tn, options::fmfTypeCompletionThresh());
     d_may_complete[tn] = mc;
     return mc;
   }
-  else
+  return it->second;
+}
+
+bool TermEnumeration::mayComplete(TypeNode tn, unsigned maxCard)
+{
+  bool mc = false;
+  if (tn.isClosedEnumerable() && tn.isInterpretedFinite())
   {
-    return it->second;
+    Cardinality c = tn.getCardinality();
+    if (!c.isLargeFinite())
+    {
+      NodeManager* nm = NodeManager::currentNM();
+      Node card = nm->mkConst(Rational(c.getFiniteCardinality()));
+      // check if less than fixed upper bound
+      Node oth = nm->mkConst(Rational(maxCard));
+      Node eq = nm->mkNode(LEQ, card, oth);
+      eq = Rewriter::rewrite(eq);
+      mc = eq.isConst() && eq.getConst<bool>();
+    }
   }
+  return mc;
 }
 
 } /* CVC4::theory::quantifiers namespace */
index cede77dbe2919c5ebfaef22f1548fe99a523edfa..cf25335f4a644a1679836eb8b1c2cd6318338233 100644 (file)
@@ -42,25 +42,19 @@ class TermEnumeration
   ~TermEnumeration() {}
   /** get i^th term for type tn */
   Node getEnumerateTerm(TypeNode tn, unsigned i);
-  /** is closed enumerable type
-   *
-   * This returns true if this type has an enumerator that produces
-   * constants that are handled by ground theory solvers.
-   * Examples of types that are not closed enumerable are:
-   * (1) uninterpreted sorts,
-   * (2) arrays,
-   * (3) codatatypes,
-   * (4) parametric sorts involving any of the above.
-   */
-  bool isClosedEnumerableType(TypeNode tn);
   /** may complete type
    *
-   * Returns true if the type tn is closed 
-   * enumerable, and is small enough
-   * for finite model finding to enumerate it,
-   * by some heuristic (current cardinality < 1000).
+   * Returns true if the type tn is closed enumerable, is interpreted as a
+   * finite type, and has cardinality less than some reasonable value
+   * (currently < 1000). This method caches the results of whether each type
+   * may be completed.
    */
   bool mayComplete(TypeNode tn);
+  /**
+   * Static version of the above method where maximum cardinality is
+   * configurable.
+   */
+  static bool mayComplete(TypeNode tn, unsigned cardMax);
 
  private:
   /** ground terms enumerated for types */