Fix issues related to proofs of lemmas with duplicate conclusions in strings (#7366)
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>
Fri, 15 Oct 2021 12:24:28 +0000 (07:24 -0500)
committerGitHub <noreply@github.com>
Fri, 15 Oct 2021 12:24:28 +0000 (12:24 +0000)
This makes string proof construction more robust by maintaining two separate proof inference constructors, one for facts and one for lemmas/conflicts. This avoids issues where 2 lemmas with the same conclusion (but possibly different explanations) are added in the same call to check.

This fixes one of the two issues related to proofs for #6973.

src/theory/strings/infer_proof_cons.cpp
src/theory/strings/infer_proof_cons.h
src/theory/strings/inference_manager.cpp
src/theory/strings/inference_manager.h
src/theory/strings/regexp_solver.cpp
test/regress/CMakeLists.txt
test/regress/regress1/strings/issue6973-dup-lemma-conc.smt2 [new file with mode: 0644]

index 34597c8be381d91249d78adaeb86c192209c9d39..5eba8663ab695ecef0b7f789fe5f2a9e88cd549c 100644 (file)
@@ -59,6 +59,11 @@ void InferProofCons::notifyFact(const InferInfo& ii)
   d_lazyFactMap.insert(ii.d_conc, iic);
 }
 
+void InferProofCons::notifyLemma(const InferInfo& ii)
+{
+  d_lazyFactMap[ii.d_conc] = std::make_shared<InferInfo>(ii);
+}
+
 bool InferProofCons::addProofTo(CDProof* pf,
                                 Node conc,
                                 InferenceId infer,
index 110d231cf177ba1d8fab64ae3f266dd2096f3b05..02b266fd7a93de2478a8857ca72a3067d27360a9 100644 (file)
@@ -69,6 +69,11 @@ class InferProofCons : public ProofGenerator
    * only for facts that are explained.
    */
   void notifyFact(const InferInfo& ii);
+  /**
+   * Same as above, but always overwrites. This is used for lemmas and
+   * conflicts, which do not necessarily have unique conclusions.
+   */
+  void notifyLemma(const InferInfo& ii);
 
   /**
    * This returns the proof for fact. This is required for using this class as
index 0e971fc54c0b7aa0df4cca6c147d2073831104e7..1f531a97c9aeea9847fb01a29e662c357fc65253 100644 (file)
@@ -42,7 +42,8 @@ InferenceManager::InferenceManager(Env& env,
       d_termReg(tr),
       d_extt(e),
       d_statistics(statistics),
-      d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr)
+      d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr),
+      d_ipcl(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr)
 {
   NodeManager* nm = NodeManager::currentNM();
   d_zero = nm->mkConst(Rational(0));
@@ -279,12 +280,12 @@ void InferenceManager::processConflict(const InferInfo& ii)
 {
   Assert(!d_state.isInConflict());
   // setup the fact to reproduce the proof in the call below
-  if (d_ipc != nullptr)
+  if (d_ipcl != nullptr)
   {
-    d_ipc->notifyFact(ii);
+    d_ipcl->notifyLemma(ii);
   }
   // make the trust node
-  TrustNode tconf = mkConflictExp(ii.d_premises, d_ipc.get());
+  TrustNode tconf = mkConflictExp(ii.d_premises, d_ipcl.get());
   Assert(tconf.getKind() == TrustNodeKind::CONFLICT);
   Trace("strings-assert") << "(assert (not " << tconf.getNode()
                           << ")) ; conflict " << ii.getId() << std::endl;
@@ -335,11 +336,11 @@ TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p)
   }
   // ensure that the proof generator is ready to explain the final conclusion
   // of the lemma (ii.d_conc).
-  if (d_ipc != nullptr)
+  if (d_ipcl != nullptr)
   {
-    d_ipc->notifyFact(ii);
+    d_ipcl->notifyLemma(ii);
   }
-  TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipc.get());
+  TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipcl.get());
   Trace("strings-pending") << "Process pending lemma : " << tlem.getNode()
                            << std::endl;
 
index 49f10d7cbff0109dfec9af7f3f97d9f5177cf519..9f4cd1986f0ce2fe3a5e1644301bbd1e711fbdde 100644 (file)
@@ -249,8 +249,15 @@ class InferenceManager : public InferenceManagerBuffered
   ExtTheory& d_extt;
   /** Reference to the statistics for the theory of strings/sequences. */
   SequencesStatistics& d_statistics;
-  /** Conversion from inferences to proofs */
+  /** Conversion from inferences to proofs for facts */
   std::unique_ptr<InferProofCons> d_ipc;
+  /**
+   * Conversion from inferences to proofs for lemmas and conflicts. This is
+   * separate from the above proof generator to avoid rare cases where the
+   * conclusion of a lemma is a duplicate of the conclusion of another lemma,
+   * or is a fact in the current equality engine.
+   */
+  std::unique_ptr<InferProofCons> d_ipcl;
   /** Common constants */
   Node d_true;
   Node d_false;
index 614a5e6e030faa329acdf1f20999be19228ce620..898c0f1b7cdc1f4cbffd2c6db493109596ae1ace 100644 (file)
@@ -546,7 +546,10 @@ bool RegExpSolver::checkPDerivative(
       {
         std::vector<Node> noExplain;
         noExplain.push_back(atom);
-        noExplain.push_back(x.eqNode(d_emptyString));
+        if (x != d_emptyString)
+        {
+          noExplain.push_back(x.eqNode(d_emptyString));
+        }
         std::vector<Node> iexp = nf_exp;
         iexp.insert(iexp.end(), noExplain.begin(), noExplain.end());
         d_im.sendInference(iexp, noExplain, d_false, InferenceId::STRINGS_RE_DELTA_CONF);
index 54fb91db42ae45230aa7fcde72cc21dabac156ee..587b2d9d6f6e86905424dbddbeb3bcac60511dec 100644 (file)
@@ -2204,6 +2204,7 @@ set(regress_1_tests
   regress1/strings/issue6653-rre-small.smt2
   regress1/strings/issue6777-seq-nth-eval-cm.smt2
   regress1/strings/issue6913.smt2
+  regress1/strings/issue6973-dup-lemma-conc.smt2 
   regress1/strings/kaluza-fl.smt2
   regress1/strings/loop002.smt2
   regress1/strings/loop003.smt2
diff --git a/test/regress/regress1/strings/issue6973-dup-lemma-conc.smt2 b/test/regress/regress1/strings/issue6973-dup-lemma-conc.smt2
new file mode 100644 (file)
index 0000000..4c6fe5c
--- /dev/null
@@ -0,0 +1,15 @@
+(set-logic QF_SLIA)
+(set-info :status unsat)
+(declare-fun a () String)
+(assert
+ (str.in_re ""
+  (re.++ (re.diff (re.comp re.all) (re.++ (str.to_re a) (re.comp re.all)))
+   (str.to_re
+    (ite
+     (str.in_re ""
+      (re.++ (str.to_re (ite (str.in_re "" (re.++ (str.to_re a) (re.comp re.all))) a ""))
+       (re.inter (str.to_re a)
+        (re.++ (str.to_re a)
+         (re.comp (re.union (re.++ (str.to_re a) (re.comp re.all)) re.all))))))
+     a "")))))
+(check-sat)