Skip to content

mathext: add dilogarithm function Li2#2059

Merged
kortschak merged 20 commits intogonum:masterfrom
Schwarf:feature/add_dilogarithm
Sep 24, 2025
Merged

mathext: add dilogarithm function Li2#2059
kortschak merged 20 commits intogonum:masterfrom
Schwarf:feature/add_dilogarithm

Conversation

@Schwarf
Copy link
Copy Markdown
Contributor

@Schwarf Schwarf commented Sep 21, 2025

This change adds Li2, the complex dilogarithm on the principal branch, to mathext.
Results have been verified against scipy.special.spence(1 - z) for a range of real and complex inputs.

@Schwarf Schwarf force-pushed the feature/add_dilogarithm branch from 728eff0 to feab458 Compare September 21, 2025 11:05
Copy link
Copy Markdown
Member

@kortschak kortschak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff --git a/mathext/dilog.go b/mathext/dilog.go
index 0eedd87c..fc3f2ae9 100644
--- a/mathext/dilog.go
+++ b/mathext/dilog.go
@@ -42,13 +42,13 @@ func Li2(z complex128) complex128 {
        // Arg(-z)= +/-Pi branch choice in log(-z) and can yield wrong imaginary signs
        // if care is not taken.
        if real(z) > 0.5 {
-               return complex(math.Pi*math.Pi/6, 0) - cmplx.Log(z)*cmplx.Log(1-z) - Li2(1-z)
+               return complex(math.Pi*math.Pi/6, 0) - complex128(cmplx.Log(z)*cmplx.Log(1-z)) - Li2(1-z)
        }
 
        // Inversion: map |z| > 1 into unit disk
        if cmplx.Abs(z) > 1 {
                logmz := cmplx.Log(-z)
-               return -complex(math.Pi*math.Pi/6, 0) - 0.5*logmz*logmz - Li2(1/z)
+               return -complex(math.Pi*math.Pi/6, 0) - complex128(0.5*logmz*logmz) - Li2(1/z)
        }
 
        // Direct series for |z| <= 1 and Re(z) <= 0.5
@@ -57,10 +57,10 @@ func Li2(z complex128) complex128 {
 
 func li2Series(z complex128) complex128 {
        const tol = 1e-15
-       sum := complex(0, 0)
+       var sum complex128
        zk := z // zk = z^k
-       for k := 1; cmplx.Abs(zk)/float64(k*k) > tol; k++ {
-               sum += zk / complex(float64(k*k), 0)
+       for k := 1.0; cmplx.Abs(zk)/(k*k) > tol; k++ {
+               sum += zk / complex(k*k, 0)
                zk *= z
        }
        return sum
diff --git a/mathext/dilog_test.go b/mathext/dilog_test.go
index dfd59c33..a9676ccc 100644
--- a/mathext/dilog_test.go
+++ b/mathext/dilog_test.go
@@ -8,48 +8,53 @@ import (
        "math"
        "math/cmplx"
        "testing"
+
+       "gonum.org/v1/gonum/cmplxs/cscalar"
 )
 
 func TestDiLogValues(t *testing.T) {
        t.Parallel()
 
        for _, test := range []struct {
-               input, want complex128
+               in, want complex128
        }{
                // Reference values were generated using Python's scipy.special.spence
                // (where Li2(z) = spence(1 - z)) and verified with a computer algebra system.
 
                // well known values
-               {0 + 0i, 0 + 0i},
-               {1 + 0i, math.Pi*math.Pi/6 + 0i},
-               {-1 + 0i, -math.Pi*math.Pi/12 + 0i},
-               {-1.000010000000000 + 0i, -0.822473964886262 + 0i},
-               {-0.999999000000000 + 0i, -0.822466340276836 + 0i},
-               {0.5 + 0i, 0.582240526465012 + 0i},
-               {2 + 0i, 2.467401100272340 - 2.177586090303602i},
+               {in: 0 + 0i, want: 0 + 0i},
+               {in: 1 + 0i, want: math.Pi*math.Pi/6 + 0i},
+               {in: -1 + 0i, want: -math.Pi*math.Pi/12 + 0i},
+               {in: -1.000010000000000 + 0i, want: -0.822473964886262 + 0i},
+               {in: -0.999999000000000 + 0i, want: -0.822466340276836 + 0i},
+               {in: 0.5 + 0i, want: 0.582240526465012 + 0i},
+               {in: 2 + 0i, want: 2.467401100272340 - 2.177586090303602i},
+
                // abs(z) < 0.5
-               {0.1 + 0.1i, 0.099751196798713 + 0.105220383905600i},
-               {-0.3 + 0.39i, -0.306174046754339 + 0.337550668621259i},
-               {0.001 - 0.49i, -0.055831393285929 - 0.478156430372353i},
+               {in: 0.1 + 0.1i, want: 0.099751196798713 + 0.105220383905600i},
+               {in: -0.3 + 0.39i, want: -0.306174046754339 + 0.337550668621259i},
+               {in: 0.001 - 0.49i, want: -0.055831393285929 - 0.478156430372353i},
+
                // 0.5 < abs(z) < 1
-               {0.5 + 0.7i, 0.359364350522653 + 0.856767712327590i},
-               {complex(math.Pi/4, -math.Pi/7), 0.828086461155377 - 0.739345385309341i},
-               {-0.8 - 0.0001i, -0.679781588954542 - 0.000073473333084i},
+               {in: 0.5 + 0.7i, want: 0.359364350522653 + 0.856767712327590i},
+               {in: complex(math.Pi/4, -math.Pi/7), want: 0.828086461155377 - 0.739345385309341i},
+               {in: -0.8 - 0.0001i, want: -0.679781588954542 - 0.000073473333084i},
+
                // abs(z) > 1
-               {5 + 0i, 1.783719161266631 - 5.056198322111862i},
-               {-10 + 0i, -4.198277886858104 + 0i},
-               {1000 + 10000i, -42.71073756884990 + 15.39396088869304i},
-               {-1791.91931 + 0.5i, -29.70223568904652 + 0.00209038439188i},
+               {in: 5 + 0i, want: 1.783719161266631 - 5.056198322111862i},
+               {in: -10 + 0i, want: -4.198277886858104 + 0i},
+               {in: 1000 + 10000i, want: -42.71073756884990 + 15.39396088869304i},
+               {in: -1791.91931 + 0.5i, want: -29.70223568904652 + 0.00209038439188i},
        } {
-               got := Li2(test.input)
+               got := Li2(test.in)
                const tol = 1e-10
                diff := cmplx.Abs(got - test.want)
-               if cmplx.Abs(test.want) > 0 {
-                       if diff/cmplx.Abs(test.want) > tol {
-                               t.Errorf("Li2(%g) relative error %g exceeds tol %g", test.input, diff/cmplx.Abs(test.want), tol)
+               if test.want != 0 {
+                       if !cscalar.EqualWithinRel(got, test.want, tol) {
+                               t.Errorf("Li2(%g) relative error %g exceeds tol %g", test.in, diff/cmplx.Abs(test.want), tol)
                        }
-               } else if diff > tol {
-                       t.Errorf("Li2(%g) abs error %g exceeds tol %g", test.input, diff, tol)
+               } else if !cscalar.EqualWithinAbs(got, test.want, tol) {
+                       t.Errorf("Li2(%g) abs error %g exceeds tol %g", test.in, diff, tol)
                }
        }
 }
@@ -67,7 +72,7 @@ func TestDiLogProperties(t *testing.T) {
        } {
                lhs := Li2(z * z)
                rhs := 2 * (Li2(z) + Li2(-z))
-               if math.Abs(real(lhs)-real(rhs)) > tol || math.Abs(imag(lhs)-imag(rhs)) > tol {
+               if !cscalar.EqualWithinAbs(lhs, rhs, tol) {
                        t.Errorf("duplication formula failed for case %d, z=%v: got %v want %v", i, z, lhs, rhs)
                }
        }
@@ -81,7 +86,7 @@ func TestDiLogProperties(t *testing.T) {
        } {
                lhs := Li2(cmplx.Conj(z))
                rhs := cmplx.Conj(Li2(z))
-               if math.Abs(real(lhs)-real(rhs)) > tol || math.Abs(imag(lhs)-imag(rhs)) > tol {
+               if !cscalar.EqualWithinAbs(lhs, rhs, tol) {
                        t.Errorf("conjugation symmetry failed for case %d, z=%v: got %v want %v", i, z, lhs, rhs)
                }
        }

@Schwarf Schwarf requested a review from kortschak September 22, 2025 20:11
Comment thread mathext/dilog.go Outdated
Comment thread mathext/dilog_test.go
Comment thread mathext/dilog_test.go
Comment thread mathext/dilog_test.go
Comment thread mathext/dilog_test.go Outdated
Comment thread mathext/dilog_test.go
@Schwarf Schwarf requested a review from kortschak September 23, 2025 14:32
Comment thread mathext/dilog_test.go Outdated
Comment thread mathext/dilog.go Outdated
Schwarf and others added 2 commits September 24, 2025 16:22
Co-authored-by: Dan Kortschak <[email protected]>
Co-authored-by: Dan Kortschak <[email protected]>
@Schwarf Schwarf requested a review from kortschak September 24, 2025 16:55
Copy link
Copy Markdown
Member

@kortschak kortschak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@kortschak kortschak merged commit 9c251ca into gonum:master Sep 24, 2025
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants