Use codatatype bound variables for codatatype values (#7425)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Wed, 20 Oct 2021 16:49:53 +0000 (11:49 -0500)
committerGitHub <noreply@github.com>
Wed, 20 Oct 2021 16:49:53 +0000 (16:49 +0000)
Previously, we had conflated codatatype bound variables and uninterpreted constants (as they have the same functionality). However, we should separate these concepts so we can ensure that uninterpreted constants are only used for uninterpreted sorts.

13 files changed:
src/expr/CMakeLists.txt
src/expr/codatatype_bound_variable.cpp [new file with mode: 0644]
src/expr/codatatype_bound_variable.h [new file with mode: 0644]
src/expr/uninterpreted_constant.cpp
src/theory/datatypes/datatypes_rewriter.cpp
src/theory/datatypes/kinds
src/theory/datatypes/theory_datatypes.cpp
src/theory/datatypes/theory_datatypes_type_rules.cpp
src/theory/datatypes/theory_datatypes_type_rules.h
src/theory/datatypes/type_enumerator.cpp
src/theory/theory_model_builder.cpp
test/python/unit/api/test_solver.py
test/unit/api/solver_black.cpp

index e73960676591d3091912ab6f89e7a7a1e45529ec..46750086856a0ea5a16a3c8232b5dc4387689b6d 100644 (file)
@@ -26,6 +26,8 @@ libcvc5_add_sources(
   bound_var_manager.h
   cardinality_constraint.cpp
   cardinality_constraint.h
+  codatatype_bound_variable.cpp
+  codatatype_bound_variable.h
   emptyset.cpp
   emptyset.h
   emptybag.cpp
diff --git a/src/expr/codatatype_bound_variable.cpp b/src/expr/codatatype_bound_variable.cpp
new file mode 100644 (file)
index 0000000..a6a9d8e
--- /dev/null
@@ -0,0 +1,113 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Representation of bound variables in codatatype values
+ */
+
+#include "expr/codatatype_bound_variable.h"
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "base/check.h"
+#include "expr/type_node.h"
+
+using namespace std;
+
+namespace cvc5 {
+
+CodatatypeBoundVariable::CodatatypeBoundVariable(const TypeNode& type,
+                                                 Integer index)
+    : d_type(new TypeNode(type)), d_index(index)
+{
+  PrettyCheckArgument(type.isCodatatype(),
+                      type,
+                      "codatatype bound variables can only be created for "
+                      "codatatype sorts, not `%s'",
+                      type.toString().c_str());
+  PrettyCheckArgument(
+      index >= 0,
+      index,
+      "index >= 0 required for codatatype bound variable index, not `%s'",
+      index.toString().c_str());
+}
+
+CodatatypeBoundVariable::~CodatatypeBoundVariable() {}
+
+CodatatypeBoundVariable::CodatatypeBoundVariable(
+    const CodatatypeBoundVariable& other)
+    : d_type(new TypeNode(other.getType())), d_index(other.getIndex())
+{
+}
+
+const TypeNode& CodatatypeBoundVariable::getType() const { return *d_type; }
+const Integer& CodatatypeBoundVariable::getIndex() const { return d_index; }
+bool CodatatypeBoundVariable::operator==(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return getType() == cbv.getType() && d_index == cbv.d_index;
+}
+bool CodatatypeBoundVariable::operator!=(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return !(*this == cbv);
+}
+
+bool CodatatypeBoundVariable::operator<(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return getType() < cbv.getType()
+         || (getType() == cbv.getType() && d_index < cbv.d_index);
+}
+bool CodatatypeBoundVariable::operator<=(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return getType() < cbv.getType()
+         || (getType() == cbv.getType() && d_index <= cbv.d_index);
+}
+bool CodatatypeBoundVariable::operator>(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return !(*this <= cbv);
+}
+bool CodatatypeBoundVariable::operator>=(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return !(*this < cbv);
+}
+
+std::ostream& operator<<(std::ostream& out, const CodatatypeBoundVariable& cbv)
+{
+  std::stringstream ss;
+  ss << cbv.getType();
+  std::string st(ss.str());
+  // must remove delimiting quotes from the name of the type
+  // this prevents us from printing symbols like |@cbv_|T|_n|
+  std::string q("|");
+  size_t pos;
+  while ((pos = st.find(q)) != std::string::npos)
+  {
+    st.replace(pos, 1, "");
+  }
+  return out << "cbv_" << st.c_str() << "_" << cbv.getIndex();
+}
+
+size_t CodatatypeBoundVariableHashFunction::operator()(
+    const CodatatypeBoundVariable& cbv) const
+{
+  return std::hash<TypeNode>()(cbv.getType())
+         * IntegerHashFunction()(cbv.getIndex());
+}
+
+}  // namespace cvc5
diff --git a/src/expr/codatatype_bound_variable.h b/src/expr/codatatype_bound_variable.h
new file mode 100644 (file)
index 0000000..9af8476
--- /dev/null
@@ -0,0 +1,91 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ *   Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
+ * in the top-level source directory and their institutional affiliations.
+ * All rights reserved.  See the file COPYING in the top-level source
+ * directory for licensing information.
+ * ****************************************************************************
+ *
+ * Representation of bound variables in codatatype values
+ */
+
+#include "cvc5_public.h"
+
+#ifndef CVC5__EXPR__CODATATYPE_BOUND_VARIABLE_H
+#define CVC5__EXPR__CODATATYPE_BOUND_VARIABLE_H
+
+#include <iosfwd>
+#include <memory>
+
+#include "util/integer.h"
+
+namespace cvc5 {
+
+class TypeNode;
+
+/**
+ * In theory, codatatype values are represented in using a MU-notation form,
+ * where recursive values may contain free constants indexed by their de Bruijn
+ * indices. This is sometimes written:
+ *    MU x. (cons 0 x).
+ * where the MU binder is label for a term position, which x references.
+ * Instead of constructing an explicit MU binder (which is problematic for
+ * canonicity), we use de Bruijn indices for representing bound variables,
+ * whose index indicates the level in which it is nested. For example, we
+ * represent the above value as:
+ *    (cons 0 @cbv_0)
+ * In the above value, @cbv_0 is a pointer to its direct parent, so the above
+ * value represents the infinite list (cons 0 (cons 0 (cons 0 ... ))).
+ * Another example, the value:
+ *    (cons 0 (cons 1 @cbv_1))
+ * @cbv_1 is pointer to the top most node of this value, so this is value
+ * represents the infinite list (cons 0 (cons 1 (cons 0 (cons 1 ...)))). The
+ * value:
+ *    (cons 0 (cons 1 @cbv_0))
+ * on the other hand represents the lasso:
+ *    (cons 0 (cons 1 (cons 1 (cons 1 ... ))))
+ *
+ * This class is used for representing the indexed bound variables in the above
+ * values. It is considered a constant (isConst is true). However, constants
+ * of codatatype type are normalized by the rewriter (see datatypes rewriter
+ * normalizeCodatatypeConstant) to ensure their canonicity, via a variant
+ * of Hopcroft's algorithm.
+ */
+class CodatatypeBoundVariable
+{
+ public:
+  CodatatypeBoundVariable(const TypeNode& type, Integer index);
+  ~CodatatypeBoundVariable();
+
+  CodatatypeBoundVariable(const CodatatypeBoundVariable& other);
+
+  const TypeNode& getType() const;
+  const Integer& getIndex() const;
+  bool operator==(const CodatatypeBoundVariable& cbv) const;
+  bool operator!=(const CodatatypeBoundVariable& cbv) const;
+  bool operator<(const CodatatypeBoundVariable& cbv) const;
+  bool operator<=(const CodatatypeBoundVariable& cbv) const;
+  bool operator>(const CodatatypeBoundVariable& cbv) const;
+  bool operator>=(const CodatatypeBoundVariable& cbv) const;
+
+ private:
+  std::unique_ptr<TypeNode> d_type;
+  const Integer d_index;
+};
+std::ostream& operator<<(std::ostream& out, const CodatatypeBoundVariable& cbv);
+
+/**
+ * Hash function for codatatype bound variables.
+ */
+struct CodatatypeBoundVariableHashFunction
+{
+  size_t operator()(const CodatatypeBoundVariable& cbv) const;
+};
+
+}  // namespace cvc5
+
+#endif /* CVC5__UNINTERPRETED_CONSTANT_H */
index ef354568da346ea45cff03edbc8d892779f2cfc4..709ec112f50ec5f11284801bebb55a59ae7b4bf0 100644 (file)
@@ -31,7 +31,11 @@ UninterpretedConstant::UninterpretedConstant(const TypeNode& type,
                                              Integer index)
     : d_type(new TypeNode(type)), d_index(index)
 {
-  //PrettyCheckArgument(type.isSort(), type, "uninterpreted constants can only be created for uninterpreted sorts, not `%s'", type.toString().c_str());
+  PrettyCheckArgument(type.isSort(),
+                      type,
+                      "uninterpreted constants can only be created for "
+                      "uninterpreted sorts, not `%s'",
+                      type.toString().c_str());
   PrettyCheckArgument(index >= 0, index, "index >= 0 required for uninterpreted constant index, not `%s'", index.toString().c_str());
 }
 
index c446504fd8ac0a3f34bf1d336df4eefca0c9642a..fd957baaaf82b7350b3472cae45d9259ca85fd42 100644 (file)
 #include "theory/datatypes/datatypes_rewriter.h"
 
 #include "expr/ascription_type.h"
+#include "expr/codatatype_bound_variable.h"
 #include "expr/dtype.h"
 #include "expr/dtype_cons.h"
 #include "expr/node_algorithm.h"
 #include "expr/skolem_manager.h"
 #include "expr/sygus_datatype.h"
-#include "expr/uninterpreted_constant.h"
 #include "options/datatypes_options.h"
 #include "theory/datatypes/sygus_datatype_utils.h"
 #include "theory/datatypes/theory_datatypes_utils.h"
@@ -729,7 +729,7 @@ Node DatatypesRewriter::collectRef(Node n,
       else
       {
         // a loop
-        const Integer& i = n.getConst<UninterpretedConstant>().getIndex();
+        const Integer& i = n.getConst<CodatatypeBoundVariable>().getIndex();
         uint32_t index = i.toUnsignedInt();
         if (index >= sk.size())
         {
@@ -771,7 +771,7 @@ Node DatatypesRewriter::normalizeCodatatypeConstantEqc(
     {
       int debruijn = depth - it->second - 1;
       return NodeManager::currentNM()->mkConst(
-          UninterpretedConstant(n.getType(), debruijn));
+          CodatatypeBoundVariable(n.getType(), debruijn));
     }
     std::vector<Node> children;
     bool childChanged = false;
@@ -798,10 +798,10 @@ Node DatatypesRewriter::replaceDebruijn(Node n,
                                         TypeNode orig_tn,
                                         unsigned depth)
 {
-  if (n.getKind() == kind::UNINTERPRETED_CONSTANT && n.getType() == orig_tn)
+  if (n.getKind() == kind::CODATATYPE_BOUND_VARIABLE && n.getType() == orig_tn)
   {
     unsigned index =
-        n.getConst<UninterpretedConstant>().getIndex().toUnsignedInt();
+        n.getConst<CodatatypeBoundVariable>().getIndex().toUnsignedInt();
     if (index == depth)
     {
       return orig;
index 41d5ded7695c933957eddba8d18e81842a963bc6..cb3a78cf275bda9cce64e367362d6a61f3ba2ef1 100644 (file)
@@ -145,4 +145,13 @@ parameterized TUPLE_PROJECT TUPLE_PROJECT_OP 1 \
 typerule TUPLE_PROJECT_OP  "SimpleTypeRule<RBuiltinOperator>"
 typerule TUPLE_PROJECT     ::cvc5::theory::datatypes::TupleProjectTypeRule
 
+# For representing codatatype values
+constant CODATATYPE_BOUND_VARIABLE \
+    class \
+    CodatatypeBoundVariable \
+    ::cvc5::CodatatypeBoundVariableHashFunction \
+    "expr/codatatype_bound_variable.h" \
+    "the kind of expressions representing bound variables in codatatype constants, which are de Bruijn indexed variables; payload is an instance of the cvc5::CodatatypeBoundVariable class (used in models)"
+typerule CODATATYPE_BOUND_VARIABLE ::cvc5::theory::datatypes::CodatatypeBoundVariableTypeRule
+
 endtheory
index c892ffc118a05906b04492e60ca8d04e80f6529a..0cbeaa515c202a76bd0cf31972eaa4b7fc3c308c 100644 (file)
 #include <sstream>
 
 #include "base/check.h"
+#include "expr/codatatype_bound_variable.h"
 #include "expr/dtype.h"
 #include "expr/dtype_cons.h"
 #include "expr/kind.h"
 #include "expr/skolem_manager.h"
-#include "expr/uninterpreted_constant.h"
 #include "options/datatypes_options.h"
 #include "options/quantifiers_options.h"
 #include "options/smt_options.h"
@@ -1290,10 +1290,10 @@ bool TheoryDatatypes::collectModelValues(TheoryModel* m,
 
 Node TheoryDatatypes::getCodatatypesValue( Node n, std::map< Node, Node >& eqc_cons, std::map< Node, int >& vmap, int depth ){
   std::map< Node, int >::iterator itv = vmap.find( n );
+  NodeManager* nm = NodeManager::currentNM();
   if( itv!=vmap.end() ){
     int debruijn = depth - 1 - itv->second;
-    return NodeManager::currentNM()->mkConst(
-        UninterpretedConstant(n.getType(), debruijn));
+    return nm->mkConst(CodatatypeBoundVariable(n.getType(), debruijn));
   }else if( n.getType().isDatatype() ){
     Node nc = eqc_cons[n];
     if( !nc.isNull() ){
@@ -1308,7 +1308,7 @@ Node TheoryDatatypes::getCodatatypesValue( Node n, std::map< Node, Node >& eqc_c
         children.push_back( rv );
       }
       vmap.erase( n );
-      return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
+      return nm->mkNode(APPLY_CONSTRUCTOR, children);
     }
   }
   return n;
index 503eaf4df62f40937daf9cf6fdec6febdc0fabc7..86a11357bfbda66f18da4d4a66c6ad85a9020df4 100644 (file)
@@ -18,6 +18,7 @@
 #include <sstream>
 
 #include "expr/ascription_type.h"
+#include "expr/codatatype_bound_variable.h"
 #include "expr/dtype.h"
 #include "expr/dtype_cons.h"
 #include "expr/type_matcher.h"
@@ -597,6 +598,13 @@ TypeNode TupleProjectTypeRule::computeType(NodeManager* nm, TNode n, bool check)
   return nm->mkTupleType(types);
 }
 
+TypeNode CodatatypeBoundVariableTypeRule::computeType(NodeManager* nodeManager,
+                                                      TNode n,
+                                                      bool check)
+{
+  return n.getConst<CodatatypeBoundVariable>().getType();
+}
+
 }  // namespace datatypes
 }  // namespace theory
 }  // namespace cvc5
index cf57a6c0d1f8fe96988b3bd8250268122a390608..7cf98aa747e378ff6c9a59cc9cb664e2527f8c7a 100644 (file)
@@ -99,6 +99,12 @@ class TupleProjectTypeRule
   static TypeNode computeType(NodeManager* nm, TNode n, bool check);
 };
 
+class CodatatypeBoundVariableTypeRule
+{
+ public:
+  static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
+};
+
 }  // namespace datatypes
 }  // namespace theory
 }  // namespace cvc5
index 39b48ece995936aada48f2d8525ed9b46ce17f4f..6528f1052ac83baa544433db42ccba6a46876e80 100644 (file)
@@ -16,6 +16,7 @@
 #include "theory/datatypes/type_enumerator.h"
 
 #include "expr/ascription_type.h"
+#include "expr/codatatype_bound_variable.h"
 #include "expr/dtype_cons.h"
 #include "theory/datatypes/datatypes_rewriter.h"
 #include "theory/datatypes/theory_datatypes_utils.h"
@@ -108,8 +109,8 @@ Node DatatypesEnumerator::getTermEnum( TypeNode tn, unsigned i ){
    {
      if (d_child_enum)
      {
-       ret = NodeManager::currentNM()->mkConst(
-           UninterpretedConstant(d_type, d_size_limit));
+       NodeManager* nm = NodeManager::currentNM();
+       ret = nm->mkConst(CodatatypeBoundVariable(d_type, d_size_limit));
      }
      else
      {
index 33dbe9ffad5dbad66b71468575852d3ed8466049..1a0549a0a1990444e6852c2dc4908a27e9e3c46a 100644 (file)
@@ -224,7 +224,7 @@ bool TheoryEngineModelBuilder::isExcludedCdtValue(
     {
       Trace("model-builder-debug") << "  ...matches with " << eqc << " -> "
                                    << eqc_m << std::endl;
-      if (eqc_m.getKind() == kind::UNINTERPRETED_CONSTANT)
+      if (eqc_m.getKind() == kind::CODATATYPE_BOUND_VARIABLE)
       {
         Trace("model-builder-debug") << "*** " << val
                                      << " is excluded datatype for " << eqc
index db49f8beafe43f35e1d87bd0f26a8632705be973..29d637dc6060ae51d906903af221f1af3e8a309f 100644 (file)
@@ -381,15 +381,6 @@ def test_mk_rounding_mode(solver):
     solver.mkRoundingMode(pycvc5.RoundTowardZero)
 
 
-def test_mk_uninterpreted_const(solver):
-    solver.mkUninterpretedConst(solver.getBooleanSort(), 1)
-    with pytest.raises(RuntimeError):
-        solver.mkUninterpretedConst(pycvc5.Sort(solver), 1)
-    slv = pycvc5.Solver()
-    with pytest.raises(RuntimeError):
-        slv.mkUninterpretedConst(solver.getBooleanSort(), 1)
-
-
 def test_mk_abstract_value(solver):
     solver.mkAbstractValue("1")
     with pytest.raises(ValueError):
index 19113ae139ff9c5baa7ebb4dfb8909b9d65eea59..f90018101a2165839ae326714b9d40c31b60165d 100644 (file)
@@ -376,15 +376,6 @@ TEST_F(TestApiBlackSolver, mkRoundingMode)
   ASSERT_NO_THROW(d_solver.mkRoundingMode(RoundingMode::ROUND_TOWARD_ZERO));
 }
 
-TEST_F(TestApiBlackSolver, mkUninterpretedConst)
-{
-  ASSERT_NO_THROW(d_solver.mkUninterpretedConst(d_solver.getBooleanSort(), 1));
-  ASSERT_THROW(d_solver.mkUninterpretedConst(Sort(), 1), CVC5ApiException);
-  Solver slv;
-  ASSERT_THROW(slv.mkUninterpretedConst(d_solver.getBooleanSort(), 1),
-               CVC5ApiException);
-}
-
 TEST_F(TestApiBlackSolver, mkAbstractValue)
 {
   ASSERT_NO_THROW(d_solver.mkAbstractValue(std::string("1")));