(proof-new) Add proofs for exponential functions (#5956)
authorGereon Kremer <gereon.kremer@cs.rwth-aachen.de>
Mon, 22 Feb 2021 21:27:30 +0000 (22:27 +0100)
committerGitHub <noreply@github.com>
Mon, 22 Feb 2021 21:27:30 +0000 (15:27 -0600)
This PR adds proofs for lemmas concerned with the exponential function. If also adds the necessary proof rules and corresponding proof checker.

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

index a5dde78ab2ca58553f27d39cfa11b81032a2eaee..110d9467e9c4b0ee42af6a603e56f486a0277fa9 100644 (file)
@@ -162,6 +162,13 @@ const char* toString(PfRule id)
     case PfRule::ARITH_MULT_TANGENT: return "ARITH_MULT_TANGENT";
     case PfRule::ARITH_OP_ELIM_AXIOM: return "ARITH_OP_ELIM_AXIOM";
     case PfRule::ARITH_TRANS_PI: return "ARITH_TRANS_PI";
+    case PfRule::ARITH_TRANS_EXP_NEG: return "ARITH_TRANS_EXP_NEG";
+    case PfRule::ARITH_TRANS_EXP_POSITIVITY:
+      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_BELOW:
+      return "ARITH_TRANS_EXP_APPROX_BELOW";
     case PfRule::ARITH_TRANS_SINE_BOUNDS: return "ARITH_TRANS_SINE_BOUNDS";
     case PfRule::ARITH_TRANS_SINE_SHIFT: return "ARITH_TRANS_SINE_SHIFT";
     case PfRule::ARITH_TRANS_SINE_SYMMETRY: return "ARITH_TRANS_SINE_SYMMETRY";
index 28ea2453392414a8037578753a39eea23e4e304f..10801648f5cd81e9ee35d06ecfd4c25f3745d0f4 100644 (file)
@@ -1113,6 +1113,42 @@ enum class PfRule : uint32_t
   // Where l (u) is a valid lower (upper) bound on pi.
   ARITH_TRANS_PI,
 
+  //======== Exp at negative values
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (= (< t 0) (< (exp t) 1))
+  ARITH_TRANS_EXP_NEG,
+  //======== Exp is always positive
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (> (exp t) 0)
+  ARITH_TRANS_EXP_POSITIVITY,
+  //======== Exp grows super-linearly for positive values
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (or (<= t 0) (> exp(t) (+ t 1)))
+  ARITH_TRANS_EXP_SUPER_LIN,
+  //======== Exp at zero
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (= (= t 0) (= (exp t) 1))
+  ARITH_TRANS_EXP_ZERO,
+  //======== Exp is approximated from below
+  // Children: none
+  // Arguments: (d, t)
+  // ---------------------
+  // Conclusion: (>= (exp t) (maclaurin exp d t))
+  // Where d is an odd positive number and (maclaurin exp d t) is the d'th
+  // taylor polynomial at zero (also called the Maclaurin series) of the
+  // exponential function evaluated at t. The Maclaurin series for the
+  // exponential function is the following:
+  //   e^x = \sum_{n=0}^{\infty} x^n / n!
+  ARITH_TRANS_EXP_APPROX_BELOW,
+
   //======== Sine is always between -1 and 1
   // Children: none
   // Arguments: (t)
index 7f9d98fe1c1152f4cb810f6e3d3df2088d655f54..86b17376cbf1a2d0c5a72f79251d2b54e1a62058 100644 (file)
@@ -70,24 +70,42 @@ void ExponentialSolver::checkInitialRefine()
         {
           // exp is always positive: exp(t) > 0
           Node lem = nm->mkNode(Kind::GT, t, d_data->d_zero);
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(lem, PfRule::ARITH_TRANS_EXP_POSITIVITY, {}, {t[0]});
+          }
           d_data->d_im.addPendingLemma(
-              lem, InferenceId::ARITH_NL_T_INIT_REFINE, nullptr);
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           // exp at zero: (t = 0) <=> (exp(t) = 1)
           Node lem = nm->mkNode(Kind::EQUAL,
                                 t[0].eqNode(d_data->d_zero),
                                 t.eqNode(d_data->d_one));
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(lem, PfRule::ARITH_TRANS_EXP_ZERO, {}, {t[0]});
+          }
           d_data->d_im.addPendingLemma(
-              lem, InferenceId::ARITH_NL_T_INIT_REFINE, nullptr);
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           // exp on negative values: (t < 0) <=> (exp(t) < 1)
           Node lem = nm->mkNode(Kind::EQUAL,
                                 nm->mkNode(Kind::LT, t[0], d_data->d_zero),
                                 nm->mkNode(Kind::LT, t, d_data->d_one));
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(lem, PfRule::ARITH_TRANS_EXP_NEG, {}, {t[0]});
+          }
           d_data->d_im.addPendingLemma(
-              lem, InferenceId::ARITH_NL_T_INIT_REFINE, nullptr);
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           // exp on positive values: (t <= 0) or (exp(t) > t+1)
@@ -96,8 +114,14 @@ void ExponentialSolver::checkInitialRefine()
               nm->mkNode(Kind::LEQ, t[0], d_data->d_zero),
               nm->mkNode(
                   Kind::GT, t, nm->mkNode(Kind::PLUS, t[0], d_data->d_one)));
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(lem, PfRule::ARITH_TRANS_EXP_SUPER_LIN, {}, {t[0]});
+          }
           d_data->d_im.addPendingLemma(
-              lem, InferenceId::ARITH_NL_T_INIT_REFINE, nullptr);
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
       }
     }
@@ -172,7 +196,10 @@ void ExponentialSolver::checkMonotonic()
   }
 }
 
-void ExponentialSolver::doTangentLemma(TNode e, TNode c, TNode poly_approx)
+void ExponentialSolver::doTangentLemma(TNode e,
+                                       TNode c,
+                                       TNode poly_approx,
+                                       std::uint64_t d)
 {
   NodeManager* nm = NodeManager::currentNM();
   // compute tangent plane
@@ -189,8 +216,17 @@ void ExponentialSolver::doTangentLemma(TNode e, TNode c, TNode poly_approx)
   Trace("nl-ext-exp") << "*** Tangent plane lemma : " << lem << std::endl;
   Assert(d_data->d_model.computeAbstractModelValue(lem) == d_data->d_false);
   // Figure 3 : line 9
+  CDProof* proof = nullptr;
+  if (d_data->isProofEnabled())
+  {
+    proof = d_data->getProof();
+    proof->addStep(lem,
+                   PfRule::ARITH_TRANS_EXP_APPROX_BELOW,
+                   {},
+                   {nm->mkConst<Rational>(d), e[0]});
+  }
   d_data->d_im.addPendingLemma(
-      lem, InferenceId::ARITH_NL_T_TANGENT, nullptr, true);
+      lem, InferenceId::ARITH_NL_T_TANGENT, proof, true);
 }
 
 void ExponentialSolver::doSecantLemmas(TNode e,
index 1fad896e4cafc6bf3c8653306feddf9ec44bae0e..b874e1a4b10c20204893e4c0e326bab791b4518b 100644 (file)
@@ -85,10 +85,10 @@ class ExponentialSolver
    */
   void checkMonotonic();
 
-  /** Sent tangent lemma around c for e */
-  void doTangentLemma(TNode e, TNode c, TNode poly_approx);
+  /** Send tangent lemma around c for e */
+  void doTangentLemma(TNode e, TNode c, TNode poly_approx, std::uint64_t d);
 
-  /** Sent secant lemmas around c for e */
+  /** Send secant lemmas around c for e */
   void doSecantLemmas(TNode e,
                       TNode poly_approx,
                       TNode center,
index bec2bd9b84450dbd28af64b8f75a70ead287de06..6dc2df2015d07b8d3660b50218e5a76ac861f024 100644 (file)
@@ -60,6 +60,11 @@ Node mkSecant(TNode t, TNode l, TNode u, TNode evall, TNode evalu)
 void TranscendentalProofRuleChecker::registerTo(ProofChecker* pc)
 {
   pc->registerChecker(PfRule::ARITH_TRANS_PI, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_EXP_NEG, this);
+  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_BELOW, 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);
@@ -80,10 +85,16 @@ Node TranscendentalProofRuleChecker::checkInternal(
   auto pi = nm->mkNullaryOperator(nm->realType(), Kind::PI);
   auto mpi = nm->mkNode(Kind::MULT, mone, pi);
   Trace("nl-trans-checker") << "Checking " << id << std::endl;
+  Trace("nl-trans-checker") << "Children:" << std::endl;
   for (const auto& c : children)
   {
     Trace("nl-trans-checker") << "\t" << c << std::endl;
   }
+  Trace("nl-trans-checker") << "Args:" << std::endl;
+  for (const auto& a : args)
+  {
+    Trace("nl-trans-checker") << "\t" << a << std::endl;
+  }
   if (id == PfRule::ARITH_TRANS_PI)
   {
     Assert(children.empty());
@@ -91,6 +102,55 @@ Node TranscendentalProofRuleChecker::checkInternal(
     return nm->mkAnd(std::vector<Node>{nm->mkNode(Kind::GEQ, pi, args[0]),
                                        nm->mkNode(Kind::LEQ, pi, args[1])});
   }
+  else if (id == PfRule::ARITH_TRANS_EXP_NEG)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Node e = nm->mkNode(Kind::EXPONENTIAL, args[0]);
+    return nm->mkNode(
+        EQUAL, nm->mkNode(LT, args[0], zero), nm->mkNode(LT, e, one));
+  }
+  else if (id == PfRule::ARITH_TRANS_EXP_POSITIVITY)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Node e = nm->mkNode(Kind::EXPONENTIAL, args[0]);
+    return nm->mkNode(GT, e, zero);
+  }
+  else if (id == PfRule::ARITH_TRANS_EXP_SUPER_LIN)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Node e = nm->mkNode(Kind::EXPONENTIAL, args[0]);
+    return nm->mkNode(OR,
+                      nm->mkNode(LEQ, args[0], zero),
+                      nm->mkNode(GT, e, nm->mkNode(PLUS, args[0], one)));
+  }
+  else if (id == PfRule::ARITH_TRANS_EXP_ZERO)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    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_BELOW)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 2);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::EXPONENTIAL, d, bounds);
+    Node eval =
+        Rewriter::rewrite(bounds.d_lower.substitute(tg.getTaylorVariable(), t));
+    return nm->mkNode(
+        Kind::GEQ, std::vector<Node>{nm->mkNode(Kind::EXPONENTIAL, t), eval});
+  }
   else if (id == PfRule::ARITH_TRANS_SINE_BOUNDS)
   {
     Assert(children.empty());
index 7b87adab17a8f90b435597c0b94018d669e05741..0548c6a4164d238564adfcf73a56bd627b392bc8 100644 (file)
@@ -387,7 +387,7 @@ bool TranscendentalSolver::checkTfTangentPlanesFun(Node tf, unsigned d)
   {
     if (k == Kind::EXPONENTIAL)
     {
-      d_expSlv.doTangentLemma(tf, c, poly_approx_c);
+      d_expSlv.doTangentLemma(tf, c, poly_approx_c, d);
     }
     else if (k == Kind::SINE)
     {