generate.py

back to table · edit · history · where entries came from · files · download

4084 bytes, as of the version from 2026-11-10 02:09 (current). Recorded here, not run.

"""Elliptic trilogarithm L32 at CM torsion points -- numberdb.org/T703.

Run with SageMath:
    sage -pip install numberdb
    sage -python generate.py           # verify the stored table

The Table class enumerates 494 points. See argument-selection.md for the
CM orders, finite torsion groups and noncanonical cutoff. All logarithms
are natural. The marking is tau=(-b+i*sqrt(abs(Delta)))/2. Odd Delta gives
negative q. This file computes only L32, with Samart's logarithmic correction.

Each J3 term is evaluated in Arb using Re log(1-z), with log1p to preserve
relative accuracy for small z. The n=0 term for r=0 is exactly zero,
including x=1. For a=pi*sqrt(abs(Delta)), t=exp(-a), xi=r/N, the two tails
start at h=K+1+xi and h=K+1-xi. The absolute sum of either omitted tail is
bounded by
 a^2*t^h/(1-t^h) * [h^2/(1-t)+2*h*t/(1-t)^2+t*(1+t)/(1-t)^3].
This uses |log|1-z||<=|z|/(1-|z|) and exact geometric moment sums. Both
tail bounds are added to the ball. No rounded external argument is used.
192 extra working bits accommodate the small real-axis values; checks.py
measures the actual minimum retained relative precision. Tail truncation
requires 30 extra decimal digits relative to the current nonzero sum.
"""
import math
import os
import sys
import numberdb.sage as numberdb
from sage.rings.real_arb import RealBallField
from sage.rings.complex_arb import ComplexBallField
from sage.rings.rational_field import QQ

DISCRIMINANTS = (-3,-4,-7,-8,-11,-12,-16,-19,-27,-28,-43,-67,-163)
WORKING_GUARD = 192


def compute(Delta, N, r, s, digits=100, diagnostics=False):
    Delta,N,r,s = map(int,(Delta,N,r,s))
    if Delta>=0 or Delta%4 not in (0,1) or N<1 or not (0<=r<N and 0<=s<N):
        raise ValueError('Invalid marked CM torsion coordinates')
    R=RealBallField(numberdb.bits(digits,losing=WORKING_GUARD))
    C=ComplexBallField(R.precision())
    a=R.pi()*R(-Delta).sqrt(); t=(-a).exp(); xi=QQ(r)/QQ(N)
    b=int(Delta%4!=0); q=(-1 if b else 1)*t
    phase=C(0,R.pi()*R(QQ(2*s-b*r)/QQ(N))).exp()
    x=(-a*R(xi)).exp()*phase
    total= -a**3*R(xi)**2*(1-R(xi))**2/4
    if r:
        total += (a*R(xi))**2*(-x).log1p().real()
    def tail(h):
        h=R(h); th=(-a*h).exp(); v=1-t
        return a*a*th/(1-th)*(h*h/v+2*h*t/(v*v)+t*(1+t)/(v**3))
    power=C(1)
    tolerance=R(10)**(-digits-30)
    for k in range(1,10000):
        power *= q
        total += (a*(k+R(xi)))**2*(-power*x).log1p().real()
        total += (a*(k-R(xi)))**2*(-power/x).log1p().real()
        error=tail(k+1+xi)+tail(k+1-xi)
        if abs(total)>0 and error<abs(total)*tolerance:
            result=total.add_error(error)
            assert result.is_finite() and not result.contains_zero()
            if diagnostics:
                return result,{'terms_each_side':k,'relative_bits':int(result.accuracy()),'tail_bound':str(error)}
            return result
    raise ArithmeticError('Tail did not converge')


class Table(numberdb.Generator):
    table='T703'
    parameters=('Delta','N','r','s')
    type='R'
    digits=100
    rigour='proven'
    files=('generate.py','checks.py','check.py','argument-selection.md',
           'numerical-evidence.json','source-checks.md')

    def enumerate(self, max_order=6):
        for D in DISCRIMINANTS:
            for N in range(1,max_order+1):
                for r in range(N):
                    for s in range(N):
                        if math.gcd(N,math.gcd(r,s))==1 and (r,s)<=((-r)%N,(-s)%N):
                            yield dict(Delta=D,N=N,r=r,s=s)

    def value(self,params,digits):
        value=compute(*(params[k] for k in self.parameters),digits=digits)
        if tuple(int(params[k]) for k in self.parameters)==(-4,1,0,0):
            return {'number':value,'comment':'The origin on the square torus. CITE{formula-square} gives its Dirichlet-beta expression.'}
        return value


if __name__=='__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
        numberdb.configure(api_key=sys.stdin.read().strip())
    result=Table().verify(sample=None)
    print(result)
    sys.exit(0 if result.ok else 1)