Himalaya
Li2.cpp
Go to the documentation of this file.
1 // ====================================================================
2 // This file is part of Himalaya.
3 //
4 // Himalaya is licenced under the GNU General Public License (GNU GPL)
5 // version 3.
6 // ====================================================================
7 
8 #include "himalaya/misc/Li2.hpp"
10 #include <cmath>
11 #include <limits>
12 
13 /**
14  * @file Li2.cpp
15  * @brief Implementation of the dilogarithm function
16  * @note The implementation has been taken from the polylogarithm package.
17  */
18 
19 namespace himalaya {
20 
21 namespace {
22 
23  template <typename T, int N>
24  T horner(T x, const T (&c)[N]) noexcept
25  {
26  T p = 0;
27  for (int i = N - 1; i >= 0; --i) {
28  p = p*x + c[i];
29  }
30  return p;
31  }
32 
33  template <int Nstart, typename T, int N>
34  Complex<T> horner(const Complex<T>& z, const T (&coeffs)[N]) noexcept
35  {
36  static_assert(0 <= Nstart && Nstart < N && N >= 2, "invalid array bounds");
37 
38  const T r = z.re + z.re;
39  const T s = z.re * z.re + z.im * z.im;
40  T a = coeffs[N - 1], b = coeffs[N - 2];
41 
42  for (int i = N - 3; i >= Nstart; --i) {
43  const T t = a;
44  a = b + r * a;
45  b = coeffs[i] - s * t;
46  }
47 
48  return Complex<T>(z.re*a + b, z.im*a);
49  }
50 
51 } // namespace
52 
53 /**
54  * @brief Real dilogarithm \f$\mathrm{Li}_2(x)\f$
55  * @param x real argument
56  * @return \f$\mathrm{Li}_2(x)\f$
57  * @author Alexander Voigt
58  *
59  * Implemented as an economized Pade approximation with a
60  * maximum error of 4.16e-18.
61  */
62 double dilog(double x) noexcept
63 {
64  const double PI = 3.1415926535897932;
65  const double P[] = {
66  1.0706105563309304277e+0,
67  -4.5353562730201404017e+0,
68  7.4819657596286408905e+0,
69  -6.0516124315132409155e+0,
70  2.4733515209909815443e+0,
71  -4.6937565143754629578e-1,
72  3.1608910440687221695e-2,
73  -2.4630612614645039828e-4
74  };
75  const double Q[] = {
76  1.0000000000000000000e+0,
77  -4.5355682121856044935e+0,
78  8.1790029773247428573e+0,
79  -7.4634190853767468810e+0,
80  3.6245392503925290187e+0,
81  -8.9936784740041174897e-1,
82  9.8554565816757007266e-2,
83  -3.2116618742475189569e-3
84  };
85 
86  double y = 0, r = 0, s = 1;
87 
88  // transform to [0, 1/2)
89  if (x < -1) {
90  const double l = std::log(1 - x);
91  y = 1/(1 - x);
92  r = -PI*PI/6 + l*(0.5*l - std::log(-x));
93  s = 1;
94  } else if (x == -1) {
95  return -PI*PI/12;
96  } else if (x < 0) {
97  const double l = std::log1p(-x);
98  y = x/(x - 1);
99  r = -0.5*l*l;
100  s = -1;
101  } else if (x == 0) {
102  return 0;
103  } else if (x < 0.5) {
104  y = x;
105  r = 0;
106  s = 1;
107  } else if (x < 1) {
108  y = 1 - x;
109  r = PI*PI/6 - std::log(x)*std::log(1 - x);
110  s = -1;
111  } else if (x == 1) {
112  return PI*PI/6;
113  } else if (x < 2) {
114  const double l = std::log(x);
115  y = 1 - 1/x;
116  r = PI*PI/6 - l*(std::log(1 - 1/x) + 0.5*l);
117  s = 1;
118  } else {
119  const double l = std::log(x);
120  y = 1/x;
121  r = PI*PI/3 - 0.5*l*l;
122  s = -1;
123  }
124 
125  const double z = y - 0.25;
126 
127  const double p = horner(z, P);
128  const double q = horner(z, Q);
129 
130  return r + s*y*p/q;
131 }
132 
133 /**
134  * @brief Complex dilogarithm \f$\mathrm{Li}_2(z)\f$
135  * @param z_ complex argument
136  * @return \f$\mathrm{Li}_2(z)\f$
137  * @note Implementation translated from SPheno to C++
138  * @author Werner Porod
139  * @note translated to C++ by Alexander Voigt
140  */
141 std::complex<double> dilog(const std::complex<double>& z_) noexcept
142 {
143  const double PI = 3.1415926535897932;
144  const Complex<double> z = { std::real(z_), std::imag(z_) };
145 
146  // bf[1..N-1] are the even Bernoulli numbers / (2 n + 1)!
147  // generated by: Table[BernoulliB[2 n]/(2 n + 1)!, {n, 1, 9}]
148  const double bf[10] = {
149  - 1.0/4.0,
150  + 1.0/36.0,
151  - 1.0/3600.0,
152  + 1.0/211680.0,
153  - 1.0/10886400.0,
154  + 1.0/526901760.0,
155  - 4.0647616451442255e-11,
156  + 8.9216910204564526e-13,
157  - 1.9939295860721076e-14,
158  + 4.5189800296199182e-16
159  };
160 
161  // special cases
162  if (z.im == 0) {
163  if (z.re <= 1) {
164  return dilog(z.re);
165  }
166  // z.re > 1
167  return { dilog(z.re), -PI*std::log(z.re) };
168  }
169 
170  const double nz = norm_sqr(z);
171 
172  if (nz < std::numeric_limits<double>::epsilon()) {
173  return z_;
174  }
175 
176  Complex<double> u(0.0, 0.0), rest(0.0, 0.0);
177  double sgn = 1;
178 
179  // transformation to |z|<1, Re(z)<=0.5
180  if (z.re <= 0.5) {
181  if (nz > 1) {
182  const Complex<double> lz = log(-z);
183  u = -log(1.0 - 1.0 / z);
184  rest = -0.5*lz*lz - PI*PI/6;
185  sgn = -1;
186  } else { // nz <= 1
187  u = -log(1.0 - z);
188  rest = 0;
189  sgn = 1;
190  }
191  } else { // z.re > 0.5
192  if (nz <= 2*z.re) {
193  u = -log(z);
194  rest = u*log(1.0 - z) + PI*PI/6;
195  sgn = -1;
196  } else { // nz > 2*z.re
197  const Complex<double> lz = log(-z);
198  u = -log(1.0 - 1.0 / z);
199  rest = -0.5*lz*lz - PI*PI/6;
200  sgn = -1;
201  }
202  }
203 
204  const Complex<double> u2(u*u);
205 
206  return sgn*(u + u2*(bf[0] + u*horner<1>(u2, bf))) + rest;
207 }
208 
209 /**
210  * @brief Clausen function \f$\mathrm{Cl}_2(\theta) = \mathrm{Im}(\mathrm{Li}_2(e^{i\theta}))\f$
211  * @param x real angle
212  * @return \f$\mathrm{Cl}_2(\theta)\f$
213  */
214 double clausen_2(double x) noexcept
215 {
216  const double PI = 3.141592653589793;
217  const std::complex<double> i(0.0, 1.0);
218 
219  while (x >= 2*PI) {
220  x -= 2*PI;
221  }
222 
223  while (x < 0.0) {
224  x += 2*PI;
225  }
226 
227  if (std::abs(x) < std::numeric_limits<double>::epsilon() ||
228  std::abs(x - PI) < std::numeric_limits<double>::epsilon() ||
229  std::abs(x - 2*PI) < std::numeric_limits<double>::epsilon()) {
230  return 0.0;
231  }
232 
233  return std::imag(dilog(std::exp(i*x)));
234 }
235 
236 } // namespace himalaya
Definition: H3.cpp:14
Declaration of the dilogarithm function.
double clausen_2(double x) noexcept
Clausen function .
Definition: Li2.cpp:214
double dilog(double x) noexcept
Real dilogarithm .
Definition: Li2.cpp:62
constexpr T norm_sqr(const Complex< T > &z) noexcept
Definition: complex.hpp:52
Complex< T > log(const Complex< T > &z_) noexcept
Definition: complex.hpp:45