/src/quantlib/ql/math/integrals/fourierintegration.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2004, 2005, 2008 Klaus Spanderen |
5 | | Copyright (C) 2007 StatPro Italia srl |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <https://www.quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | #include <ql/math/integrals/discreteintegrals.hpp> |
22 | | #include <ql/math/integrals/expsinhintegral.hpp> |
23 | | #include <ql/math/integrals/tanhsinhintegral.hpp> |
24 | | #include <ql/math/integrals/fourierintegration.hpp> |
25 | | #include <ql/math/integrals/gausslobattointegral.hpp> |
26 | | #include <ql/math/integrals/kronrodintegral.hpp> |
27 | | #include <ql/math/integrals/simpsonintegral.hpp> |
28 | | #include <ql/math/integrals/trapezoidintegral.hpp> |
29 | | #include <ql/math/solvers1d/brent.hpp> |
30 | | #include <ql/utilities/null.hpp> |
31 | | #include <cmath> |
32 | | #include <limits> |
33 | | #include <utility> |
34 | | |
35 | | namespace QuantLib { |
36 | | |
37 | | namespace { |
38 | | |
39 | | class integrand1 { |
40 | | private: |
41 | | const Real c_inf_; |
42 | | const std::function<Real(Real)> f_; |
43 | | public: |
44 | 0 | integrand1(Real c_inf, std::function<Real(Real)> f) : c_inf_(c_inf), f_(std::move(f)) {} |
45 | 0 | Real operator()(Real x) const { |
46 | 0 | if ((1.0-x)*c_inf_ > QL_EPSILON) |
47 | 0 | return f_(-std::log(0.5-0.5*x)/c_inf_)/((1.0-x)*c_inf_); |
48 | 0 | else |
49 | 0 | return 0.0; |
50 | 0 | } |
51 | | }; |
52 | | |
53 | | class integrand2 { |
54 | | private: |
55 | | const Real c_inf_; |
56 | | const std::function<Real(Real)> f_; |
57 | | public: |
58 | 0 | integrand2(Real c_inf, std::function<Real(Real)> f) : c_inf_(c_inf), f_(std::move(f)) {} |
59 | 0 | Real operator()(Real x) const { |
60 | 0 | if (x*c_inf_ > QL_EPSILON) { |
61 | 0 | return f_(-std::log(x)/c_inf_)/(x*c_inf_); |
62 | 0 | } else { |
63 | 0 | return 0.0; |
64 | 0 | } |
65 | 0 | } |
66 | | }; |
67 | | |
68 | | class integrand3 { |
69 | | private: |
70 | | const integrand2 int_; |
71 | | public: |
72 | | integrand3(Real c_inf, const std::function<Real(Real)>& f) |
73 | 0 | : int_(c_inf, f) {} |
74 | | |
75 | 0 | Real operator()(Real x) const { return int_(1.0-x); } |
76 | | }; |
77 | | |
78 | | class u_Max { |
79 | | public: |
80 | 0 | u_Max(Real c_inf, Real epsilon) : c_inf_(c_inf), logEpsilon_(std::log(epsilon)) {} |
81 | | |
82 | 0 | Real operator()(Real u) const { |
83 | 0 | ++evaluations_; |
84 | 0 | return c_inf_*u + std::log(u) + logEpsilon_; |
85 | 0 | } |
86 | | |
87 | 0 | Size evaluations() const { return evaluations_; } |
88 | | |
89 | | private: |
90 | | const Real c_inf_, logEpsilon_; |
91 | | mutable Size evaluations_ = 0; |
92 | | }; |
93 | | |
94 | | |
95 | | class uHat_Max { |
96 | | public: |
97 | 0 | uHat_Max(Real v0T2, Real epsilon) : v0T2_(v0T2), logEpsilon_(std::log(epsilon)) {} |
98 | | |
99 | 0 | Real operator()(Real u) const { |
100 | 0 | ++evaluations_; |
101 | 0 | return v0T2_*u*u + std::log(u) + logEpsilon_; |
102 | 0 | } |
103 | | |
104 | 0 | Size evaluations() const { return evaluations_; } |
105 | | |
106 | | private: |
107 | | const Real v0T2_, logEpsilon_; |
108 | | mutable Size evaluations_ = 0; |
109 | | }; |
110 | | } |
111 | | |
112 | | |
113 | | FourierIntegration::FourierIntegration(Algorithm intAlgo, |
114 | | ext::shared_ptr<Integrator> integrator) |
115 | 0 | : intAlgo_(intAlgo), integrator_(std::move(integrator)) {} |
116 | | |
117 | | FourierIntegration::FourierIntegration( |
118 | | Algorithm intAlgo, ext::shared_ptr<GaussianQuadrature> gaussianQuadrature) |
119 | 0 | : intAlgo_(intAlgo), gaussianQuadrature_(std::move(gaussianQuadrature)) {} |
120 | | |
121 | | FourierIntegration FourierIntegration::gaussLobatto( |
122 | 0 | Real relTolerance, Real absTolerance, Size maxEvaluations, bool useConvergenceEstimate) { |
123 | 0 | return FourierIntegration(GaussLobatto, |
124 | 0 | ext::make_shared<GaussLobattoIntegral>(maxEvaluations, |
125 | 0 | absTolerance, |
126 | 0 | relTolerance, |
127 | 0 | useConvergenceEstimate)); |
128 | 0 | } |
129 | | |
130 | | FourierIntegration FourierIntegration::gaussKronrod(Real absTolerance, |
131 | 0 | Size maxEvaluations) { |
132 | 0 | return FourierIntegration(GaussKronrod, |
133 | 0 | ext::make_shared<GaussKronrodAdaptive>(absTolerance, |
134 | 0 | maxEvaluations)); |
135 | 0 | } |
136 | | |
137 | | FourierIntegration FourierIntegration::simpson(Real absTolerance, |
138 | 0 | Size maxEvaluations) { |
139 | 0 | return FourierIntegration(Simpson, |
140 | 0 | ext::make_shared<SimpsonIntegral>(absTolerance, |
141 | 0 | maxEvaluations)); |
142 | 0 | } |
143 | | |
144 | | FourierIntegration FourierIntegration::trapezoid(Real absTolerance, |
145 | 0 | Size maxEvaluations) { |
146 | 0 | return FourierIntegration(Trapezoid, |
147 | 0 | ext::make_shared<TrapezoidIntegral<Default>>(absTolerance, |
148 | 0 | maxEvaluations)); |
149 | 0 | } |
150 | | |
151 | 0 | FourierIntegration FourierIntegration::gaussLaguerre(Size intOrder) { |
152 | 0 | return FourierIntegration(GaussLaguerre, |
153 | 0 | ext::make_shared<GaussLaguerreIntegration>(intOrder)); |
154 | 0 | } |
155 | | |
156 | 0 | FourierIntegration FourierIntegration::gaussLegendre(Size intOrder) { |
157 | 0 | return FourierIntegration(GaussLegendre, |
158 | 0 | ext::make_shared<GaussLegendreIntegration>(intOrder)); |
159 | 0 | } |
160 | | |
161 | 0 | FourierIntegration FourierIntegration::gaussChebyshev(Size intOrder) { |
162 | 0 | return FourierIntegration(GaussChebyshev, |
163 | 0 | ext::make_shared<GaussChebyshevIntegration>(intOrder)); |
164 | 0 | } |
165 | | |
166 | 0 | FourierIntegration FourierIntegration::gaussChebyshev2nd(Size intOrder) { |
167 | 0 | return FourierIntegration(GaussChebyshev2nd, |
168 | 0 | ext::make_shared<GaussChebyshev2ndIntegration>(intOrder)); |
169 | 0 | } |
170 | | |
171 | 0 | FourierIntegration FourierIntegration::discreteSimpson(Size evaluations) { |
172 | 0 | return FourierIntegration( |
173 | 0 | DiscreteSimpson, ext::make_shared<DiscreteSimpsonIntegrator>(evaluations)); |
174 | 0 | } |
175 | | |
176 | 0 | FourierIntegration FourierIntegration::discreteTrapezoid(Size evaluations) { |
177 | 0 | return FourierIntegration( |
178 | 0 | DiscreteTrapezoid, ext::make_shared<DiscreteTrapezoidIntegrator>(evaluations)); |
179 | 0 | } |
180 | | |
181 | 0 | FourierIntegration FourierIntegration::expSinh(Real relTolerance) { |
182 | 0 | return FourierIntegration( |
183 | 0 | ExpSinh, ext::make_shared<ExpSinhIntegral>(relTolerance)); |
184 | 0 | } |
185 | | |
186 | 0 | FourierIntegration FourierIntegration::tanhSinh(Real relTolerance) { |
187 | 0 | return FourierIntegration( |
188 | 0 | TanhSinh, ext::make_shared<TanhSinhIntegral>(relTolerance)); |
189 | 0 | } |
190 | | |
191 | 0 | Size FourierIntegration::numberOfEvaluations() const { |
192 | 0 | if (integrator_ != nullptr) { |
193 | 0 | return integrator_->numberOfEvaluations(); |
194 | 0 | } else if (gaussianQuadrature_ != nullptr) { |
195 | 0 | return gaussianQuadrature_->order(); |
196 | 0 | } else { |
197 | 0 | QL_FAIL("neither Integrator nor GaussianQuadrature given"); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | 0 | bool FourierIntegration::isAdaptiveIntegration() const { |
202 | 0 | return intAlgo_ == GaussLobatto |
203 | 0 | || intAlgo_ == GaussKronrod |
204 | 0 | || intAlgo_ == Simpson |
205 | 0 | || intAlgo_ == Trapezoid |
206 | 0 | || intAlgo_ == ExpSinh |
207 | 0 | || intAlgo_ == TanhSinh; |
208 | 0 | } |
209 | | |
210 | | Real FourierIntegration::calculate( |
211 | | Real c_inf, |
212 | | const std::function<Real(Real)>& f, |
213 | | const std::function<Real()>& maxBound, |
214 | 0 | const Real scaling) const { |
215 | |
|
216 | 0 | Real retVal; |
217 | |
|
218 | 0 | switch(intAlgo_) { |
219 | 0 | case GaussLaguerre: |
220 | 0 | retVal = (*gaussianQuadrature_)(f); |
221 | 0 | break; |
222 | 0 | case GaussLegendre: |
223 | 0 | case GaussChebyshev: |
224 | 0 | case GaussChebyshev2nd: |
225 | 0 | retVal = (*gaussianQuadrature_)(integrand1(c_inf, f)); |
226 | 0 | break; |
227 | 0 | case ExpSinh: |
228 | 0 | retVal = scaling*(*integrator_)( |
229 | 0 | [scaling, f](Real x) -> Real { return f(scaling*x);}, |
230 | 0 | 0.0, std::numeric_limits<Real>::max()); |
231 | 0 | break; |
232 | 0 | case TanhSinh: |
233 | 0 | retVal = (*integrator_)(f, 0.0, std::numeric_limits<Real>::max()); |
234 | 0 | break; |
235 | 0 | case Simpson: |
236 | 0 | case Trapezoid: |
237 | 0 | case GaussLobatto: |
238 | 0 | case GaussKronrod: |
239 | 0 | if (maxBound && maxBound() != Null<Real>()) |
240 | 0 | retVal = (*integrator_)(f, 0.0, maxBound()); |
241 | 0 | else |
242 | 0 | retVal = (*integrator_)(integrand2(c_inf, f), 0.0, 1.0); |
243 | 0 | break; |
244 | 0 | case DiscreteTrapezoid: |
245 | 0 | case DiscreteSimpson: |
246 | 0 | if (maxBound && maxBound() != Null<Real>()) |
247 | 0 | retVal = (*integrator_)(f, 0.0, maxBound()); |
248 | 0 | else |
249 | 0 | retVal = (*integrator_)(integrand3(c_inf, f), 0.0, 1.0); |
250 | 0 | break; |
251 | 0 | default: |
252 | 0 | QL_FAIL("unknwon integration algorithm"); |
253 | 0 | } |
254 | | |
255 | 0 | return retVal; |
256 | 0 | } |
257 | | |
258 | | Real FourierIntegration::calculate( |
259 | | Real c_inf, |
260 | | const std::function<Real(Real)>& f, |
261 | 0 | Real maxBound) const { |
262 | |
|
263 | 0 | return FourierIntegration::calculate( |
264 | 0 | c_inf, f, [=](){ return maxBound; }); |
265 | 0 | } |
266 | | |
267 | | Real FourierIntegration::andersenPiterbargIntegrationLimit( |
268 | 0 | Real c_inf, Real epsilon, Real v0, Real t) { |
269 | |
|
270 | 0 | const Real uMaxGuess = -std::log(epsilon)/c_inf; |
271 | 0 | const Real uMaxStep = 0.1*uMaxGuess; |
272 | |
|
273 | 0 | const Real uMax = Brent().solve(u_Max(c_inf, epsilon), |
274 | 0 | QL_EPSILON*uMaxGuess, uMaxGuess, uMaxStep); |
275 | |
|
276 | 0 | try { |
277 | 0 | const Real uHatMaxGuess = std::sqrt(-std::log(epsilon)/(0.5*v0*t)); |
278 | 0 | const Real uHatMax = Brent().solve(uHat_Max(0.5*v0*t, epsilon), |
279 | 0 | QL_EPSILON*uHatMaxGuess, uHatMaxGuess, 0.001*uHatMaxGuess); |
280 | |
|
281 | 0 | return std::max(uMax, uHatMax); |
282 | 0 | } |
283 | 0 | catch (const Error&) { |
284 | 0 | return uMax; |
285 | 0 | } |
286 | 0 | } |
287 | | } |