Stratify unfolding of regular expressions based on polarity (#3067)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Mon, 24 Jun 2019 15:30:27 +0000 (10:30 -0500)
committerGitHub <noreply@github.com>
Mon, 24 Jun 2019 15:30:27 +0000 (10:30 -0500)
src/theory/strings/regexp_solver.cpp
src/theory/strings/theory_strings.cpp
test/regress/CMakeLists.txt
test/regress/regress1/strings/regexp-strat-fix.smt2 [new file with mode: 0644]

index f0e68890a22dbc2db7bbf5fbb36c918e56db74e8..bf3a170df711ad0ddc3e7dcd1c28e754e0bfae69 100644 (file)
@@ -172,23 +172,35 @@ void RegExpSolver::check()
   if (!addedLemma)
   {
     NodeManager* nm = NodeManager::currentNM();
-    for (unsigned i = 0; i < d_regexp_memberships.size(); i++)
+    // representatives of strings that are the LHS of positive memberships that
+    // we unfolded
+    std::unordered_set<Node, NodeHashFunction> repUnfold;
+    // check positive (e=0), then negative (e=1) memberships
+    for (unsigned e = 0; e < 2; e++)
     {
-      // check regular expression membership
-      Node assertion = d_regexp_memberships[i];
-      Trace("regexp-debug")
-          << "Check : " << assertion << " "
-          << (d_regexp_ucached.find(assertion) == d_regexp_ucached.end()) << " "
-          << (d_regexp_ccached.find(assertion) == d_regexp_ccached.end())
-          << std::endl;
-      if (d_regexp_ucached.find(assertion) == d_regexp_ucached.end()
-          && d_regexp_ccached.find(assertion) == d_regexp_ccached.end())
+      for (const Node& assertion : d_regexp_memberships)
       {
+        // check regular expression membership
+        Trace("regexp-debug")
+            << "Check : " << assertion << " "
+            << (d_regexp_ucached.find(assertion) == d_regexp_ucached.end())
+            << " "
+            << (d_regexp_ccached.find(assertion) == d_regexp_ccached.end())
+            << std::endl;
+        if (d_regexp_ucached.find(assertion) != d_regexp_ucached.end()
+            || d_regexp_ccached.find(assertion) != d_regexp_ccached.end())
+        {
+          continue;
+        }
         Trace("strings-regexp")
             << "We have regular expression assertion : " << assertion
             << std::endl;
         Node atom = assertion.getKind() == NOT ? assertion[0] : assertion;
         bool polarity = assertion.getKind() != NOT;
+        if (polarity != (e == 0))
+        {
+          continue;
+        }
         bool flag = true;
         Node x = atom[0];
         Node r = atom[1];
@@ -228,7 +240,16 @@ void RegExpSolver::check()
             break;
           }
         }
-
+        if (e == 1 && repUnfold.find(x) != repUnfold.end())
+        {
+          // do not unfold negative memberships of strings that have new
+          // positive unfoldings. For example:
+          //   x in ("A")* ^ NOT x in ("B")*
+          // We unfold x = "A" ++ x' only. The intution here is that positive
+          // unfoldings lead to stronger constraints (equalities are stronger
+          // than disequalities), and are easier to check.
+          continue;
+        }
         if (polarity)
         {
           flag = checkPDerivative(x, r, atom, addedLemma, rnfexp);
@@ -269,11 +290,18 @@ void RegExpSolver::check()
           {
             processed.push_back(assertion);
           }
+          if (e == 0)
+          {
+            // Remember that we have unfolded a membership for x
+            // notice that we only do this here, after we have definitely
+            // added a lemma.
+            repUnfold.insert(x);
+          }
+        }
+        if (d_parent.inConflict())
+        {
+          break;
         }
-      }
-      if (d_parent.inConflict())
-      {
-        break;
       }
     }
   }
index cea3c35150bca51b4e564f3b0b320133fbc95010..7b6bbdc9974c7bdc1c161a0bf9dd1ca39ae111fb 100644 (file)
@@ -4023,10 +4023,9 @@ void TheoryStrings::registerTerm( Node n, int effort ) {
   else if (n.getKind() == STRING_STRIDOF)
   {
     Node len = mkLength(n[0]);
-    Node lem = nm->mkNode(
-        AND,
-        nm->mkNode(GEQ, n, nm->mkConst(Rational(-1))),
-        nm->mkNode(LT, n, len));
+    Node lem = nm->mkNode(AND,
+                          nm->mkNode(GEQ, n, nm->mkConst(Rational(-1))),
+                          nm->mkNode(LT, n, len));
     d_out->lemma(lem);
   }
 }
index f3abe2c8730ae8f5ff9d52ebd7538a18a999b055..3c5d82fa848b838dd7de41acfee43120705d50f0 100644 (file)
@@ -1580,6 +1580,7 @@ set(regress_1_tests
   regress1/strings/regexp001.smt2
   regress1/strings/regexp002.smt2
   regress1/strings/regexp003.smt2
+  regress1/strings/regexp-strat-fix.smt2
   regress1/strings/reloop.smt2
   regress1/strings/repl-empty-sem.smt2
   regress1/strings/repl-soundness-sem.smt2
diff --git a/test/regress/regress1/strings/regexp-strat-fix.smt2 b/test/regress/regress1/strings/regexp-strat-fix.smt2
new file mode 100644 (file)
index 0000000..9ff78c9
--- /dev/null
@@ -0,0 +1,8 @@
+(set-info :smt-lib-version 2.5)
+(set-logic QF_S)
+(set-info :status unsat)
+(set-option :strings-exp true)
+(declare-fun var0 () String)
+(assert (str.in.re var0 (re.* (re.* (str.to.re "0")))))
+(assert (not (str.in.re var0 (re.union (re.+ (str.to.re "0")) (re.* (str.to.re "1"))))))
+(check-sat)