Add proof for mult sign lemma (#5966)
authorGereon Kremer <gereon.kremer@cs.rwth-aachen.de>
Tue, 23 Feb 2021 18:31:28 +0000 (19:31 +0100)
committerGitHub <noreply@github.com>
Tue, 23 Feb 2021 18:31:28 +0000 (12:31 -0600)
This PR adds the proof for a nonlinear refinement lemma that infers the sign of a monomial from the sign of the variables.

src/expr/proof_rule.cpp
src/expr/proof_rule.h
src/theory/arith/nl/ext/monomial_check.cpp
src/theory/arith/nl/ext/proof_checker.cpp

index cc5613057b99d0119ce4f45ce4ba347eab950162..d29aca6ea52707fa14e19c7055f3a83567d2fea8 100644 (file)
@@ -159,6 +159,7 @@ const char* toString(PfRule id)
     case PfRule::INT_TIGHT_LB: return "INT_TIGHT_LB";
     case PfRule::INT_TIGHT_UB: return "INT_TIGHT_UB";
     case PfRule::INT_TRUST: return "INT_TRUST";
+    case PfRule::ARITH_MULT_SIGN: return "ARITH_MULT_SIGN";
     case PfRule::ARITH_MULT_POS: return "ARITH_MULT_POS";
     case PfRule::ARITH_MULT_NEG: return "ARITH_MULT_NEG";
     case PfRule::ARITH_MULT_TANGENT: return "ARITH_MULT_TANGENT";
index bf6f1d6ae1725555304176237bb103a99cbf3841..76c2d0958a34cf58a757970ea6063c45b2d396a0 100644 (file)
@@ -1092,6 +1092,18 @@ enum class PfRule : uint32_t
   // Conclusion: (Q)
   INT_TRUST,
 
+  //======== Multiplication sign inference
+  // Children: none
+  // Arguments: (f1, ..., fk, m)
+  // ---------------------
+  // Conclusion: (=> (and f1 ... fk) (~ m 0))
+  // Where f1, ..., fk are variables compared to zero (less, greater or not
+  // equal), m is a monomial from these variables, and ~ is the comparison (less
+  // or greater) that results from the signs of the variables. All variables
+  // with even exponent in m should be given as not equal to zero while all
+  // variables with odd exponent in m should be given as less or greater than
+  // zero.
+  ARITH_MULT_SIGN,
   //======== Multiplication with positive factor
   // Children: none
   // Arguments: (m, orig, lhs, rel, rhs)
index c6ee3d7648df944a5ed51c818334786b60d05ce4..a3e56358b29af823f5ffb2d07d8e07db6d16bb54 100644 (file)
@@ -295,7 +295,15 @@ int MonomialCheck::compareSign(
     {
       Node lemma =
           nm->mkAnd(exp).impNode(mkLit(oa, d_data->d_zero, status * 2));
-      d_data->d_im.addPendingLemma(lemma, InferenceId::ARITH_NL_SIGN);
+      CDProof* proof = nullptr;
+      if (d_data->isProofEnabled())
+      {
+        proof = d_data->getProof();
+        std::vector<Node> args = exp;
+        args.emplace_back(oa);
+        proof->addStep(lemma, PfRule::ARITH_MULT_SIGN, {}, args);
+      }
+      d_data->d_im.addPendingLemma(lemma, InferenceId::ARITH_NL_SIGN, proof);
     }
     return status;
   }
index a0c5d202137b5da4cbb43230d020ceef5a98cc45..e3e26cb654203eadeb75d42cd843570cd23a3eb6 100644 (file)
@@ -27,6 +27,7 @@ namespace nl {
 
 void ExtProofRuleChecker::registerTo(ProofChecker* pc)
 {
+  pc->registerChecker(PfRule::ARITH_MULT_SIGN, this);
   pc->registerChecker(PfRule::ARITH_MULT_POS, this);
   pc->registerChecker(PfRule::ARITH_MULT_NEG, this);
   pc->registerChecker(PfRule::ARITH_MULT_TANGENT, this);
@@ -47,7 +48,78 @@ Node ExtProofRuleChecker::checkInternal(PfRule id,
   {
     Trace("nl-ext-checker") << "\t" << c << std::endl;
   }
-  if (id == PfRule::ARITH_MULT_POS)
+  if (id == PfRule::ARITH_MULT_SIGN)
+  {
+    Assert(children.empty());
+    Assert(args.size() > 1);
+    Node mon = args.back();
+    std::map<Node, int> exps;
+    std::vector<Node> premise = args;
+    premise.pop_back();
+    Assert(mon.getKind() == Kind::MULT
+           || mon.getKind() == Kind::NONLINEAR_MULT);
+    for (const auto& v : mon)
+    {
+      exps[v]++;
+    }
+    std::map<Node, int> signs;
+    for (const auto& f : premise)
+    {
+      if (f.getKind() == Kind::NOT)
+      {
+        Assert(f[0].getKind() == Kind::EQUAL);
+        Assert(f[0][1].isConst() && f[0][1].getConst<Rational>().isZero());
+        Assert(signs.find(f[0][0]) == signs.end());
+        signs.emplace(f[0][0], 0);
+        continue;
+      }
+      Assert(f.getKind() == Kind::LT || f.getKind() == Kind::GT);
+      Assert(f[1].isConst() && f[1].getConst<Rational>().isZero());
+      Assert(signs.find(f[0]) == signs.end());
+      signs.emplace(f[0], f.getKind() == Kind::LT ? -1 : 1);
+    }
+    int sign = 0;
+    for (const auto& ve : exps)
+    {
+      auto sit = signs.find(ve.first);
+      Assert(sit != signs.end());
+      if (ve.second % 2 == 0)
+      {
+        Assert(sit->second == 0);
+        if (sign == 0)
+        {
+          sign = 1;
+        }
+      }
+      else
+      {
+        Assert(sit->second != 0);
+        if (sign == 0)
+        {
+          sign = sit->second;
+        }
+        else
+        {
+          sign *= sit->second;
+        }
+      }
+    }
+    switch (sign)
+    {
+      case -1:
+        return nm->mkNode(
+            Kind::IMPLIES, nm->mkAnd(premise), nm->mkNode(Kind::GT, zero, mon));
+      case 0:
+        return nm->mkNode(Kind::IMPLIES,
+                          nm->mkAnd(premise),
+                          nm->mkNode(Kind::DISTINCT, mon, zero));
+      case 1:
+        return nm->mkNode(
+            Kind::IMPLIES, nm->mkAnd(premise), nm->mkNode(Kind::GT, mon, zero));
+      default: Assert(false); return Node();
+    }
+  }
+  else if (id == PfRule::ARITH_MULT_POS)
   {
     Assert(children.empty());
     Assert(args.size() == 3);