Track names for witness terms in model (#8184)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Mon, 28 Feb 2022 21:03:30 +0000 (15:03 -0600)
committerGitHub <noreply@github.com>
Mon, 28 Feb 2022 21:03:30 +0000 (21:03 +0000)
This ensures that enough information is set to allow users to understand models with witness terms.

For example, for input:

(set-logic ALL)
(set-info :status sat)
(declare-fun x () String)
(declare-fun y () String)
(declare-fun z () String)
(assert (= (str.len x) 999999999999999999999999))
(assert (= (str.len y) 999999999999999999999999))
(assert (= z (str.++ x y)))
(check-sat)
we now get:

sat
(
(define-fun x () String (witness ((s String)) (! (= (str.len s) 999999999999999999999999) :qid w1) ))
(define-fun y () String (witness ((s String)) (! (= (str.len s) 999999999999999999999999) :qid w0) ))
(define-fun z () String (str.++ (witness ((s String)) (! (= (str.len s) 999999999999999999999999) :qid w1) ) (witness ((s String)) (! (= (str.len s) 999999999999999999999999) :qid w0) )))
)

12 files changed:
src/parser/smt2/Smt2.g
src/printer/smt2/smt2_printer.cpp
src/theory/builtin/kinds
src/theory/builtin/theory_builtin_type_rules.h
src/theory/quantifiers/quantifiers_attributes.cpp
src/theory/quantifiers/quantifiers_attributes.h
src/theory/strings/theory_strings.cpp
src/theory/strings/theory_strings.h
src/theory/strings/theory_strings_utils.cpp
src/theory/strings/theory_strings_utils.h
test/regress/CMakeLists.txt
test/regress/regress0/strings/distinct-witness-id.smt2 [new file with mode: 0644]

index fafda041a4012b8afa95d1abe8cf5b916b16a757..e19a78c84afc272c3b183d13993bf69522a1273c 100644 (file)
@@ -1802,7 +1802,9 @@ attribute[cvc5::api::Term& expr, cvc5::api::Term& retExpr]
   | tok=( ATTRIBUTE_QUANTIFIER_ID_TOK ) symbol[s,CHECK_UNDECLARED,SYM_VARIABLE]
     {
       api::Term keyword = SOLVER->mkString("qid");
-      api::Term name = SOLVER->mkString(s);
+      // must create a variable whose name is the name of the quantified
+      // formula, not a string.
+      api::Term name = SOLVER->mkConst(SOLVER->getBooleanSort(), s);
       retExpr = MK_TERM(api::INST_ATTRIBUTE, keyword, name);
     }
   | ATTRIBUTE_NAMED_TOK symbol[s,CHECK_UNDECLARED,SYM_VARIABLE]
index 05252b2913116add913d75e3d8fcb62ab0193f55..f3078a450bb4ec334a71b192cbb33aa6df27d9d8 100644 (file)
@@ -858,20 +858,41 @@ void Smt2Printer::toStream(std::ostream& out,
       annot << " ";
       for (const Node& nc : n[2])
       {
-        if (nc.getKind() == kind::INST_PATTERN)
+        Kind nck = nc.getKind();
+        if (nck == kind::INST_PATTERN)
         {
           out << "(! ";
           annot << ":pattern ";
           toStream(annot, nc, toDepth, nullptr);
           annot << ") ";
         }
-        else if (nc.getKind() == kind::INST_NO_PATTERN)
+        else if (nck == kind::INST_NO_PATTERN)
         {
           out << "(! ";
           annot << ":no-pattern ";
           toStream(annot, nc[0], toDepth, nullptr);
           annot << ") ";
         }
+        else if (nck == kind::INST_ATTRIBUTE)
+        {
+          // notice that INST_ATTRIBUTES either have an "internal" form,
+          // where the argument is a variable with an internal attribute set
+          // on it, or an "external" form where it is of the form
+          // (INST_ATTRIBUTE "keyword" [nodeValues]). We print the latter
+          // here only.
+          if (nc[0].getKind() == kind::CONST_STRING)
+          {
+            out << "(! ";
+            // print out as string to avoid quotes
+            annot << ":" << nc[0].getConst<String>().toString();
+            for (size_t j = 1, nchild = nc.getNumChildren(); j < nchild; j++)
+            {
+              annot << " ";
+              toStream(annot, nc[j], toDepth, nullptr);
+            }
+            annot << ") ";
+          }
+        }
       }
     }
     // Use a fresh let binder, since using existing let symbols may violate
index eb184174ef8bf5949b563b5792d6e4fa254b3c2f..9f1b4fb57bbe706e0d3ed67137fbf4468e93b9d7 100644 (file)
@@ -292,7 +292,7 @@ variable BOUND_VARIABLE "a bound variable (permitted in bindings and the associa
 variable SKOLEM "a Skolem variable (internal only)"
 operator SEXPR 0: "a symbolic expression (any arity)"
 
-operator WITNESS 2 "a witness expression; first parameter is a BOUND_VAR_LIST, second is the witness body"
+operator WITNESS 2:3 "a witness expression; first parameter is a BOUND_VAR_LIST, second is the witness body"
 
 constant TYPE_CONSTANT \
     skip \
index d3173384079ece2ec76f993caab6cdf6e867946a..12e6b05a1dab4f9f00f9538089a2984a6c9679f3 100644 (file)
@@ -125,6 +125,16 @@ class WitnessTypeRule
         ss << "expected a body of a WITNESS expression to have Boolean type";
         throw TypeCheckingExceptionPrivate(n, ss.str());
       }
+      if (n.getNumChildren() == 3)
+      {
+        if (n[2].getType(check) != nodeManager->instPatternListType())
+        {
+          throw TypeCheckingExceptionPrivate(
+              n,
+              "third argument of witness is not instantiation "
+              "pattern list");
+        }
+      }
     }
     // The type of a witness function is the type of its bound variable.
     return n[0][0].getType();
index bb9568c88bc7c620d9f20887619527c83f5523d3..e6b4d49b1b076a4276edf880e301f8cbd7b6fa5d 100644 (file)
@@ -15,6 +15,8 @@
 
 #include "theory/quantifiers/quantifiers_attributes.h"
 
+#include "expr/node_manager_attributes.h"
+#include "expr/skolem_manager.h"
 #include "options/quantifiers_options.h"
 #include "theory/arith/arith_msum.h"
 #include "theory/quantifiers/fmf/bounded_integers.h"
@@ -269,13 +271,13 @@ void QuantAttributes::computeQuantAttributes( Node q, QAttributes& qa ){
           // only set the name if there is a value
           if (q[2][i].getNumChildren() > 1)
           {
-            Trace("quant-attr") << "Attribute : quantifier name : "
-                                << q[2][i][1].getConst<String>().toString()
+            std::string name;
+            q[2][i][1].getAttribute(expr::VarNameAttr(), name);
+            Trace("quant-attr") << "Attribute : quantifier name : " << name
                                 << " for " << q << std::endl;
             // assign the name to a variable with the given name (to avoid
             // enclosing the name in quotes)
-            qa.d_name = nm->mkBoundVar(q[2][i][1].getConst<String>().toString(),
-                                       nm->booleanType());
+            qa.d_name = nm->mkBoundVar(name, nm->booleanType());
           }
           else
           {
@@ -443,6 +445,18 @@ void QuantAttributes::setInstantiationLevelAttr(Node n, uint64_t level)
   }
 }
 
+Node mkNamedQuant(Kind k, Node bvl, Node body, const std::string& name)
+{
+  NodeManager* nm = NodeManager::currentNM();
+  SkolemManager* sm = nm->getSkolemManager();
+  Node v = sm->mkDummySkolem(
+      name, nm->booleanType(), "", SkolemManager::SKOLEM_EXACT_NAME);
+  Node attr = nm->mkConst(String("qid"));
+  Node ip = nm->mkNode(INST_ATTRIBUTE, attr, v);
+  Node ipl = nm->mkNode(INST_PATTERN_LIST, ip);
+  return nm->mkNode(k, bvl, body, ipl);
+}
+
 }  // namespace quantifiers
 }  // namespace theory
 }  // namespace cvc5
index 53f59b0e5942cd843ec8f6a9401678172699b0dd..24d02e960d37ff9547845c719971b1d7f61274ca 100644 (file)
@@ -227,6 +227,12 @@ class QuantAttributes
   std::map< Node, bool > d_fun_defs;
 };
 
+/**
+ * Make a named quantified formula. This is a quantified formula that will
+ * print like:
+ *   (<k> <bvl> (! <body> :qid name))
+ */
+Node mkNamedQuant(Kind k, Node bvl, Node body, const std::string& name);
 }
 }
 }  // namespace cvc5
index b14621f0744139b6ad0718fcbb1e248e33bb36f8..1f9d8caa5649ad8dde680cb2611ac4e7fad1f825 100644 (file)
@@ -84,7 +84,8 @@ TheoryStrings::TheoryStrings(Env& env, OutputChannel& out, Valuation valuation)
           env, d_state, d_im, d_termReg, d_csolver, d_esolver, d_statistics),
       d_regexp_elim(options().strings.regExpElimAgg, d_pnm, userContext()),
       d_stringsFmf(env, valuation, d_termReg),
-      d_strat(d_env)
+      d_strat(d_env),
+      d_absModelCounter(0)
 {
   d_termReg.finishInit(&d_im);
 
@@ -408,7 +409,9 @@ bool TheoryStrings::collectModelInfoType(
         Assert(!lenValue.isNull() && lenValue.isConst());
         // make the abstract value (witness ((x String)) (= (str.len x)
         // lenValue))
-        Node w = utils::mkAbstractStringValueForLength(eqc, lenValue);
+        Node w = utils::mkAbstractStringValueForLength(
+            eqc, lenValue, d_absModelCounter);
+        d_absModelCounter++;
         Trace("strings-model")
             << "-> length out of bounds, assign abstract " << w << std::endl;
         if (!m->assertEquality(eqc, w, true))
index 6337e164b01f4c1609db8250f452ff633427e800..9d15af8962154f05271938f248372ee59db8e23e 100644 (file)
@@ -317,6 +317,11 @@ class TheoryStrings : public Theory {
   StringsFmf d_stringsFmf;
   /** The representation of the strategy */
   Strategy d_strat;
+  /**
+   * For model building, a counter on the number of abstract witness terms
+   * we have built, so that unique debug names can be assigned.
+   */
+  size_t d_absModelCounter;
 };/* class TheoryStrings */
 
 }  // namespace strings
index 9404298c4e2de52a54b9485de6d0bef654fe8ab2..3c6562aae3a54e6d4aa7b67143f0522ef9d807e0 100644 (file)
@@ -22,6 +22,7 @@
 #include "expr/skolem_manager.h"
 #include "options/strings_options.h"
 #include "theory/quantifiers/fmf/bounded_integers.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
 #include "theory/rewriter.h"
 #include "theory/strings/arith_entail.h"
 #include "theory/strings/strings_entail.h"
@@ -419,7 +420,7 @@ struct StringValueForLengthVarAttributeId
 typedef expr::Attribute<StringValueForLengthVarAttributeId, Node>
     StringValueForLengthVarAttribute;
 
-Node mkAbstractStringValueForLength(Node n, Node len)
+Node mkAbstractStringValueForLength(Node n, Node len, size_t id)
 {
   NodeManager* nm = NodeManager::currentNM();
   BoundVarManager* bvm = nm->getBoundVarManager();
@@ -428,7 +429,10 @@ Node mkAbstractStringValueForLength(Node n, Node len)
       cacheVal, "s", n.getType());
   Node pred = nm->mkNode(STRING_LENGTH, v).eqNode(len);
   // return (witness ((v String)) (= (str.len v) len))
-  return nm->mkNode(WITNESS, nm->mkNode(BOUND_VAR_LIST, v), pred);
+  Node bvl = nm->mkNode(BOUND_VAR_LIST, v);
+  std::stringstream ss;
+  ss << "w" << id;
+  return quantifiers::mkNamedQuant(WITNESS, bvl, pred, ss.str());
 }
 
 }  // namespace utils
index 6aff3742aa889356a3c47ca236acd7acd461e598..59b58709a830fab96b248833aff3eb3ac8d902da 100644 (file)
@@ -209,7 +209,7 @@ Node mkForallInternal(Node bvl, Node body);
  * This is used for constructing models for strings whose lengths are too large
  * to represent in memory.
  */
-Node mkAbstractStringValueForLength(Node n, Node len);
+Node mkAbstractStringValueForLength(Node n, Node len, size_t id);
 
 }  // namespace utils
 }  // namespace strings
index 083edd4ec387fbec0de23444946fbedc414e2465..393e191dab80f1e8de2f5eaf0e064452e07a55f4 100644 (file)
@@ -1278,6 +1278,7 @@ set(regress_0_tests
   regress0/strings/code-sat-neg-one.smt2
   regress0/strings/complement-simple.smt2
   regress0/strings/delta-trust-subs.smt2
+  regress0/strings/distinct-witness-id.smt2
   regress0/strings/escchar_25.smt2
   regress0/strings/escchar.smt2
   regress0/strings/from_code.smt2
diff --git a/test/regress/regress0/strings/distinct-witness-id.smt2 b/test/regress/regress0/strings/distinct-witness-id.smt2
new file mode 100644 (file)
index 0000000..d306afa
--- /dev/null
@@ -0,0 +1,11 @@
+; COMMAND-LINE: -q
+; EXPECT: sat
+(set-logic ALL)
+(set-info :status sat)
+(declare-fun x () String)
+(declare-fun y () String)
+(declare-fun z () String)
+(assert (= (str.len x) 999999999999999999999999))
+(assert (= (str.len y) 999999999999999999999999))
+(assert (= z (str.++ x y)))
+(check-sat)