back to table · edit · history · where entries came from · files · download
8379 bytes, as of the version from 2026-10-28 08:03 (current). Recorded here, not run.
"""Independent checks of Jack polynomials -- numberdb.org/T575.
Run beside generate.py and check.py:
sage -python checks.py
sage -python checks.py stored-table.json
A saved table document can also be nested under Numbers, as returned by the API.
The generator is imported only to obtain locally computed values. Validation
uses Sage's Jack implementation, a differential operator, and a separate Schur
alternant. No numerical tolerances or floating-point calculations are used.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.symbolic.ring import SR # Initialize symbolic code before Symmetrica.
from sage.combinat.sf.sf import SymmetricFunctions
from sage.combinat.partition import Partitions
from sage.env import SAGE_VERSION
from itertools import permutations, combinations
from math import factorial
from collections import Counter
from pathlib import Path
import json
import sys
from check import exactness, agrees_with, measure, names_its_rings
B = PolynomialRing(QQ, 'a')
S = SymmetricFunctions(B.fraction_field())
J = S.jack(t=B.gen()).J()
def ring(n):
return PolynomialRing(ZZ, ['x%d' % i for i in range(1,n+1)] + ['a'])
def expected_identities():
return [(n,tuple(lam)) for n in [2,3,4]
for d in range(2,(4 if n==4 else 5)+1)
for lam in Partitions(d) if len(lam)<=n]
def sage_jack(key):
n, shape = key
return ring(n)(str(J[list(shape)].expand(n,alphabet=['x%d' % i for i in range(1,n+1)])))
def determinant(rows):
n = len(rows)
R = rows[0][0].parent()
out = R(0)
for order in permutations(range(n)):
inversions = sum(order[i]>order[j] for i in range(n) for j in range(i+1,n))
term = R((-1)**inversions)
for i,j in enumerate(order):
term *= rows[i][j]
out += term
return out
def schur(n, shape):
R=ring(n); xs=R.gens()[:-1]
padded=shape+(0,)*(n-len(shape))
top=determinant([[xs[i]**(padded[j]+n-j-1) for j in range(n)] for i in range(n)])
bottom=determinant([[xs[i]**(n-j-1) for j in range(n)] for i in range(n)])
q,r=top.quo_rem(bottom)
assert r==0
return q
def scalar_hook(n,shape):
R=ring(n); a=R.gen(n); out=R(1)
conjugate=[sum(part>=j for part in shape) for j in range(1,shape[0]+1)]
for i,part in enumerate(shape,1):
for j in range(1,part+1):
out *= (part-j)*a+conjugate[j-1]-i+1
return out
def differential(f,n):
xs=f.parent().gens()[:-1]; a=f.parent().gen(n)
out=a*sum((x*x*f.derivative(x).derivative(x) for x in xs), f.parent()(0))
for i in range(n):
for j in range(i+1,n):
num=xs[i]**2*f.derivative(xs[i])-xs[j]**2*f.derivative(xs[j])
q,r=num.quo_rem(xs[i]-xs[j])
assert r==0
out+=2*q
return out
def compositions(total,n):
if n==1:
yield (total,)
else:
for first in range(total+1):
for tail in compositions(total-first,n-1):
yield (first,)+tail
def row_formula(n,d):
R=ring(n); xs=R.gens()[:-1]; a=R.gen(n); out=R(0)
for powers in compositions(d,n):
denominator=1
for power in powers:
denominator*=factorial(power)
coefficient=ZZ(factorial(d))//denominator
term=R(coefficient)
for x,k in zip(xs,powers):
term*=x**k
for j in range(k):
term*=1+j*a
out+=term
return out
def validate(values):
# Nonzero controls, before trusting any checker.
R=ring(2); x,y,a=R.gens()
assert schur(2,(2,))==x*x+x*y+y*y
assert scalar_hook(2,(2,))==a+1
assert differential(x+y,2)==2*(x+y)
known=(a+1)*(x*x+y*y)+2*x*y
assert row_formula(2,2)==known
assert sage_jack((2,(2,)))==known
assert differential(known,2)==(2*a+4)*known
assert differential(known+x*y,2)!=(2*a+4)*(known+x*y)
assert list(values)==expected_identities(), 'Missing, extra or misordered parameter identities'
assert not exactness(values)
disagreements=agrees_with(values,sage_jack)
assert not disagreements, disagreements
counts=Counter()
for (n,shape),f in values.items():
R=ring(n); f=R(f); xs=R.gens()[:-1]; a=R.gen(n); d=sum(shape)
assert all(sum(e[:-1])==d for e in f.dict())
assert all(c>0 for c in f.coefficients())
for i in range(n-1):
images=list(R.gens()); images[i],images[i+1]=images[i+1],images[i]
assert f(*images)==f
padded=shape+(0,)*(n-len(shape))
leading=sum((c*a**tuple(e)[-1] for e,c in f.dict().items() if tuple(e[:-1])==padded), R(0))
assert leading==scalar_hook(n,shape)
for e in f.dict():
mu=sorted(e[:-1],reverse=True)
assert all(sum(mu[:i])<=sum(padded[:i]) for i in range(1,n+1))
eigen=a*sum(k*(k-1) for k in shape)+2*sum((n-i-1)*k for i,k in enumerate(shape))
assert differential(f,n)==eigen*f
candidates=[tuple(mu) for mu in Partitions(d) if len(mu)<=n]
eigenvalues=[a*sum(k*(k-1) for k in mu)+2*sum((n-i-1)*k for i,k in enumerate(mu)) for mu in candidates]
assert len(set(eigenvalues))==len(candidates), 'Eigenoperator degeneracy needs another check'
assert f.subs({a:1})==scalar_hook(n,shape).subs({a:1})*schur(n,shape)
counts['sage_jack']+=1; counts['differential_eigenidentity']+=1
counts['schur_specialization']+=1; counts['symmetry_homogeneity_triangularity_normalization']+=1
if len(shape)==1:
assert f==row_formula(n,d)
counts['row_formula']+=1
if all(k==1 for k in shape):
elementary=R(0)
for chosen in combinations(xs,d):
term=R(1)
for x in chosen: term*=x
elementary+=term
assert f==factorial(d)*elementary
counts['column_formula']+=1
if len(shape)==n:
assert f.subs({xs[-1]:0})==0
counts['alphabet_vanishing']+=1
if n>2 and (n-1,shape) in values:
assert f.subs({xs[-1]:0})==R(values[n-1,shape])
counts['alphabet_stability']+=1
# Compare every available Schur entry with the separately downloaded corpus.
corpus=Path(__file__).with_name('T122.json')
if corpus.exists():
doc=json.loads(corpus.read_text())
for (n,shape),f in values.items():
spelling=','.join(map(str,shape)); source=doc['Numbers'].get(str(n),{}).get(spelling)
if source is not None:
if isinstance(source,dict): source=source['number']
assert schur(n,shape)==ring(n)(source)
counts['stored_T122_schur']+=1
return {'table':'T575','entries':len(values),'checks':dict(counts),
'measurement':measure(values),'controls':'known nonzero and deliberately corrupted polynomials passed',
'sage_version':SAGE_VERSION,'numberdb_version':numberdb.__version__}
def read_document(path):
doc=json.loads(Path(path).read_text()); out={}
entries=doc.get('Numbers',doc.get('Data',[]))
if isinstance(entries,list):
for record in entries:
n=int(record['params']['n']); shape=tuple(map(int,str(record['params']['lambda']).split(',')))
out[n,shape]=ring(n)(record['number'])
else:
for n,group in entries.items():
for shape,entry in group.items():
if isinstance(entry,dict): entry=entry['number']
out[int(n),tuple(map(int,shape.split(',')))]=ring(int(n))(entry)
return out
if __name__=='__main__':
if len(sys.argv)>1:
values=read_document(sys.argv[1])
else:
from generate import JackPolynomials, jack
assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
G=JackPolynomials()
values={(int(p['n']),tuple(map(int,p['lambda'].split(',')))):G.value(p,100) for p in G.enumerate()}
# Boundary formulas excluded from storage, verified for all measured degrees.
for n in [1,2,3,4]:
R=ring(n); assert jack(n,())==1
assert jack(n,(1,))==sum(R.gens()[:-1])
for d in range(2,6): assert jack(1,(d,))==row_formula(1,d)
print(json.dumps(validate(values),indent=2))