QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
exponentialintegrals.cpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2020 Klaus Spanderen
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20/*! \file exponentialintegrals.cpp
21*/
22
23
24#include <ql/errors.hpp>
25#include <ql/mathconstants.hpp>
28
29#include <boost/math/special_functions/sign.hpp>
30#include <cmath>
31
32namespace QuantLib {
33 namespace exponential_integrals_helper {
34
35 // Reference:
36 // Rowe et al: GALSIM: The modular galaxy image simulation toolkit
37 // https://arxiv.org/abs/1407.7676
38
39 Real f(Real x) {
40 const Real x2 = 1.0/(x*x);
41
42 return (
43 1 + x2*(7.44437068161936700618e2 + x2*(1.96396372895146869801e5
44 + x2*(2.37750310125431834034e7 + x2*(1.43073403821274636888e9
45 + x2*(4.33736238870432522765e10 + x2*(6.40533830574022022911e11
46 + x2*(4.20968180571076940208e12 + x2*(1.00795182980368574617e13
47 + x2*(4.94816688199951963482e12 - x2*4.94701168645415959931e11)))))))))
48 )/(x *(
49 1 + x2*(7.46437068161927678031e2 + x2*(1.97865247031583951450e5
50 + x2*(2.41535670165126845144e7 + x2*(1.47478952192985464958e9
51 + x2*(4.58595115847765779830e10 + x2*(7.08501308149515401563e11
52 + x2*(5.06084464593475076774e12 + x2*(1.43468549171581016479e13
53 + x2*1.11535493509914254097e13))))))))
54 ) );
55 }
56
57 Real g(Real x) {
58 const Real x2 = 1.0/(x*x);
59
60 return x2*(
61 1 + x2*(8.1359520115168615e2 + x2*(2.35239181626478200e5
62 + x2*(3.12557570795778731e7 + x2*(2.06297595146763354e9
63 + x2*(6.83052205423625007e10 + x2*(1.09049528450362786e12
64 + x2*(7.57664583257834349e12 + x2*(1.81004487464664575e13
65 + x2*(6.43291613143049485e12 - x2*1.36517137670871689e12)))))))))
66 )/(
67 1 + x2*(8.19595201151451564e2 + x2*(2.40036752835578777e5
68 + x2*(3.26026661647090822e7 + x2*(2.23355543278099360e9
69 + x2*(7.87465017341829930e10 + x2*(1.39866710696414565e12
70 + x2*(1.17164723371736605e13 + x2*(4.01839087307656620e13
71 + x2*3.99653257887490811e13))))))))
72 );
73 }
74 }
75
76 namespace ExponentialIntegral {
78 if (x < 0)
79 return -Si(Real(-x));
80 else if (x <= 4.0) {
81 const Real x2 = x*x;
82
83 return x*
84 ( 1 + x2*(-4.54393409816329991e-2 + x2*(1.15457225751016682e-3
85 + x2*(-1.41018536821330254e-5 + x2*(9.43280809438713025e-8
86 + x2*(-3.53201978997168357e-10 + x2*(7.08240282274875911e-13
87 - x2*6.05338212010422477e-16))))))
88 ) / (
89 1 + x2*(1.01162145739225565e-2 + x2*(4.99175116169755106e-5
90 + x2*(1.55654986308745614e-7 + x2*(3.28067571055789734e-10
91 + x2*(4.5049097575386581e-13 + x2*3.21107051193712168e-16)))))
92 );
93 }
94 else {
95 using namespace exponential_integrals_helper;
96 return M_PI_2 - f(x)*std::cos(x) - g(x)*std::sin(x);
97 }
98 }
99
101 QL_REQUIRE(x >= 0, "x < 0 => Ci(x) = Ci(-x) + i*pi");
102
103 if (x <= 4.0) {
104 const Real x2 = x*x;
105
106 return M_EULER_MASCHERONI + std::log(x) +
107 x2* ( -0.25 + x2*(7.51851524438898291e-3 +x2*(-1.27528342240267686e-4
108 + x2*(1.05297363846239184e-6 +x2*(-4.68889508144848019e-9
109 + x2*(1.06480802891189243e-11 - x2*9.93728488857585407e-15)))))
110 ) / (
111 1 + x2*(1.1592605689110735e-2 + x2*(6.72126800814254432e-5
112 + x2*(2.55533277086129636e-7 + x2*(6.97071295760958946e-10
113 + x2*(1.38536352772778619e-12 + x2*(1.89106054713059759e-15
114 + x2*1.39759616731376855e-18))))))
115 );
116 }
117 else {
118 using namespace exponential_integrals_helper;
119 return f(x)*std::sin(x) - g(x)*std::cos(x);
120 }
121 }
122
123 std::complex<Real> Ei(
124 const std::complex<Real>& z, const std::complex<Real>& acc) {
125
126 if (z.real() == 0.0 && z.imag() == 0.0
127 && std::numeric_limits<Real>::has_infinity) {
128 return std::complex<Real>(-std::numeric_limits<Real>::infinity());
129 }
130
131
132 constexpr double DIST = 4.5;
133 constexpr double MAX_ERROR = 5.0 * QL_EPSILON;
134
135 const Real z_inf = std::log(0.01*QL_MAX_REAL) + std::log(100.0);
136 QL_REQUIRE(z.real() < z_inf, "argument error " << z);
137
138 const Real z_asym = 2.0 - 1.035*std::log(MAX_ERROR);
139
140 using boost::math::sign;
141 const Real abs_z = std::abs(z);
142
143 const auto match = [=](
144 const std::complex<Real>& z1, const std::complex<Real>& z2)
145 -> bool {
146 const std::complex<Real> d = z1 - z2;
147 return std::abs(d.real()) <= MAX_ERROR*std::abs(z1.real())
148 && std::abs(d.imag()) <= MAX_ERROR*std::abs(z1.imag());
149 };
150
151 if (z.real() > z_inf)
152 return std::complex<Real>(std::exp(z)/z) + acc;
153
154 if (abs_z > 1.1*z_asym) {
155 std::complex<Real> ei = acc + std::complex<Real>(Real(0.0), sign(z.imag())*M_PI);
156 std::complex<Real> s(std::exp(z)/z);
157 for (Size i=1; i <= std::floor(abs_z)+1; ++i) {
158 if (match(ei+s, ei))
159 return ei+s;
160 ei += s;
161 s *= Real(i)/z;
162 }
163 QL_FAIL("series conversion issue for Ei(" << z << ")");
164 }
165
166 if (abs_z > DIST && (z.real() < 0 || std::abs(z.imag()) > DIST)) {
167 std::complex<Real> ei(0.0);
168 for (Size k = 47; k >=1; --k) {
169 ei = - Real(k*k)/(2.0*k + 1.0 - z + ei);
170 }
171 return (acc + std::complex<Real>(0.0, sign(z.imag())*M_PI))
172 - std::exp(z)/ (1.0 - z + ei);
173
174 QL_FAIL("series conversion issue for Ei(" << z << ")");
175 }
176
177 std::complex<Real> s(0.0), sn=z;
178 Real nn=1.0;
179
180 Size n;
181 for (n=2; n < 1000 && s+sn*nn != s; ++n) {
182 s+=sn*nn;
183
184 if ((n & 1) != 0U)
185 nn += 1/(2.0*(n/2) + 1); // NOLINT(bugprone-integer-division)
186
187 sn *= -z / Real(2*n);
188 }
189
190 QL_REQUIRE(n < 1000, "series conversion issue for Ei(" << z << ")");
191
192 const std::complex<Real> r
193 = (M_EULER_MASCHERONI + acc) + std::log(z) + std::exp(0.5*z)*s;
194
195 if (z.imag() != Real(0.0))
196 return r;
197 else
198 return std::complex<Real>(r.real(), acc.imag());
199 }
200
201 std::complex<Real> Ei(const std::complex<Real>&z) {
202 return Ei(z, std::complex<Real>(0.0));
203 }
204
205 std::complex<Real> E1(const std::complex<Real>& z) {
206 if (z.imag() < 0.0) {
207 return -Ei(-z, std::complex<Real>(0.0, -M_PI));
208 }
209 else if (z.imag() > 0.0 || z.real() < 0.0) {
210 return -Ei(-z, std::complex<Real>(0.0, M_PI));
211 }
212 else {
213 return -Ei(-z);
214 }
215 }
216
217 // Reference:
218 // https://functions.wolfram.com/GammaBetaErf/ExpIntegralEi/introductions/ExpIntegrals/ShowAll.html
219 std::complex<Real> Si(const std::complex<Real>& z) {
220 if (std::abs(z) <= 0.2) {
221 std::complex<Real> s(0), nn(z);
222 Size k;
223 for (k=2; k < 100 && s != s+nn; ++k) {
224 s += nn;
225 nn *= -z*z/((2.0*k-2)*(2*k-1)*(2*k-1))*(2.0*k-3);
226 }
227 QL_REQUIRE(k < 100, "series conversion issue for Si(" << z << ")");
228
229 return s;
230 }
231 else {
232 const std::complex<Real> i(0.0, 1.0);
233 return 0.5*i*(E1(-i*z) - E1(i*z)
234 - std::complex<Real>(0.0, ((z.real() >= 0 && z.imag() >= 0)
235 || (z.real() > 0 && z.imag() < 0) )? M_PI : -M_PI));
236 }
237 }
238
239 std::complex<Real> Ci(const std::complex<Real>& z) {
240 const std::complex<Real> i(0.0, 1.0);
241
242 std::complex<Real> acc(0.0);
243 if (z.real() < 0.0 && z.imag() >= 0.0)
244 acc.imag(M_PI);
245 else if (z.real() <= 0.0 && z.imag() <= 0.0)
246 acc.imag(-M_PI);
247
248 return -0.5*(E1(-i*z) + E1(i*z)) + acc;
249 }
250 }
251}
floating-point comparisons
Classes and functions for error handling.
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
#define QL_FAIL(message)
throw an error (possibly with file and line information)
Definition: errors.hpp:92
Date d
#define M_EULER_MASCHERONI
#define QL_MAX_REAL
Definition: qldefines.hpp:176
#define QL_EPSILON
Definition: qldefines.hpp:178
QL_REAL Real
real number
Definition: types.hpp:50
std::size_t Size
size of a container
Definition: types.hpp:58
#define M_PI_2
#define M_PI
std::complex< Real > Ei(const std::complex< Real > &z, const std::complex< Real > &acc)
std::complex< Real > E1(const std::complex< Real > &z)
Definition: any.hpp:35
ext::shared_ptr< YieldTermStructure > r