Fixes and improvements for single invocation inference (#2261)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Mon, 6 Aug 2018 22:33:23 +0000 (17:33 -0500)
committerGitHub <noreply@github.com>
Mon, 6 Aug 2018 22:33:23 +0000 (17:33 -0500)
src/theory/quantifiers/single_inv_partition.cpp
src/theory/quantifiers/single_inv_partition.h
test/regress/Makefile.tests
test/regress/regress1/sygus/constant-bool-si-all.sy [new file with mode: 0644]

index 40c191ca2b0299c7ff3fd281ea1ce15abc42775d..851204a848a88076eacb270fa75167f0b59b7e83 100644 (file)
@@ -128,26 +128,38 @@ bool SingleInvocationPartition::inferArgTypes(Node n,
 
 bool SingleInvocationPartition::init(std::vector<Node>& funcs, Node n)
 {
-  Trace("si-prt") << "Initialize with " << funcs.size() << " input functions..."
-                  << std::endl;
+  Trace("si-prt") << "Initialize with " << funcs.size() << " input functions ("
+                  << funcs << ")..." << std::endl;
   std::vector<TypeNode> typs;
   if (!funcs.empty())
   {
     TypeNode tn0 = funcs[0].getType();
-    for (unsigned i = 1; i < funcs.size(); i++)
+    if (tn0.getNumChildren() > 0)
     {
-      if (funcs[i].getType() != tn0)
+      for (unsigned i = 0, nargs = tn0.getNumChildren() - 1; i < nargs; i++)
+      {
+        typs.push_back(tn0[i]);
+      }
+    }
+    for (unsigned i = 1, size = funcs.size(); i < size; i++)
+    {
+      TypeNode tni = funcs[i].getType();
+      if (tni.getNumChildren() != tn0.getNumChildren())
       {
         // can't anti-skolemize functions of different sort
         Trace("si-prt") << "...type mismatch" << std::endl;
         return false;
       }
-    }
-    if (tn0.getNumChildren() > 1)
-    {
-      for (unsigned j = 0; j < tn0.getNumChildren() - 1; j++)
+      else if (tni.getNumChildren() > 0)
       {
-        typs.push_back(tn0[j]);
+        for (unsigned j = 0, nargs = tni.getNumChildren() - 1; j < nargs; j++)
+        {
+          if (tni[j] != typs[j])
+          {
+            Trace("si-prt") << "...argument type mismatch" << std::endl;
+            return false;
+          }
+        }
       }
     }
   }
@@ -163,6 +175,7 @@ bool SingleInvocationPartition::init(std::vector<Node>& funcs,
   Assert(d_arg_types.empty());
   Assert(d_input_funcs.empty());
   Assert(d_si_vars.empty());
+  NodeManager* nm = NodeManager::currentNM();
   d_has_input_funcs = has_funcs;
   d_arg_types.insert(d_arg_types.end(), typs.begin(), typs.end());
   d_input_funcs.insert(d_input_funcs.end(), funcs.begin(), funcs.end());
@@ -171,10 +184,15 @@ bool SingleInvocationPartition::init(std::vector<Node>& funcs,
   {
     std::stringstream ss;
     ss << "s_" << j;
-    Node si_v = NodeManager::currentNM()->mkBoundVar(ss.str(), d_arg_types[j]);
+    Node si_v = nm->mkBoundVar(ss.str(), d_arg_types[j]);
     d_si_vars.push_back(si_v);
   }
   Assert(d_si_vars.size() == d_arg_types.size());
+  for (const Node& inf : d_input_funcs)
+  {
+    Node sk = nm->mkSkolem("_sik", inf.getType());
+    d_input_func_sks.push_back(sk);
+  }
   Trace("si-prt") << "SingleInvocationPartition::process " << n << std::endl;
   Trace("si-prt") << "Get conjuncts..." << std::endl;
   std::vector<Node> conj;
@@ -187,7 +205,21 @@ bool SingleInvocationPartition::init(std::vector<Node>& funcs,
       std::vector<Node> si_subs;
       Trace("si-prt") << "Process conjunct : " << conj[i] << std::endl;
       // do DER on conjunct
-      Node cr = TermUtil::getQuantSimplify(conj[i]);
+      // Must avoid eliminating the first-order input functions in the
+      // getQuantSimplify step below. We use a substitution to avoid this.
+      // This makes it so that e.g. the synthesis conjecture:
+      //   exists f. f!=0 ^ P
+      // is not rewritten to exists f. (f=0 => false) ^ P and subsquently
+      // rewritten to exists f. false ^ P by the elimination f -> 0.
+      Node cr = conj[i].substitute(d_input_funcs.begin(),
+                                   d_input_funcs.end(),
+                                   d_input_func_sks.begin(),
+                                   d_input_func_sks.end());
+      cr = TermUtil::getQuantSimplify(cr);
+      cr = cr.substitute(d_input_func_sks.begin(),
+                         d_input_func_sks.end(),
+                         d_input_funcs.begin(),
+                         d_input_funcs.end());
       if (cr != conj[i])
       {
         Trace("si-prt-debug") << "...rewritten to " << cr << std::endl;
index 588b1fde80f69a133d0a9f3842bdff807d449cf1..199ab29d43beafdf529964dd41f28cdb94ee15c9 100644 (file)
@@ -228,6 +228,8 @@ class SingleInvocationPartition
   std::vector<Node> d_input_funcs;
   /** all input functions */
   std::vector<Node> d_all_funcs;
+  /** skolem of the same type as input functions */
+  std::vector<Node> d_input_func_sks;
 
   /** infer the argument types of uninterpreted function applications
    *
index b8479475f271454b99c3a53d001b7ce9d6dd1891..34855e81ddb10ced04962ad9bdbc3720c7174e38 100644 (file)
@@ -1508,6 +1508,7 @@ REG1_TESTS = \
        regress1/sygus/clock-inc-tuple.sy \
        regress1/sygus/commutative.sy \
        regress1/sygus/constant.sy \
+       regress1/sygus/constant-bool-si-all.sy \
        regress1/sygus/constant-dec-tree-bug.sy \
        regress1/sygus/constant-ite-bv.sy \
        regress1/sygus/crci-ssb-unk.sy \
diff --git a/test/regress/regress1/sygus/constant-bool-si-all.sy b/test/regress/regress1/sygus/constant-bool-si-all.sy
new file mode 100644 (file)
index 0000000..eee7956
--- /dev/null
@@ -0,0 +1,14 @@
+; EXPECT: unsat
+; COMMAND-LINE: --sygus-out=status
+(set-logic SAT)
+(synth-fun f () Bool)
+(synth-fun g () Bool)
+(synth-fun h () Bool)
+(synth-fun w () Int)
+
+(constraint (not (= w 0)))
+(constraint f)
+(constraint (not g))
+(constraint h)
+
+(check-synth)