1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Gereon Kremer, Andrew Reynolds |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Generate taylor approximations transcendental lemmas. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/arith/nl/transcendental/taylor_generator.h" |
17 |
|
|
18 |
|
#include "theory/arith/arith_utilities.h" |
19 |
|
#include "theory/arith/nl/nl_model.h" |
20 |
|
#include "theory/rewriter.h" |
21 |
|
|
22 |
|
namespace cvc5 { |
23 |
|
namespace theory { |
24 |
|
namespace arith { |
25 |
|
namespace nl { |
26 |
|
namespace transcendental { |
27 |
|
|
28 |
4981 |
TaylorGenerator::TaylorGenerator() |
29 |
4981 |
: d_nm(NodeManager::currentNM()), |
30 |
4981 |
d_taylor_real_fv(d_nm->mkBoundVar("x", d_nm->realType())) |
31 |
|
{ |
32 |
4981 |
} |
33 |
|
|
34 |
3351 |
TNode TaylorGenerator::getTaylorVariable() { return d_taylor_real_fv; } |
35 |
|
|
36 |
3496 |
std::pair<Node, Node> TaylorGenerator::getTaylor(Kind k, std::uint64_t n) |
37 |
|
{ |
38 |
3496 |
Assert(n > 0); |
39 |
|
// check if we have already computed this Taylor series |
40 |
3496 |
auto itt = d_taylor_terms[k].find(n); |
41 |
3496 |
if (itt != d_taylor_terms[k].end()) |
42 |
|
{ |
43 |
3254 |
return itt->second; |
44 |
|
} |
45 |
|
|
46 |
242 |
NodeManager* nm = NodeManager::currentNM(); |
47 |
|
|
48 |
|
// the current factorial `counter!` |
49 |
484 |
Integer factorial = 1; |
50 |
|
// the current variable power `x^counter` |
51 |
484 |
Node varpow = nm->mkConst(Rational(1)); |
52 |
484 |
std::vector<Node> sum; |
53 |
1642 |
for (std::uint64_t counter = 1; counter <= n; ++counter) |
54 |
|
{ |
55 |
1400 |
if (k == Kind::EXPONENTIAL) |
56 |
|
{ |
57 |
|
// Maclaurin series for exponential: |
58 |
|
// \sum_{n=0}^\infty x^n / n! |
59 |
788 |
sum.push_back( |
60 |
1576 |
nm->mkNode(Kind::DIVISION, varpow, nm->mkConst<Rational>(factorial))); |
61 |
|
} |
62 |
612 |
else if (k == Kind::SINE) |
63 |
|
{ |
64 |
|
// Maclaurin series for exponential: |
65 |
|
// \sum_{n=0}^\infty (-1)^n / (2n+1)! * x^(2n+1) |
66 |
612 |
if (counter % 2 == 0) |
67 |
|
{ |
68 |
306 |
int sign = (counter % 4 == 0 ? -1 : 1); |
69 |
918 |
sum.push_back(nm->mkNode(Kind::MULT, |
70 |
1224 |
nm->mkNode(Kind::DIVISION, |
71 |
612 |
nm->mkConst<Rational>(sign), |
72 |
612 |
nm->mkConst<Rational>(factorial)), |
73 |
|
varpow)); |
74 |
|
} |
75 |
|
} |
76 |
1400 |
factorial *= counter; |
77 |
1400 |
varpow = |
78 |
2800 |
Rewriter::rewrite(nm->mkNode(Kind::MULT, d_taylor_real_fv, varpow)); |
79 |
|
} |
80 |
|
Node taylor_sum = |
81 |
484 |
Rewriter::rewrite(sum.size() == 1 ? sum[0] : nm->mkNode(Kind::PLUS, sum)); |
82 |
|
Node taylor_rem = Rewriter::rewrite( |
83 |
484 |
nm->mkNode(Kind::DIVISION, varpow, nm->mkConst<Rational>(factorial))); |
84 |
|
|
85 |
484 |
auto res = std::make_pair(taylor_sum, taylor_rem); |
86 |
|
|
87 |
|
// put result in cache |
88 |
242 |
d_taylor_terms[k][n] = res; |
89 |
|
|
90 |
242 |
return res; |
91 |
|
} |
92 |
|
|
93 |
3240 |
void TaylorGenerator::getPolynomialApproximationBounds( |
94 |
|
Kind k, std::uint64_t d, ApproximationBounds& pbounds) |
95 |
|
{ |
96 |
3240 |
auto it = d_poly_bounds[k].find(d); |
97 |
3240 |
if (it == d_poly_bounds[k].end()) |
98 |
|
{ |
99 |
242 |
NodeManager* nm = NodeManager::currentNM(); |
100 |
|
// n is the Taylor degree we are currently considering |
101 |
242 |
std::uint64_t n = 2 * d; |
102 |
|
// n must be even |
103 |
484 |
std::pair<Node, Node> taylor = getTaylor(k, n); |
104 |
484 |
Node taylor_sum = taylor.first; |
105 |
|
// ru is x^{n+1}/(n+1)! |
106 |
484 |
Node ru = taylor.second; |
107 |
484 |
Trace("nl-trans") << "Taylor for " << k << " is : " << taylor.first |
108 |
242 |
<< std::endl; |
109 |
484 |
Trace("nl-trans") << "Taylor remainder for " << k << " is " << taylor.second |
110 |
242 |
<< std::endl; |
111 |
242 |
if (k == Kind::EXPONENTIAL) |
112 |
|
{ |
113 |
130 |
pbounds.d_lower = taylor_sum; |
114 |
130 |
pbounds.d_upperNeg = |
115 |
260 |
Rewriter::rewrite(nm->mkNode(Kind::PLUS, taylor_sum, ru)); |
116 |
260 |
pbounds.d_upperPos = Rewriter::rewrite( |
117 |
260 |
nm->mkNode(Kind::MULT, |
118 |
|
taylor_sum, |
119 |
390 |
nm->mkNode(Kind::PLUS, nm->mkConst(Rational(1)), ru))); |
120 |
|
} |
121 |
|
else |
122 |
|
{ |
123 |
112 |
Assert(k == Kind::SINE); |
124 |
224 |
Node l = Rewriter::rewrite(nm->mkNode(Kind::MINUS, taylor_sum, ru)); |
125 |
224 |
Node u = Rewriter::rewrite(nm->mkNode(Kind::PLUS, taylor_sum, ru)); |
126 |
112 |
pbounds.d_lower = l; |
127 |
112 |
pbounds.d_upperNeg = u; |
128 |
112 |
pbounds.d_upperPos = u; |
129 |
|
} |
130 |
484 |
Trace("nl-trans") << "Polynomial approximation for " << k |
131 |
242 |
<< " is: " << std::endl; |
132 |
242 |
Trace("nl-trans") << " Lower: " << pbounds.d_lower << std::endl; |
133 |
242 |
Trace("nl-trans") << " Upper (neg): " << pbounds.d_upperNeg << std::endl; |
134 |
242 |
Trace("nl-trans") << " Upper (pos): " << pbounds.d_upperPos << std::endl; |
135 |
242 |
d_poly_bounds[k].emplace(d, pbounds); |
136 |
|
} |
137 |
|
else |
138 |
|
{ |
139 |
2998 |
pbounds = it->second; |
140 |
|
} |
141 |
3240 |
} |
142 |
|
|
143 |
2533 |
std::uint64_t TaylorGenerator::getPolynomialApproximationBoundForArg( |
144 |
|
Kind k, Node c, std::uint64_t d, ApproximationBounds& pbounds) |
145 |
|
{ |
146 |
2533 |
getPolynomialApproximationBounds(k, d, pbounds); |
147 |
2533 |
Trace("nl-trans") << "c = " << c << std::endl; |
148 |
2533 |
Assert(c.isConst()); |
149 |
2533 |
if (k == Kind::EXPONENTIAL && c.getConst<Rational>().sgn() == 1) |
150 |
|
{ |
151 |
1604 |
bool success = false; |
152 |
1604 |
std::uint64_t ds = d; |
153 |
3208 |
TNode ttrf = getTaylorVariable(); |
154 |
3208 |
TNode tc = c; |
155 |
1650 |
do |
156 |
|
{ |
157 |
3254 |
success = true; |
158 |
3254 |
std::uint64_t n = 2 * ds; |
159 |
6508 |
std::pair<Node, Node> taylor = getTaylor(k, n); |
160 |
|
// check that 1-c^{n+1}/(n+1)! > 0 |
161 |
6508 |
Node ru = taylor.second; |
162 |
6508 |
Node rus = ru.substitute(ttrf, tc); |
163 |
3254 |
rus = Rewriter::rewrite(rus); |
164 |
3254 |
Assert(rus.isConst()); |
165 |
3254 |
if (rus.getConst<Rational>() > 1) |
166 |
|
{ |
167 |
1650 |
success = false; |
168 |
1650 |
ds = ds + 1; |
169 |
|
} |
170 |
3254 |
} while (!success); |
171 |
1604 |
if (ds > d) |
172 |
|
{ |
173 |
1280 |
Trace("nl-ext-exp-taylor") |
174 |
640 |
<< "*** Increase Taylor bound to " << ds << " > " << d << " for (" |
175 |
640 |
<< k << " " << c << ")" << std::endl; |
176 |
|
// must use sound upper bound |
177 |
1280 |
ApproximationBounds pboundss; |
178 |
640 |
getPolynomialApproximationBounds(k, ds, pboundss); |
179 |
640 |
pbounds.d_upperPos = pboundss.d_upperPos; |
180 |
|
} |
181 |
1604 |
return ds; |
182 |
|
} |
183 |
929 |
return d; |
184 |
|
} |
185 |
|
|
186 |
1402 |
std::pair<Node, Node> TaylorGenerator::getTfModelBounds(Node tf, |
187 |
|
std::uint64_t d, |
188 |
|
NlModel& model) |
189 |
|
{ |
190 |
|
// compute the model value of the argument |
191 |
2804 |
Node c = model.computeAbstractModelValue(tf[0]); |
192 |
1402 |
Assert(c.isConst()); |
193 |
1402 |
int csign = c.getConst<Rational>().sgn(); |
194 |
1402 |
Kind k = tf.getKind(); |
195 |
1402 |
if (csign == 0) |
196 |
|
{ |
197 |
|
NodeManager* nm = NodeManager::currentNM(); |
198 |
|
// at zero, its trivial |
199 |
|
if (k == Kind::SINE) |
200 |
|
{ |
201 |
|
Node zero = nm->mkConst(Rational(0)); |
202 |
|
return std::pair<Node, Node>(zero, zero); |
203 |
|
} |
204 |
|
Assert(k == Kind::EXPONENTIAL); |
205 |
|
Node one = nm->mkConst(Rational(1)); |
206 |
|
return std::pair<Node, Node>(one, one); |
207 |
|
} |
208 |
1402 |
bool isNeg = csign == -1; |
209 |
|
|
210 |
2804 |
ApproximationBounds pbounds; |
211 |
1402 |
getPolynomialApproximationBoundForArg(k, c, d, pbounds); |
212 |
|
|
213 |
2804 |
std::vector<Node> bounds; |
214 |
2804 |
TNode tfv = getTaylorVariable(); |
215 |
2804 |
TNode tfs = tf[0]; |
216 |
4206 |
for (unsigned d2 = 0; d2 < 2; d2++) |
217 |
|
{ |
218 |
|
Node pab = (d2 == 0 ? pbounds.d_lower |
219 |
5608 |
: (isNeg ? pbounds.d_upperNeg : pbounds.d_upperPos)); |
220 |
2804 |
if (!pab.isNull()) |
221 |
|
{ |
222 |
|
// { x -> M_A(tf[0]) } |
223 |
|
// Notice that we compute the model value of tfs first, so that |
224 |
|
// the call to rewrite below does not modify the term, where notice that |
225 |
|
// rewrite( x*x { x -> M_A(t) } ) = M_A(t)*M_A(t) |
226 |
|
// is not equal to |
227 |
|
// M_A( x*x { x -> t } ) = M_A( t*t ) |
228 |
|
// where M_A denotes the abstract model. |
229 |
5608 |
Node mtfs = model.computeAbstractModelValue(tfs); |
230 |
2804 |
pab = pab.substitute(tfv, mtfs); |
231 |
2804 |
pab = Rewriter::rewrite(pab); |
232 |
2804 |
Assert(pab.isConst()); |
233 |
2804 |
bounds.push_back(pab); |
234 |
|
} |
235 |
|
else |
236 |
|
{ |
237 |
|
bounds.push_back(Node::null()); |
238 |
|
} |
239 |
|
} |
240 |
1402 |
return std::pair<Node, Node>(bounds[0], bounds[1]); |
241 |
|
} |
242 |
|
|
243 |
|
} // namespace transcendental |
244 |
|
} // namespace nl |
245 |
|
} // namespace arith |
246 |
|
} // namespace theory |
247 |
28191 |
} // namespace cvc5 |