Add trans secant proofs. (#5957)
authorGereon Kremer <gereon.kremer@cs.rwth-aachen.de>
Tue, 23 Feb 2021 04:35:46 +0000 (05:35 +0100)
committerGitHub <noreply@github.com>
Tue, 23 Feb 2021 04:35:46 +0000 (22:35 -0600)
This PR adds proofs for secant lemmas in the transcendental lemmas for both exponential and sine functions.
It also adds proof rules and corresponding proof checkers.

src/expr/proof_rule.cpp
src/expr/proof_rule.h
src/theory/arith/nl/transcendental/proof_checker.cpp
src/theory/arith/nl/transcendental/transcendental_solver.cpp
src/theory/arith/nl/transcendental/transcendental_state.cpp

index 110d9467e9c4b0ee42af6a603e56f486a0277fa9..85463d2d930abf3d66b9450840d13067c1828aae 100644 (file)
@@ -167,6 +167,10 @@ const char* toString(PfRule id)
       return "ARITH_TRANS_EXP_POSITIVITY";
     case PfRule::ARITH_TRANS_EXP_SUPER_LIN: return "ARITH_TRANS_EXP_SUPER_LIN";
     case PfRule::ARITH_TRANS_EXP_ZERO: return "ARITH_TRANS_EXP_ZERO";
+    case PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_NEG:
+      return "ARITH_TRANS_EXP_APPROX_ABOVE_NEG";
+    case PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_POS:
+      return "ARITH_TRANS_EXP_APPROX_ABOVE_POS";
     case PfRule::ARITH_TRANS_EXP_APPROX_BELOW:
       return "ARITH_TRANS_EXP_APPROX_BELOW";
     case PfRule::ARITH_TRANS_SINE_BOUNDS: return "ARITH_TRANS_SINE_BOUNDS";
index 10801648f5cd81e9ee35d06ecfd4c25f3745d0f4..917db40889640a0bc9ee5008379931d5d8679f24 100644 (file)
@@ -1137,6 +1137,36 @@ enum class PfRule : uint32_t
   // ---------------------
   // Conclusion: (= (= t 0) (= (exp t) 1))
   ARITH_TRANS_EXP_ZERO,
+  //======== Exp is approximated from above for negative values
+  // Children: none
+  // Arguments: (d, t, l, u)
+  // ---------------------
+  // Conclusion: (=> (and (>= t l) (<= t u)) (<= (exp t) (secant exp l u t))
+  // Where d is an even positive number, t an arithmetic term and l (u) a lower
+  // (upper) bound on t. Let p be the d'th taylor polynomial at zero (also
+  // called the Maclaurin series) of the exponential function. (secant exp l u
+  // t) denotes the secant of p from (l, exp(l)) to (u, exp(u)) evaluated at t,
+  // calculated as follows:
+  //    (p(l) - p(u)) / (l - u) * (t - l) + p(l)
+  // The lemma states that if t is between l and u, then (exp t) is below the
+  // secant of p from l to u.
+  ARITH_TRANS_EXP_APPROX_ABOVE_NEG,
+  //======== Exp is approximated from above for positive values
+  // Children: none
+  // Arguments: (d, t, l, u)
+  // ---------------------
+  // Conclusion: (=> (and (>= t l) (<= t u)) (<= (exp t) (secant-pos exp l u t))
+  // Where d is an even positive number, t an arithmetic term and l (u) a lower
+  // (upper) bound on t. Let p* be a modification of the d'th taylor polynomial
+  // at zero (also called the Maclaurin series) of the exponential function as
+  // follows where p(d-1) is the regular Maclaurin series of degree d-1:
+  //    p* = p(d-1) * (1 + t^n / n!)
+  // (secant-pos exp l u t) denotes the secant of p from (l, exp(l)) to (u,
+  // exp(u)) evaluated at t, calculated as follows:
+  //    (p(l) - p(u)) / (l - u) * (t - l) + p(l)
+  // The lemma states that if t is between l and u, then (exp t) is below the
+  // secant of p from l to u.
+  ARITH_TRANS_EXP_APPROX_ABOVE_POS,
   //======== Exp is approximated from below
   // Children: none
   // Arguments: (d, t)
index 6dc2df2015d07b8d3660b50218e5a76ac861f024..e09c8509a9dde6093d9da2d4f54e69cff0a50ce4 100644 (file)
@@ -64,7 +64,10 @@ void TranscendentalProofRuleChecker::registerTo(ProofChecker* pc)
   pc->registerChecker(PfRule::ARITH_TRANS_EXP_POSITIVITY, this);
   pc->registerChecker(PfRule::ARITH_TRANS_EXP_SUPER_LIN, this);
   pc->registerChecker(PfRule::ARITH_TRANS_EXP_ZERO, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_POS, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_NEG, this);
   pc->registerChecker(PfRule::ARITH_TRANS_EXP_APPROX_BELOW, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_SINE_BOUNDS, this);
   pc->registerChecker(PfRule::ARITH_TRANS_SINE_SHIFT, this);
   pc->registerChecker(PfRule::ARITH_TRANS_SINE_SYMMETRY, this);
   pc->registerChecker(PfRule::ARITH_TRANS_SINE_TANGENT_ZERO, this);
@@ -133,6 +136,62 @@ Node TranscendentalProofRuleChecker::checkInternal(
     Node e = nm->mkNode(Kind::EXPONENTIAL, args[0]);
     return nm->mkNode(EQUAL, args[0].eqNode(zero), e.eqNode(one));
   }
+  else if (id == PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_POS)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 4);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    Assert(args[2].isConst() && args[2].getKind() == Kind::CONST_RATIONAL);
+    Assert(args[3].isConst() && args[3].getKind() == Kind::CONST_RATIONAL);
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    Node l = args[2];
+    Node u = args[3];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::EXPONENTIAL, d / 2, bounds);
+    Node evall = Rewriter::rewrite(
+        bounds.d_upperPos.substitute(tg.getTaylorVariable(), l));
+    Node evalu = Rewriter::rewrite(
+        bounds.d_upperPos.substitute(tg.getTaylorVariable(), u));
+    Node evalsecant = mkSecant(t, l, u, evall, evalu);
+    Node lem = nm->mkNode(
+        Kind::IMPLIES,
+        mkBounds(t, l, u),
+        nm->mkNode(Kind::LEQ, nm->mkNode(Kind::EXPONENTIAL, t), evalsecant));
+    return Rewriter::rewrite(lem);
+  }
+  else if (id == PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_NEG)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 4);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    Assert(args[2].isConst() && args[2].getKind() == Kind::CONST_RATIONAL);
+    Assert(args[3].isConst() && args[3].getKind() == Kind::CONST_RATIONAL);
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    Node l = args[2];
+    Node u = args[3];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::EXPONENTIAL, d / 2, bounds);
+    Node evall = Rewriter::rewrite(
+        bounds.d_upperNeg.substitute(tg.getTaylorVariable(), l));
+    Node evalu = Rewriter::rewrite(
+        bounds.d_upperNeg.substitute(tg.getTaylorVariable(), u));
+    Node evalsecant = mkSecant(t, l, u, evall, evalu);
+    Node lem = nm->mkNode(
+        Kind::IMPLIES,
+        mkBounds(t, l, u),
+        nm->mkNode(Kind::LEQ, nm->mkNode(Kind::EXPONENTIAL, t), evalsecant));
+    return Rewriter::rewrite(lem);
+  }
   else if (id == PfRule::ARITH_TRANS_EXP_APPROX_BELOW)
   {
     Assert(children.empty());
index 0548c6a4164d238564adfcf73a56bd627b392bc8..e58da1aad43704c2babe348ab3a136853a6eeb68 100644 (file)
@@ -323,14 +323,14 @@ bool TranscendentalSolver::checkTfTangentPlanesFun(Node tf, unsigned d)
     Node v_pab = r == 0 ? mvb.first : mvb.second;
     if (!v_pab.isNull())
     {
-      Trace("nl-ext-tftp-debug2")
-          << "...model value of " << pab << " is " << v_pab << std::endl;
+      Trace("nl-trans") << "...model value of " << pab << " is " << v_pab
+                        << std::endl;
 
       Assert(v_pab.isConst());
       Node comp = nm->mkNode(r == 0 ? LT : GT, v, v_pab);
-      Trace("nl-ext-tftp-debug2") << "...compare : " << comp << std::endl;
+      Trace("nl-trans") << "...compare : " << comp << std::endl;
       Node compr = Rewriter::rewrite(comp);
-      Trace("nl-ext-tftp-debug2") << "...got : " << compr << std::endl;
+      Trace("nl-trans") << "...got : " << compr << std::endl;
       if (compr == d_tstate.d_true)
       {
         poly_approx_c = Rewriter::rewrite(v_pab);
@@ -374,8 +374,8 @@ bool TranscendentalSolver::checkTfTangentPlanesFun(Node tf, unsigned d)
   // Figure 3: P( c )
   if (is_tangent || is_secant)
   {
-    Trace("nl-ext-tftp-debug2")
-        << "...poly approximation at c is " << poly_approx_c << std::endl;
+    Trace("nl-trans") << "...poly approximation at c is " << poly_approx_c
+                      << std::endl;
   }
   else
   {
index 35967a39a15656d9b128b2678b6495f062bfd60d..76c3d5357078649a7ed9eb35e57b00eddb2bbad1 100644 (file)
@@ -319,7 +319,61 @@ NlLemma TranscendentalState::mkSecantLemma(TNode lower,
   lem = Rewriter::rewrite(lem);
   Trace("nl-trans-lemma") << "*** Secant plane lemma : " << lem << std::endl;
   Assert(d_model.computeAbstractModelValue(lem) == d_false);
-  return NlLemma(InferenceId::ARITH_NL_T_SECANT, lem);
+  CDProof* proof = nullptr;
+  if (isProofEnabled())
+  {
+    proof = getProof();
+    if (tf.getKind() == Kind::EXPONENTIAL)
+    {
+      if (csign == 1)
+      {
+        proof->addStep(
+            lem,
+            PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_POS,
+            {},
+            {nm->mkConst<Rational>(2 * actual_d), tf[0], lower, upper});
+      }
+      else
+      {
+        proof->addStep(
+            lem,
+            PfRule::ARITH_TRANS_EXP_APPROX_ABOVE_NEG,
+            {},
+            {nm->mkConst<Rational>(2 * actual_d), tf[0], lower, upper});
+      }
+    }
+    else if (tf.getKind() == Kind::SINE)
+    {
+      if (convexity == Convexity::CONCAVE)
+      {
+        proof->addStep(lem,
+                       PfRule::ARITH_TRANS_SINE_APPROX_BELOW_POS,
+                       {},
+                       {nm->mkConst<Rational>(2 * actual_d),
+                        tf[0],
+                        lower,
+                        upper,
+                        lapprox,
+                        uapprox
+
+                       });
+      }
+      else
+      {
+        proof->addStep(lem,
+                       PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_NEG,
+                       {},
+                       {nm->mkConst<Rational>(2 * actual_d),
+                        tf[0],
+                        lower,
+                        upper,
+                        lapprox,
+                        uapprox});
+      }
+    }
+  }
+  return NlLemma(
+      InferenceId::ARITH_NL_T_SECANT, lem, LemmaProperty::NONE, proof);
 }
 
 void TranscendentalState::doSecantLemmas(const std::pair<Node, Node>& bounds,