GM2Calc 2.3.0
Loading...
Searching...
No Matches
gm2_dilog.cpp
Go to the documentation of this file.
1// ====================================================================
2// This file is part of GM2Calc.
3//
4// GM2Calc is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published
6// by the Free Software Foundation, either version 3 of the License,
7// or (at your option) any later version.
8//
9// GM2Calc is distributed in the hope that it will be useful, but
10// WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with GM2Calc. If not, see
16// <http://www.gnu.org/licenses/>.
17// ====================================================================
18
19#include "gm2_dilog.hpp"
20#include <cmath>
21#include <limits>
22
23namespace gm2calc {
24
25namespace {
26
27template <int Nstart, typename T, int N>
28std::complex<T> horner(const std::complex<T>& z, const T (&coeffs)[N]) noexcept
29{
30 static_assert(0 <= Nstart && Nstart < N && N >= 2, "invalid array bounds");
31
32 const T rz = std::real(z);
33 const T iz = std::imag(z);
34 const T r = rz + rz;
35 const T s = std::norm(z);
36 T a = coeffs[N - 1], b = coeffs[N - 2];
37
38 for (int i = N - 3; i >= Nstart; --i) {
39 const T t = a;
40 a = b + r*a;
41 b = coeffs[i] - s*t;
42 }
43
44 return { rz*a + b, iz*a };
45}
46
47/// returns log(1 + z) for complex z
48template <typename T>
49std::complex<T> log1p(const std::complex<T>& z) noexcept
50{
51 const std::complex<T> u = T(1) + z;
52
53 if (std::real(u) == T(1) && std::imag(u) == T(0)) {
54 return z;
55 } else if (std::real(u) <= T(0)) {
56 return std::log(u);
57 }
58
59 return std::log(u)*(z/(u - T(1)));
60}
61
62} // anonymous namespace
63
64/**
65 * @brief Real dilogarithm \f$\operatorname{Li}_2(x)\f$
66 * @param x real argument
67 * @return \f$\operatorname{Li}_2(x)\f$
68 * @author Alexander Voigt
69 * @note taken from the polylogarithm library 7.0.0
70 *
71 * Implemented as an economized Pade approximation with a
72 * maximum error of 4.16e-18.
73 * [[arXiv:2201.01678](https://arxiv.org/abs/2201.01678)].
74 */
75double dilog(double x) noexcept
76{
77 const double PI = 3.1415926535897932;
78 const double P[] = {
79 0.9999999999999999502e+0,
80 -2.6883926818565423430e+0,
81 2.6477222699473109692e+0,
82 -1.1538559607887416355e+0,
83 2.0886077795020607837e-1,
84 -1.0859777134152463084e-2
85 };
86 const double Q[] = {
87 1.0000000000000000000e+0,
88 -2.9383926818565635485e+0,
89 3.2712093293018635389e+0,
90 -1.7076702173954289421e+0,
91 4.1596017228400603836e-1,
92 -3.9801343754084482956e-2,
93 8.2743668974466659035e-4
94 };
95
96 double y = 0, r = 0, s = 1;
97
98 // transform to [0, 1/2]
99 if (x < -1) {
100 const double l = std::log(1 - x);
101 y = 1/(1 - x);
102 r = -PI*PI/6 + l*(0.5*l - std::log(-x));
103 s = 1;
104 } else if (x == -1) {
105 return -PI*PI/12;
106 } else if (x < 0) {
107 const double l = std::log1p(-x);
108 y = x/(x - 1);
109 r = -0.5*l*l;
110 s = -1;
111 } else if (x == 0) {
112 return x;
113 } else if (x < 0.5) {
114 y = x;
115 r = 0;
116 s = 1;
117 } else if (x < 1) {
118 y = 1 - x;
119 r = PI*PI/6 - std::log(x)*std::log1p(-x);
120 s = -1;
121 } else if (x == 1) {
122 return PI*PI/6;
123 } else if (x < 2) {
124 const double l = std::log(x);
125 y = 1 - 1/x;
126 r = PI*PI/6 - l*(std::log(y) + 0.5*l);
127 s = 1;
128 } else {
129 const double l = std::log(x);
130 y = 1/x;
131 r = PI*PI/3 - 0.5*l*l;
132 s = -1;
133 }
134
135 const double y2 = y*y;
136 const double y4 = y2*y2;
137 const double p = P[0] + y * P[1] + y2 * (P[2] + y * P[3]) +
138 y4 * (P[4] + y * P[5]);
139 const double q = Q[0] + y * Q[1] + y2 * (Q[2] + y * Q[3]) +
140 y4 * (Q[4] + y * Q[5] + y2 * Q[6]);
141
142 return r + s*y*p/q;
143}
144
145/**
146 * @brief Complex dilogarithm \f$\mathrm{Li}_2(z)\f$
147 * @param z complex argument
148 * @return \f$\mathrm{Li}_2(z)\f$
149 * @note Implementation translated from SPheno to C++
150 * @author Werner Porod
151 * @note translated to C++ by Alexander Voigt
152 * @note taken from the polylogarithm library 7.0.0
153 */
154std::complex<double> dilog(const std::complex<double>& z) noexcept
155{
156 const double PI = 3.1415926535897932;
157
158 // bf[1..N-1] are the even Bernoulli numbers / (2 n + 1)!
159 // generated by: Table[BernoulliB[2 n]/(2 n + 1)!, {n, 1, 9}]
160 const double bf[10] = {
161 - 1.0/4.0,
162 + 1.0/36.0,
163 - 1.0/3600.0,
164 + 1.0/211680.0,
165 - 1.0/10886400.0,
166 + 1.0/526901760.0,
167 - 4.0647616451442255e-11,
168 + 8.9216910204564526e-13,
169 - 1.9939295860721076e-14,
170 + 4.5189800296199182e-16
171 };
172
173 const double rz = std::real(z);
174 const double iz = std::imag(z);
175
176 // special cases
177 if (iz == 0) {
178 if (rz <= 1) {
179 return { dilog(rz), iz };
180 }
181 // rz > 1
182 return { dilog(rz), -PI*std::log(rz) };
183 }
184
185 const double nz = std::norm(z);
186
187 if (nz < std::numeric_limits<double>::epsilon()) {
188 return z*(1.0 + 0.25*z);
189 }
190
191 std::complex<double> u(0.0, 0.0), rest(0.0, 0.0);
192 double sgn = 1;
193
194 // transformation to |z|<1, Re(z)<=0.5
195 if (rz <= 0.5) {
196 if (nz > 1) {
197 const auto lz = std::log(-z);
198 u = -log1p(-1.0/z);
199 rest = -0.5*lz*lz - PI*PI/6;
200 sgn = -1;
201 } else { // nz <= 1
202 u = -log1p(-z);
203 rest = 0;
204 sgn = 1;
205 }
206 } else { // rz > 0.5
207 if (nz <= 2*rz) {
208 u = -std::log(z);
209 rest = u*log1p(-z) + PI*PI/6;
210 sgn = -1;
211 } else { // nz > 2*rz
212 const auto lz = std::log(-z);
213 u = -log1p(-1.0/z);
214 rest = -0.5*lz*lz - PI*PI/6;
215 sgn = -1;
216 }
217 }
218
219 const auto u2(u*u);
220
221 return sgn*(u + u2*(bf[0] + u*horner<1>(u2, bf))) + rest;
222}
223
224/**
225 * @brief Clausen function \f$\mathrm{Cl}_2(\theta) = \mathrm{Im}(\mathrm{Li}_2(e^{i\theta}))\f$
226 * @param x real angle
227 * @return \f$\mathrm{Cl}_2(\theta)\f$
228 * @author Alexander Voigt
229 * @note Implemented as economized Padé approximation.
230 * @note taken from the polylogarithm library 7.0.0
231 */
232double clausen_2(double x) noexcept
233{
234 const double PI = 3.14159265358979324;
235 const double PI2 = 2*PI, PIH = PI/2, PI28 = PI*PI/8;
236 double sgn = 1;
237
238 if (x < 0) {
239 x = -x;
240 sgn = -1;
241 }
242
243 if (x >= PI2) {
244 x = std::fmod(x, PI2);
245 }
246
247 if (x > PI) {
248 const double p0 = 6.28125;
249 const double p1 = 0.0019353071795864769253;
250 x = (p0 - x) + p1;
251 sgn = -sgn;
252 }
253
254 if (x == 0) {
255 return x;
256 } else if (x == PI) {
257 return 0;
258 }
259
260 double h = 0;
261
262 if (x < PIH) {
263 const double P[] = {
264 1.3888888888888889e-02, -4.3286930203743071e-04,
265 3.2779814789973427e-06, -3.6001540369575084e-09
266 };
267 const double Q[] = {
268 1.0000000000000000e+00, -3.6166589746694121e-02,
269 3.6015827281202639e-04, -8.3646182842184428e-07
270 };
271 const double y = x*x;
272 const double y2 = y*y;
273 const double p = P[0] + y * P[1] + y2 * (P[2] + y * P[3]);
274 const double q = Q[0] + y * Q[1] + y2 * (Q[2] + y * Q[3]);
275
276 h = x*(1 - std::log(x) + y*p/q);
277 } else {
278 const double P[] = {
279 6.4005702446195512e-01, -2.0641655351338783e-01,
280 2.4175305223497718e-02, -1.2355955287855728e-03,
281 2.5649833551291124e-05, -1.4783829128773320e-07
282 };
283 const double Q[] = {
284 1.0000000000000000e+00, -2.5299102015666356e-01,
285 2.2148751048467057e-02, -7.8183920462457496e-04,
286 9.5432542196310670e-06, -1.8184302880448247e-08
287 };
288 const double y = PI - x;
289 const double z = y*y - PI28;
290 const double z2 = z*z;
291 const double z4 = z2*z2;
292 const double p = P[0] + z * P[1] + z2 * (P[2] + z * P[3]) +
293 z4 * (P[4] + z * P[5]);
294 const double q = Q[0] + z * Q[1] + z2 * (Q[2] + z * Q[3]) +
295 z4 * (Q[4] + z * Q[5]);
296
297 h = y*p/q;
298 }
299
300 return sgn*h;
301}
302
303} // namespace gm2calc
double clausen_2(double x) noexcept
Clausen function .
double dilog(double x) noexcept
Real dilogarithm .
Definition gm2_dilog.cpp:75