back to table · edit · history · where entries came from · files · download
4295 bytes, as of the version from 2026-10-26 16:11 (current). Recorded here, not run.
"""Power-series coefficients of spheroidal eigenvalues -- numberdb.org/T520.
Run with SageMath:
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python generate.py --fill # fill this existing draft with a key
NUMBERDB_API_KEY supplies the writing key. NUMBERDB_KEY_FROM_STDIN=1 reads
it from stdin instead. --fill calls Generator.publish(), which writes entries
and attachments; it does not publish or offer the draft.
The quantity is chi_n(sqrt(q)) = sum_k a_(0,n,k) q^k, q=c^2, for the bounded
order-zero angular problem on [-1,1]. DLMF lambda_n^0(q)=chi_n(sqrt(q))-q.
All arithmetic is rational. The eigenvector gauge is coefficient of P_n = 1;
it does not claim unit L2 norm. Multiplication by x^2 moves a Legendre degree
by at most two, so order r has finite support through degree n+2r. No finite
matrix approximation or numerical differentiation is used.
Selection and proof: argument-selection.md. Independent checks: checks.py.
"""
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from functools import lru_cache
import os
import sys
N_MAX = 11
K_MAX = 12
def multiply_x_squared(vector):
"""Apply x^2 in the unnormalised Legendre basis, exactly."""
result = {}
for degree, coefficient in vector.items():
l = degree
transitions = {
l + 2: QQ((l + 1) * (l + 2)) / QQ((2*l + 1) * (2*l + 3)),
l: QQ((l + 1)**2) / QQ((2*l + 1) * (2*l + 3))
+ QQ(l*l) / QQ((2*l + 1) * (2*l - 1)),
}
if l >= 2:
transitions[l - 2] = QQ(l * (l - 1)) / QQ((2*l + 1) * (2*l - 1))
for target, weight in transitions.items():
result[target] = result.get(target, QQ(0)) + coefficient * weight
return {l: v for l, v in result.items() if v}
@lru_cache(maxsize=None)
def perturbation(n, order):
"""Return eigenvalue coefficients and Legendre eigenvector corrections."""
if n < 0 or order < 0:
raise ValueError('Mode and Taylor order must be nonnegative')
eigenvalues = [QQ(n * (n + 1))]
vectors = [{n: QQ(1)}]
for r in range(1, order + 1):
applied = multiply_x_squared(vectors[r - 1])
eigenvalues.append(applied.get(n, QQ(0)))
current = {}
for l in range(n % 2, n + 2*r + 1, 2):
if l == n:
continue
rhs = -applied.get(l, QQ(0))
for j in range(1, r + 1):
rhs += eigenvalues[j] * vectors[r - j].get(l, QQ(0))
value = rhs / QQ(l*(l + 1) - n*(n + 1))
if value:
current[l] = value
vectors.append(current)
return tuple(eigenvalues), tuple(vectors)
def coefficient(n, k):
"""One exact coefficient, including k=0."""
return perturbation(int(n), int(k))[0][int(k)]
class SpheroidalCoefficients(numberdb.Generator):
table = 'T520'
parameters = ('m', 'n', 'k')
type = 'Q'
rigour = 'exact'
digits = 100 # Irrelevant for exact rationals: no rounding is performed.
files = ('generate.py', 'checks.py', 'argument-selection.md',
'numerical-evidence.md', 'skill-manifest.json')
def enumerate(self, n_max=N_MAX, k_max=K_MAX):
for n in range(n_max + 1):
for k in range(k_max + 1):
yield {'m': 0, 'n': n, 'k': k}
def value(self, params, digits):
m, n, k = (int(params[name]) for name in self.parameters)
if m != 0 or n < 0 or k < 0:
raise ValueError('This table is restricted to m=0, n>=0, k>=0')
return perturbation(n, max(K_MAX, k))[0][k]
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
secret = sys.stdin.read().strip()
if '=' in secret and secret.split('=', 1)[0].isidentifier():
secret = secret.split('=', 1)[1].strip().strip('\"').strip("'")
os.environ['NUMBERDB_API_KEY'] = secret
generator = SpheroidalCoefficients()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_FILL') == '1':
print(generator.publish(message='Exact spheroidal Taylor coefficients, independently checked in two bases'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)