Bugfix: proofs for props of non-normal arith lits (#5702)
authorAlex Ozdemir <aozdemir@hmc.edu>
Fri, 18 Dec 2020 15:01:58 +0000 (07:01 -0800)
committerGitHub <noreply@github.com>
Fri, 18 Dec 2020 15:01:58 +0000 (09:01 -0600)
Arith has a normal form for literals, which the rewriter computes.

Nonetheless, arith sometimes gets (and ultimately propagates) non-normal
literals. It normalizes them internally and goes about its business.

However, when asked for an explanation, it must prove the non-normal
literal, rather than the normal one.

Also includes a regression for the bug, courtesy of Andy.

Co-authored-by: Andrew Reynolds andrew-reynolds@uiowa.edu
src/theory/arith/constraint.cpp
src/theory/arith/constraint.h
src/theory/arith/theory_arith_private.cpp
test/regress/CMakeLists.txt
test/regress/regress0/arith/non-normal.smt2 [new file with mode: 0644]

index 03d9c50e94d1c0a854aa765323329998704498a8..c1db8e55a6d9e3263ad573cbc06fe2f20e815591 100644 (file)
@@ -1507,7 +1507,7 @@ Node Constraint::externalExplainByAssertions(const ConstraintCPVec& b){
   return externalExplain(b, AssertionOrderSentinel);
 }
 
-TrustNode Constraint::externalExplainForPropagation() const
+TrustNode Constraint::externalExplainForPropagation(TNode lit) const
 {
   Assert(hasProof());
   Assert(!isAssumption());
@@ -1517,6 +1517,9 @@ TrustNode Constraint::externalExplainForPropagation() const
   Node n = safeConstructNary(nb);
   if (d_database->isProofEnabled())
   {
+    // Check that the literal we're explaining via this constraint actually
+    // matches the constraint's canonical literal.
+    Assert(Rewriter::rewrite(lit) == getLiteral());
     std::vector<Node> assumptions;
     if (n.getKind() == Kind::AND)
     {
@@ -1526,18 +1529,18 @@ TrustNode Constraint::externalExplainForPropagation() const
     {
       assumptions.push_back(n);
     }
-    if (getProofLiteral() != getLiteral())
+    if (getProofLiteral() != lit)
     {
       pfFromAssumptions = d_database->d_pnm->mkNode(
-          PfRule::MACRO_SR_PRED_TRANSFORM, {pfFromAssumptions}, {getLiteral()});
+          PfRule::MACRO_SR_PRED_TRANSFORM, {pfFromAssumptions}, {lit});
     }
     auto pf = d_database->d_pnm->mkScope(pfFromAssumptions, assumptions);
     return d_database->d_pfGen->mkTrustedPropagation(
-        getLiteral(), safeConstructNary(Kind::AND, assumptions), pf);
+        lit, safeConstructNary(Kind::AND, assumptions), pf);
   }
   else
   {
-    return TrustNode::mkTrustPropExp(getLiteral(), n);
+    return TrustNode::mkTrustPropExp(lit, n);
   }
 }
 
index 952879182f372e96f1623817158ffa9be806b85a..70b2cad207a5f9a410b4f006b362a3bf75948815 100644 (file)
@@ -601,8 +601,17 @@ class Constraint {
    * hasEqualityEngineProof().
    *
    * All return conjuncts were asserted before this constraint.
+   *
+   * Requires the given node to rewrite to the canonical literal for this
+   * constraint.
+   *
+   * @params n the literal to prove
+   *           n must rewrite to the constraint's canonical literal
+   *
+   * @returns a trust node of the form:
+   *         (=> explanation n)
    */
-  TrustNode externalExplainForPropagation() const;
+  TrustNode externalExplainForPropagation(TNode n) const;
 
   /**
    * Explain the constraint and its negation in terms of assertions.
index ce0445d1f120df79dcb4b9930ea8352300d4a051..d1068c6acfa996d5f9de0e19afa706a6e96ea4c9 100644 (file)
@@ -3848,12 +3848,12 @@ TrustNode TheoryArithPrivate::explain(TNode n)
   TrustNode exp;
   if(c != NullConstraint){
     Assert(!c->isAssumption());
-    exp = c->externalExplainForPropagation();
+    exp = c->externalExplainForPropagation(n);
     Debug("arith::explain") << "constraint explanation" << n << ":" << exp << endl;
   }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){
     c = d_assertionsThatDoNotMatchTheirLiterals[n];
     if(!c->isAssumption()){
-      exp = c->externalExplainForPropagation();
+      exp = c->externalExplainForPropagation(n);
       Debug("arith::explain") << "assertions explanation" << n << ":" << exp << endl;
     }else{
       Debug("arith::explain") << "this is a strange mismatch" << n << endl;
index d185049e01bcb01847b1d0eb5993c1a599398884..2c480da3aef4ea531767cffc2d213d4bc8fdbcc8 100644 (file)
@@ -68,6 +68,7 @@ set(regress_0_tests
   regress0/arith/mod-simp.smt2
   regress0/arith/mod.01.smt2
   regress0/arith/mult.01.smt2
+  regress0/arith/non-normal.smt2
   regress0/arr1.smt2
   regress0/arr1.smtv1.smt2
   regress0/arr2.smtv1.smt2
diff --git a/test/regress/regress0/arith/non-normal.smt2 b/test/regress/regress0/arith/non-normal.smt2
new file mode 100644 (file)
index 0000000..389bd6d
--- /dev/null
@@ -0,0 +1,16 @@
+; COMMAND-LINE: --proof-new-eager-checking
+; EXPECT: sat
+(set-logic QF_UFLRA)
+(declare-fun v1 () Real)
+(declare-fun v2 () Real)
+(declare-fun f1 (Real) Real)
+(declare-fun f0 (Real Real) Real)
+(declare-fun p0 (Real Real) Bool)
+(assert 
+(ite 
+  (or (= 0.0 v2) (> 1 (ite (< (- (- v1)) 1.0) 1.0 (ite (p0 0.0 v1) 0.0 1.0)))) 
+  (= 0.0 (ite (= 1.0 (f1 0.0)) 0.0 (f1 1.0))) 
+  (and 
+    (= 1.0 (ite (= 0.0 (f1 1.0)) v2 (- 1.0))) 
+    (=> (not (p0 0.0 (- (- (- v2))))) (= 1.0 (ite (= 1.0 v2) (ite (= 0 (f0 0.0 1.0)) 1.0 (f0 0.0 (- v2 (- v1)))) 0.0))))))
+(check-sat)