File: _spence.pxd

package info (click to toggle)
python-scipy 0.18.1-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 75,464 kB
  • ctags: 79,406
  • sloc: python: 143,495; cpp: 89,357; fortran: 81,650; ansic: 79,778; makefile: 364; sh: 265
file content (103 lines) | stat: -rw-r--r-- 2,853 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# Implement Spence's function, a.k.a. the dilogarithm, for complex
# arguments. Note that our definition differs from that in the sources
# by the mapping z -> 1 - z.
#
# Sources
# [1] Zagier, "The Dilogarithm Function"
# [2] functions.wolfram.com
# [3] Ginsberg, Zaborowski, "The Dilogarithm Function of a Real Argument"
#
# Author: Josh Wilson
#
# Released under the same license as Scipy.

import cython
from _complexstuff cimport zlog1, zabs, zdiv

# Relative tolerance for the series
DEF TOL = 2.220446092504131e-16
DEF PISQ_6 = 1.6449340668482264365


@cython.cdivision(True)
cdef inline double complex cspence(double complex z) nogil:
    """
    Compute Spence's function for complex arguments. The strategy is:
    - If z is close to 0, use a series centered at 0.
    - If z is far away from 1, use the reflection formula

    spence(z) = -spence(z/(z - 1)) - pi**2/6 - ln(z - 1)**2/2

    to move close to 1. See [1].
    - If z is close to 1, use a series centered at 1.

    """
    if zabs(z) < 0.5:
        # This step isn't necessary, but this series converges faster.
        return cspence_series0(z)
    elif zabs(1 - z) > 1:
        # Use of zdiv is an UGLY HACK.
        return -cspence_series1(zdiv(z, z - 1)) - PISQ_6 - 0.5*zlog1(z - 1)**2
    else:
        return cspence_series1(z)


@cython.cdivision(True)
cdef inline double complex cspence_series0(double complex z) nogil:
    """
    A series centered at z = 0; see

    http://functions.wolfram.com/10.07.06.0005.02

    """
    cdef:
        int n
        double complex zfac = 1
        double complex sum1 = 0
        double complex sum2 = 0
        double complex term1, term2

    if z == 0:
        return PISQ_6

    for n in range(1, 500):
        zfac *= z
        term1 = zfac/n**2
        sum1 += term1
        term2 = zfac/n
        sum2 += term2
        if zabs(term1) <= TOL*zabs(sum1) and zabs(term2) <= TOL*zabs(sum2):
            break
    return PISQ_6 - sum1 + zlog1(z)*sum2


@cython.cdivision(True)
cdef inline double complex cspence_series1(double complex z) nogil:
    """
    A series centered at z = 1 which enjoys faster convergence than
    the Taylor series. See [3]. The number of terms used comes from
    bounding the absolute tolerance at the edge of the radius of
    convergence where the sum is O(1).

    """
    cdef:
        int n
        double complex zfac = 1
        double complex res = 0
        double complex term, zz

    if z == 1:
        return 0
    z = 1 - z
    zz = z**2
    for n in range(1, 500):
        zfac *= z
        # Do the divisions one at a time to guard against overflow
        term = ((zfac/n**2)/(n + 1)**2)/(n + 2)**2
        res += term
        if zabs(term) <= TOL*zabs(res):
            break
    res *= 4*zz
    res += 4*z + 5.75*zz + 3*(1 - zz)*zlog1(1 - z)
    res /= 1 + 4*z + zz
    return res