(proof-new) Add proofs for sine lemmas in the transcendental solver (#5952)
authorGereon Kremer <gereon.kremer@cs.rwth-aachen.de>
Mon, 22 Feb 2021 16:25:38 +0000 (17:25 +0100)
committerGitHub <noreply@github.com>
Mon, 22 Feb 2021 16:25:38 +0000 (17:25 +0100)
This PR adds proofs for the lemmas related to the sine function in the transcendental solver.
It introduces several new proof rules with corresponding proof checkers and produces proofs in the sine solver.

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

index 4fce88a391ea6fcd6401d0e4975be798636cc6ce..a5dde78ab2ca58553f27d39cfa11b81032a2eaee 100644 (file)
@@ -162,7 +162,21 @@ 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_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";
+    case PfRule::ARITH_TRANS_SINE_TANGENT_ZERO:
+      return "ARITH_TRANS_SINE_TANGENT_ZERO";
+    case PfRule::ARITH_TRANS_SINE_TANGENT_PI:
+      return "ARITH_TRANS_SINE_TANGENT_PI";
+    case PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_NEG:
+      return "ARITH_TRANS_SINE_APPROX_ABOVE_NEG";
+    case PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS:
+      return "ARITH_TRANS_SINE_APPROX_ABOVE_POS";
+    case PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG:
+      return "ARITH_TRANS_SINE_APPROX_BELOW_NEG";
+    case PfRule::ARITH_TRANS_SINE_APPROX_BELOW_POS:
+      return "ARITH_TRANS_SINE_APPROX_BELOW_POS";
     //================================================= Unknown rule
     case PfRule::UNKNOWN: return "UNKNOWN";
     default: return "?";
index 9b7a54a09d040a9d7baa3af2a1a47b13c1712765..28ea2453392414a8037578753a39eea23e4e304f 100644 (file)
@@ -1112,6 +1112,13 @@ enum class PfRule : uint32_t
   // Conclusion: (and (>= real.pi l) (<= real.pi u))
   // Where l (u) is a valid lower (upper) bound on pi.
   ARITH_TRANS_PI,
+
+  //======== Sine is always between -1 and 1
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (and (<= (sin t) 1) (>= (sin t) (- 1)))
+  ARITH_TRANS_SINE_BOUNDS,
   //======== Sine arg shifted to -pi..pi
   // Children: none
   // Arguments: (x, y, s)
@@ -1125,6 +1132,84 @@ enum class PfRule : uint32_t
   // into -pi..pi and s is a new integer skolem that is the number of phases y
   // is shifted.
   ARITH_TRANS_SINE_SHIFT,
+  //======== Sine is symmetric with respect to negation of the argument
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (= (- (sin t) (sin (- t))) 0)
+  ARITH_TRANS_SINE_SYMMETRY,
+  //======== Sine is bounded by the tangent at zero
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (and
+  //   (=> (> t 0) (< (sin t) t))
+  //   (=> (< t 0) (> (sin t) t))
+  // )
+  ARITH_TRANS_SINE_TANGENT_ZERO,
+  //======== Sine is bounded by the tangents at -pi and pi
+  // Children: none
+  // Arguments: (t)
+  // ---------------------
+  // Conclusion: (and
+  //   (=> (> t -pi) (> (sin t) (- -pi t)))
+  //   (=> (< t pi) (< (sin t) (- pi t)))
+  // )
+  ARITH_TRANS_SINE_TANGENT_PI,
+  //======== Sine is approximated from above for negative values
+  // Children: none
+  // Arguments: (d, t, lb, ub, l, u)
+  // ---------------------
+  // Conclusion: (=> (and (>= t lb) (<= t ub)) (<= (sin t) (secant sin l u t))
+  // Where d is an even positive number, t an arithmetic term, lb (ub) a
+  // symbolic lower (upper) bound on t (possibly containing pi) and l (u) the
+  // evaluated lower (upper) bound on t. Let p be the d'th taylor polynomial at
+  // zero (also called the Maclaurin series) of the sine function. (secant sin l
+  // u t) denotes the secant of p from (l, sin(l)) to (u, sin(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 (sin t) is below the
+  // secant of p from l to u.
+  ARITH_TRANS_SINE_APPROX_ABOVE_NEG,
+  //======== Sine is approximated from above for positive values
+  // Children: none
+  // Arguments: (d, t, c, lb, ub)
+  // ---------------------
+  // Conclusion: (=> (and (>= t lb) (<= t ub)) (<= (sin t) (upper sin c))
+  // Where d is an even positive number, t an arithmetic term, c an arithmetic
+  // constant, and lb (ub) a symbolic lower (upper) bound on t (possibly
+  // containing pi). Let p be the d'th taylor polynomial at zero (also called
+  // the Maclaurin series) of the sine function. (upper sin c) denotes the upper
+  // bound on sin(c) given by p and lb,ub are such that sin(t) is the maximum of
+  // the sine function on (lb,ub).
+  ARITH_TRANS_SINE_APPROX_ABOVE_POS,
+  //======== Sine is approximated from below for negative values
+  // Children: none
+  // Arguments: (d, t, c, lb, ub)
+  // ---------------------
+  // Conclusion: (=> (and (>= t lb) (<= t ub)) (>= (sin t) (lower sin c))
+  // Where d is an even positive number, t an arithmetic term, c an arithmetic
+  // constant, and lb (ub) a symbolic lower (upper) bound on t (possibly
+  // containing pi). Let p be the d'th taylor polynomial at zero (also called
+  // the Maclaurin series) of the sine function. (lower sin c) denotes the lower
+  // bound on sin(c) given by p and lb,ub are such that sin(t) is the minimum of
+  // the sine function on (lb,ub).
+  ARITH_TRANS_SINE_APPROX_BELOW_NEG,
+  //======== Sine is approximated from below for positive values
+  // Children: none
+  // Arguments: (d, t, lb, ub, l, u)
+  // ---------------------
+  // Conclusion: (=> (and (>= t lb) (<= t ub)) (>= (sin t) (secant sin l u t))
+  // Where d is an even positive number, t an arithmetic term, lb (ub) a
+  // symbolic lower (upper) bound on t (possibly containing pi) and l (u) the
+  // evaluated lower (upper) bound on t. Let p be the d'th taylor polynomial at
+  // zero (also called the Maclaurin series) of the sine function. (secant sin l
+  // u t) denotes the secant of p from (l, sin(l)) to (u, sin(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 (sin t) is above the
+  // secant of p from l to u.
+  ARITH_TRANS_SINE_APPROX_BELOW_POS,
 
   //================================================= Unknown rule
   UNKNOWN,
index b5fccac850f51792bae2e8feafaaba9cad77c755..bec2bd9b84450dbd28af64b8f75a70ead287de06 100644 (file)
@@ -27,10 +27,47 @@ namespace arith {
 namespace nl {
 namespace transcendental {
 
+namespace {
+
+/**
+ * Helper method to construct (t >= lb) AND (t <= up)
+ */
+Node mkBounds(TNode t, TNode lb, TNode ub)
+{
+  NodeManager* nm = NodeManager::currentNM();
+  return nm->mkAnd(std::vector<Node>{nm->mkNode(Kind::GEQ, t, lb),
+                                     nm->mkNode(Kind::LEQ, t, ub)});
+}
+
+/**
+ * Helper method to construct a secant plane:
+ * ((evall - evalu) / (l - u)) * (t - l) + evall
+ */
+Node mkSecant(TNode t, TNode l, TNode u, TNode evall, TNode evalu)
+{
+  NodeManager* nm = NodeManager::currentNM();
+  return nm->mkNode(Kind::PLUS,
+                    nm->mkNode(Kind::MULT,
+                               nm->mkNode(Kind::DIVISION,
+                                          nm->mkNode(Kind::MINUS, evall, evalu),
+                                          nm->mkNode(Kind::MINUS, l, u)),
+                               nm->mkNode(Kind::MINUS, t, l)),
+                    evall);
+}
+
+}  // namespace
+
 void TranscendentalProofRuleChecker::registerTo(ProofChecker* pc)
 {
   pc->registerChecker(PfRule::ARITH_TRANS_PI, 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);
+  pc->registerChecker(PfRule::ARITH_TRANS_SINE_TANGENT_PI, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_SINE_APPROX_BELOW_POS, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS, this);
+  pc->registerChecker(PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_NEG, this);
 }
 
 Node TranscendentalProofRuleChecker::checkInternal(
@@ -53,7 +90,16 @@ Node TranscendentalProofRuleChecker::checkInternal(
     Assert(args.size() == 2);
     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_SINE_SHIFT)
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_BOUNDS)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Assert(args[0].getType().isReal());
+    Node s = nm->mkNode(Kind::SINE, args[0]);
+    return nm->mkNode(AND, nm->mkNode(LEQ, s, one), nm->mkNode(GEQ, s, mone));
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_SHIFT)
   {
     Assert(children.empty());
     Assert(args.size() == 3);
@@ -77,6 +123,159 @@ Node TranscendentalProofRuleChecker::checkInternal(
                 nm->mkNode(Kind::MULT, nm->mkConst<Rational>(2), s, pi)))),
         nm->mkNode(Kind::SINE, y).eqNode(nm->mkNode(Kind::SINE, x))});
   }
+  else if (id == PfRule::ARITH_TRANS_SINE_SYMMETRY)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Assert(args[0].getType().isReal());
+    Node s1 = nm->mkNode(Kind::SINE, args[0]);
+    Node s2 = nm->mkNode(
+        Kind::SINE, Rewriter::rewrite(nm->mkNode(Kind::MULT, mone, args[0])));
+    return nm->mkNode(PLUS, s1, s2).eqNode(zero);
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_TANGENT_ZERO)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Assert(args[0].getType().isReal());
+    Node s = nm->mkNode(Kind::SINE, args[0]);
+    return nm->mkNode(
+        AND,
+        nm->mkNode(
+            IMPLIES, nm->mkNode(GT, args[0], zero), nm->mkNode(LT, s, args[0])),
+        nm->mkNode(IMPLIES,
+                   nm->mkNode(LT, args[0], zero),
+                   nm->mkNode(GT, s, args[0])));
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_TANGENT_PI)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 1);
+    Assert(args[0].getType().isReal());
+    Node s = nm->mkNode(Kind::SINE, args[0]);
+    return nm->mkNode(
+        AND,
+        nm->mkNode(IMPLIES,
+                   nm->mkNode(GT, args[0], mpi),
+                   nm->mkNode(GT, s, nm->mkNode(MINUS, mpi, args[0]))),
+        nm->mkNode(IMPLIES,
+                   nm->mkNode(LT, args[0], pi),
+                   nm->mkNode(LT, s, nm->mkNode(MINUS, pi, args[0]))));
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_NEG)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 6);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    Assert(args[2].getType().isReal());
+    Assert(args[3].getType().isReal());
+    Assert(args[4].isConst() && args[4].getKind() == Kind::CONST_RATIONAL);
+    Assert(args[5].isConst() && args[5].getKind() == Kind::CONST_RATIONAL);
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    Node lb = args[2];
+    Node ub = args[3];
+    Node l = args[4];
+    Node u = args[5];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::SINE, 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 lem = nm->mkNode(
+        Kind::IMPLIES,
+        mkBounds(t, lb, ub),
+        nm->mkNode(
+            Kind::LEQ, nm->mkNode(Kind::SINE, t), mkSecant(t, lb, ub, l, u)));
+    return Rewriter::rewrite(lem);
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 5);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    Assert(args[2].getType().isReal());
+    Assert(args[3].getType().isReal());
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    Node c = args[2];
+    Node lb = args[3];
+    Node ub = args[4];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::SINE, d / 2, bounds);
+    Node eval = Rewriter::rewrite(
+        bounds.d_upperPos.substitute(tg.getTaylorVariable(), c));
+    return Rewriter::rewrite(
+        nm->mkNode(Kind::IMPLIES,
+                   mkBounds(t, lb, ub),
+                   nm->mkNode(Kind::LEQ, nm->mkNode(Kind::SINE, t), eval)));
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_APPROX_BELOW_POS)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 6);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    Assert(args[2].getType().isReal());
+    Assert(args[3].getType().isReal());
+    Assert(args[4].isConst() && args[4].getKind() == Kind::CONST_RATIONAL);
+    Assert(args[5].isConst() && args[5].getKind() == Kind::CONST_RATIONAL);
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    Node lb = args[2];
+    Node ub = args[3];
+    Node l = args[4];
+    Node u = args[5];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::SINE, d / 2, bounds);
+    Node evall =
+        Rewriter::rewrite(bounds.d_lower.substitute(tg.getTaylorVariable(), l));
+    Node evalu =
+        Rewriter::rewrite(bounds.d_lower.substitute(tg.getTaylorVariable(), u));
+    Node lem = nm->mkNode(
+        Kind::IMPLIES,
+        mkBounds(t, lb, ub),
+        nm->mkNode(
+            Kind::GEQ, nm->mkNode(Kind::SINE, t), mkSecant(t, lb, ub, l, u)));
+    return Rewriter::rewrite(lem);
+  }
+  else if (id == PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG)
+  {
+    Assert(children.empty());
+    Assert(args.size() == 5);
+    Assert(args[0].isConst() && args[0].getKind() == Kind::CONST_RATIONAL
+           && args[0].getConst<Rational>().isIntegral());
+    Assert(args[1].getType().isReal());
+    Assert(args[2].getType().isReal());
+    Assert(args[3].getType().isReal());
+    std::uint64_t d =
+        args[0].getConst<Rational>().getNumerator().toUnsignedInt();
+    Node t = args[1];
+    Node c = args[2];
+    Node lb = args[3];
+    Node ub = args[4];
+    TaylorGenerator tg;
+    TaylorGenerator::ApproximationBounds bounds;
+    tg.getPolynomialApproximationBounds(Kind::SINE, d / 2, bounds);
+    Node eval =
+        Rewriter::rewrite(bounds.d_lower.substitute(tg.getTaylorVariable(), c));
+    return Rewriter::rewrite(
+        nm->mkNode(Kind::IMPLIES,
+                   mkBounds(t, lb, ub),
+                   nm->mkNode(Kind::GEQ, nm->mkNode(Kind::SINE, t), eval)));
+  }
   return Node::null();
 }
 
index 0cca238b0006d9c5c39c393e7afae481f5f3dd9d..f5dc49da804e27d813ec1b015dadf818e88f2259 100644 (file)
@@ -114,14 +114,26 @@ void SineSolver::checkInitialRefine()
           Node lem = nm->mkNode(Kind::AND,
                                 nm->mkNode(Kind::LEQ, t, d_data->d_one),
                                 nm->mkNode(Kind::GEQ, t, d_data->d_neg_one));
-          d_data->d_im.addPendingLemma(lem,
-                                       InferenceId::ARITH_NL_T_INIT_REFINE);
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(lem, PfRule::ARITH_TRANS_SINE_BOUNDS, {}, {t[0]});
+          }
+          d_data->d_im.addPendingLemma(
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           // sine symmetry: sin(t) - sin(-t) = 0
           Node lem = nm->mkNode(Kind::PLUS, t, symn).eqNode(d_data->d_zero);
-          d_data->d_im.addPendingLemma(lem,
-                                       InferenceId::ARITH_NL_T_INIT_REFINE);
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(lem, PfRule::ARITH_TRANS_SINE_SYMMETRY, {}, {t[0]});
+          }
+          d_data->d_im.addPendingLemma(
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           // sine zero tangent:
@@ -135,8 +147,15 @@ void SineSolver::checkInitialRefine()
                          nm->mkNode(Kind::IMPLIES,
                                     nm->mkNode(Kind::LT, t[0], d_data->d_zero),
                                     nm->mkNode(Kind::GT, t, t[0])));
-          d_data->d_im.addPendingLemma(lem,
-                                       InferenceId::ARITH_NL_T_INIT_REFINE);
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(
+                lem, PfRule::ARITH_TRANS_SINE_TANGENT_ZERO, {}, {t[0]});
+          }
+          d_data->d_im.addPendingLemma(
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           // sine pi tangent:
@@ -156,8 +175,15 @@ void SineSolver::checkInitialRefine()
                   nm->mkNode(Kind::LT,
                              t,
                              nm->mkNode(Kind::MINUS, d_data->d_pi, t[0]))));
-          d_data->d_im.addPendingLemma(lem,
-                                       InferenceId::ARITH_NL_T_INIT_REFINE);
+          CDProof* proof = nullptr;
+          if (d_data->isProofEnabled())
+          {
+            proof = d_data->getProof();
+            proof->addStep(
+                lem, PfRule::ARITH_TRANS_SINE_TANGENT_PI, {}, {t[0]});
+          }
+          d_data->d_im.addPendingLemma(
+              lem, InferenceId::ARITH_NL_T_INIT_REFINE, proof);
         }
         {
           Node lem =
@@ -322,7 +348,8 @@ void SineSolver::checkMonotonic()
   }
 }
 
-void SineSolver::doTangentLemma(TNode e, TNode c, TNode poly_approx, int region)
+void SineSolver::doTangentLemma(
+    TNode e, TNode c, TNode poly_approx, int region, std::uint64_t d)
 {
   NodeManager* nm = NodeManager::currentNM();
 
@@ -338,7 +365,7 @@ void SineSolver::doTangentLemma(TNode e, TNode c, TNode poly_approx, int region)
       nm->mkNode(
           Kind::AND,
           nm->mkNode(
-              Kind::GEQ, e[0], usec ? Node(c) : regionToLowerBound(region)),
+              Kind::GEQ, e[0], usec ? regionToLowerBound(region) : Node(c)),
           nm->mkNode(
               Kind::LEQ, e[0], usec ? Node(c) : regionToUpperBound(region))),
       nm->mkNode(convexity == Convexity::CONVEX ? Kind::GEQ : Kind::LEQ,
@@ -351,8 +378,63 @@ void SineSolver::doTangentLemma(TNode e, TNode c, TNode poly_approx, int region)
   Trace("nl-ext-sine") << "*** 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();
+    if (convexity == Convexity::CONVEX)
+    {
+      if (usec)
+      {
+        proof->addStep(lem,
+                       PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG,
+                       {},
+                       {nm->mkConst<Rational>(2 * d),
+                        e[0],
+                        c,
+                        regionToLowerBound(region),
+                        c});
+      }
+      else
+      {
+        proof->addStep(lem,
+                       PfRule::ARITH_TRANS_SINE_APPROX_BELOW_NEG,
+                       {},
+                       {nm->mkConst<Rational>(2 * d),
+                        e[0],
+                        c,
+                        c,
+                        regionToUpperBound(region)});
+      }
+    }
+    else
+    {
+      if (usec)
+      {
+        proof->addStep(lem,
+                       PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS,
+                       {},
+                       {nm->mkConst<Rational>(2 * d),
+                        e[0],
+                        c,
+                        regionToLowerBound(region),
+                        c});
+      }
+      else
+      {
+        proof->addStep(lem,
+                       PfRule::ARITH_TRANS_SINE_APPROX_ABOVE_POS,
+                       {},
+                       {nm->mkConst<Rational>(2 * d),
+                        e[0],
+                        c,
+                        c,
+                        regionToUpperBound(region)});
+      }
+    }
+  }
   d_data->d_im.addPendingLemma(
-      lem, InferenceId::ARITH_NL_T_TANGENT, nullptr, true);
+      lem, InferenceId::ARITH_NL_T_TANGENT, proof, true);
 }
 
 void SineSolver::doSecantLemmas(TNode e,
index 5eace6104d773f7de37255fbb68ae7d8be28929b..da4b48fd6a162f7167f0ca34c90d43667200f96c 100644 (file)
@@ -87,7 +87,8 @@ class SineSolver
   void checkMonotonic();
 
   /** Sent tangent lemma around c for e */
-  void doTangentLemma(TNode e, TNode c, TNode poly_approx, int region);
+  void doTangentLemma(
+      TNode e, TNode c, TNode poly_approx, int region, std::uint64_t d);
 
   /** Sent secant lemmas around c for e */
   void doSecantLemmas(TNode e,
index ee422ebb68fa770163663d3a4c80f7fda073b9a4..7b87adab17a8f90b435597c0b94018d669e05741 100644 (file)
@@ -391,7 +391,7 @@ bool TranscendentalSolver::checkTfTangentPlanesFun(Node tf, unsigned d)
     }
     else if (k == Kind::SINE)
     {
-      d_sineSlv.doTangentLemma(tf, c, poly_approx_c, region);
+      d_sineSlv.doTangentLemma(tf, c, poly_approx_c, region, d);
     }
   }
   else if (is_secant)