# Short calculation of the dilogarithm?

Is there a nice way to implement the dilogarithm function for real values, without actually performing the integration?

A series solution would have been nice, but the series around $0$ has a convergence radius of $1$, so it doesn't work for larger $x$. Ideally, I'm looking for an "elegant" method, rather than the "fastest" method.

For reference, the gsl library uses over 650 lines of code for the implementation, but I'm looking for somthing a bit more compact.

• But you have identities like $$\mathrm{Li}_2(z) = -\mathrm{Li}_2\left(z^{-1}\right)-\frac12 \ln^2(-z)-\frac{\pi^2}{6}$$ (concerning series solution). – Start wearing purple Aug 8 '13 at 10:36
• Or you could also use $$\text{Li}_{2}(z)= \frac{ \pi^{2}}{3}- \frac{ \ln^{2}(z)}{2}- \sum_{k=1}^{ \infty} \frac{1}{k^{2}z^{k}}-i \pi \ln(z),$$ and truncate the sum to however many terms you want. – Adrian Keister Aug 8 '13 at 10:38
• What is your actual programming goal? "Nice" is rather vague, and "compact" is rarely something to strive for.... – Hurkyl Aug 8 '13 at 11:00

## 3 Answers

The cited Wikipedia page gives the expansion $$\mathrm{Li}_2(x)=\frac{\pi^2}{3}-\frac12\log(x)^2-\sum_{k=1}^\infty\frac1{k^2x^k}-i\pi\log(x)\tag{1}$$ for $x\ge1$. Combined with $$\mathrm{Li}_2(x)=\sum_{k=1}^\infty\frac{x^k}{k^2}\tag{2}$$ for $|x|\le1$, you should get what you need.

Equation $(1)$ also works for $x\le-1$ if we use $\log(x)=\log(-x)-\pi i$: $$\mathrm{Li}_2(x)=-\frac{\pi^2}{6}-\frac12\log(-x)^2-\sum_{k=1}^\infty\frac1{k^2x^k}\tag{3}$$ for $x\le-1$.

Inversion Formula \begin{align} \mathrm{Li}_2(x) &=-\int_0^x\log(1-t)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}-\int_1^x\log(1-t)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}-\pi i\log(x)-\int_1^x\log(t-1)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}-\pi i\log(x)-\int_{1/x}^1\log(1/t-1)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}-\pi i\log(x)-\int_{1/x}^1\Big(\log(1-t)-\log(t)\Big)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}-\pi i\log(x)+\frac{\pi^2}{6}+\int_0^{1/x}\log(1-t)\frac{\mathrm{d}t}{t}+\int_{1/x}^1\log(t)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{3}-\pi i\log(x)-\mathrm{Li}_2(1/x)-\frac12\log(x)^2\tag{4}\\ &=-\frac{\pi^2}{6}-\mathrm{Li}_2(1/x)-\frac12\log(-x)^2\tag{5} \end{align} $(2)$ and $(4)$ prove expansion $(1)$. $(2)$ and $(5)$ prove expansion $(3)$.

Duplication Formula \begin{align} \mathrm{Li}_2(x) &=-\int_0^x\log(1-t)\frac{\mathrm{d}t}{t}\\ &=-\int_0^1\log(1-t)\frac{\mathrm{d}t}{t}+\int_x^1\log(1-t)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}+\int_0^{1-x}\log(t)\frac{\mathrm{d}t}{1-t}\\ &=\frac{\pi^2}{6}-\log(x)\log(1-x)+\int_0^{1-x}\log(1-t)\frac{\mathrm{d}t}{t}\\ &=\frac{\pi^2}{6}-\log(x)\log(1-x)-\mathrm{Li}_2(1-x)\tag{6}\\ \mathrm{Li}_2(x)+\mathrm{Li}_2(1-x) &=\frac{\pi^2}{6}-\log(x)\log(1-x)\tag{7} \end{align}

• What about large negative $x$? the value will be real, and so the formula above (containing $i$) will not apply – nbubis Aug 8 '13 at 11:07
• @nbubis: I have converted $(1)$ for negative $x$. – robjohn Aug 8 '13 at 11:24

I do not know how well this compares to other methods, but it is one I have devised for the purpose of computing the polylogarithm for values of z near the unit circle. In the case of the dilogarithm, the group of anharmonic ratios allows one to reduce the computation in the general case to a fundamental region for that group. However that still appears to involve computation for values near (or on) the unit circle, in particular the cube roots of -1 in the right half plane are fixed points for that group, so one needs to compute those. And nearby values.

A rational approximation to the polylogarithm can be had with poles in the branch cut (where they ought to be for any decent approximation). Runge's theorem guarantees there will be many.

We arrived at this one:

$$\text{Li}_{s}(z)\approx \frac{\pi z}{Γ(s)\sqrt{N}} \sum_{k=-N}^{N}\frac{e^{k\pi/\sqrt{N}}}{(e^{k\pi/\sqrt{N}}+1-z)(1+e^{k\pi/\sqrt{N}})} \log^{s-1}(1+e^{k\pi/\sqrt{N}})$$ $$= z\sum_{k=-N}^{N}\frac{w_{k}}{λ_{k}-z}$$

which is a Riemann sum for the integral representation

$$\text{Li}_{s}(z)=\frac{z}{Γ(s)}\int_{-\infty}^{\infty} \frac{e^{\tau}}{(e^{\tau}+1-z)(1+e^{\tau})} \log^{s-1}(1+e^{\tau})d\tau$$

which follows from a simple change of variables in the well known integral representation

$$\text{Li}_{s}(z)=\frac{1}{Γ(s)}\int_0^\infty \frac{ze^{-t}}{1-ze^{-t}}t^{s-1}dt$$

which can be obtained easily from the Laplace transform.

• Thank you for your answer! I fixed up the LaTeX a bit, would you mind checking that this is still correct? – nbubis Nov 7 '13 at 18:32
• By the way I have not closely examined the selection of the distance 0≤h≤(π/(√N)) in the Riemann sum. I gave the formula for the largest possible, which seems to work well, but a smaller h could be better. As to the LaTex, it looks OK but this is what stackexchange made of what I constructed in Scientific Word, and on my screen the spacing and wraparound is a little off. The limits on the bottom integral should be 0 to infinity, which is what it sort-of looks like. – Andrew Mullhaupt Nov 7 '13 at 18:33
• Transformations of the dilogarithm by anharmonic ratios (Cf. maths.dur.ac.uk/~dma0hg/dilog.pdf): Li₂((1/z)) = -Li₂(z)-((π²)/6)-(1/2)(log(1/z))² Li₂(1-z) = -Li₂(z)+((π²)/6)-(1/2)(log z)(log(1-z)) Li₂((z/(z-1))) = -Li₂(z)-(1/2)(log(1-z))² Li₂(((z-1)/z)) = Li₂(z)-((π²)/6)-(1/2)(log(z))²+(1/2)(log z)(log(1-z)) Li₂((1/(1-z))) = Li₂(z)+((π²)/6)+(log(1/z))(log(1-z))-(1/2)(log(1-z))² The problem is reduced to computing the dilogarithm for {|1-z|≤1}∩{Rez≤(1/2)} so the power series works well except for the points near the cube roots of unity. – Andrew Mullhaupt Nov 9 '13 at 15:47
• More numerical experiments suggest that h = pi sqrt(2/N) is a good, but possibly not optimal choice. At z=1, the power series requires 1,000,000 terms to get relative error of about 6x10^-7, but the Riemann sum with 20 terms and h = pi sqrt(2/20) has relative error about 8x10^-9. So if this function is evaluated in an inner loop of another calculation, the Riemann sum should be used near the unit circle. – Andrew Mullhaupt Apr 23 '14 at 12:55

For further reference, based on @robjohn's answer, I came up with the following code for the calculation of the Spence function: $$\Phi(x)=-\int_0^x \frac{\ln|1-y|}{y}dy$$

double spenceFunction(double x, double precision = 1.0e-13) {
if (x >= 0.5 && x < 1.0) {
return  M_PI*M_PI/6.0 - spenceFunction(1.0 - x) - std::log( x)*std::log(1-x);
}
if (x >= 1.0) {
return  M_PI*M_PI/3.0 - spenceFunction(1.0 / x) - std::log( x)*std::log(x) / 2.0;
}
if (x < -1.0) {
return -M_PI*M_PI/6.0 - spenceFunction(1.0 / x) - std::log(-x)*std::log(-x) / 2.0;
}

double sum = 0.0, i = 1.0;
for (; i < INT_MAX; i += 1.0) {
double delta = std::pow(x,i) / (i*i);
sum += delta;
if (std::abs(delta/sum) < precision) break;
}
return sum;
}

• You can avoid the ${\tt\mbox{std::}}$ "scope operator" whenever you add at the program beginning ( after the ${\tt\mbox{#include} < \cdots >}$ ) the line ${\tt\mbox{using namespace std;}}$ – Felix Marin Aug 13 '14 at 3:17
• @FelixMarin - I'm aware of that of course, It's just that "using namespace std" is considered bad form, for good reason. See here for instance: stackoverflow.com/questions/1452721/… – nbubis Aug 13 '14 at 19:57
• @nubis Thanks. I didn't know what you said about the 'bad form'. I'll check the link you gave about it. – Felix Marin Aug 16 '14 at 21:22