back to table · edit · history · where entries came from · files · download
7785 bytes, as of the version from 2026-10-28 10:24 (current). Recorded here, not run.
"""Independent exact checks for Macdonald polynomials -- numberdb.org/T578.
sage -python checks.py # compute and check locally
sage -python checks.py --stored # repeat on authenticated database values
Install numberdb and keep generate.py and check.py beside this file.
No floating point tests are used. The difference eigenidentity is tested after
multiplication by the Vandermonde, so no numerical poles or cancellation arise.
Sage's independent Macdonald J algorithm uses determinantal formulas.
"""
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
import sage.rings.polynomial.laurent_polynomial_ring
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 collections import Counter
from math import prod
from pathlib import Path
import json
import os
import sys
import generate
from check import exactness, measure, agrees_with, names_its_rings
def ring(n):
return PolynomialRing(ZZ, ['x%d' % (i+1) for i in range(n)] + ['q','t'])
def independent_hook(R, lam):
q,t=R.gens()[-2:]
return prod((1-q**(width-column)*t**(sum(a>=column for a in lam)-row+1)
for row,width in enumerate(lam,1) for column in range(1,width+1)),start=R.one())
def vandermonde(x):
return prod((x[i]-x[j] for i in range(len(x)) for j in range(i+1,len(x))),start=x[0].parent().one())
def eigen_residual(f, lam):
R=f.parent(); x=R.gens()[:-2]; q,t=R.gens()[-2:]; n=len(x)
v=vandermonde(x)
lhs=R.zero()
for i in range(n):
shifted_v=prod(((t*x[a] if a==i else x[a])-(t*x[b] if b==i else x[b])
for a in range(n) for b in range(a+1,n)),start=R.one())
lhs += shifted_v*f.subs({x[i]:q*x[i]})
padded=lam+(0,)*(n-len(lam))
eigen=sum(q**padded[i]*t**(n-1-i) for i in range(n))
return lhs-eigen*v*f
def schur_alternant(R, lam):
x=R.gens()[:-2]; n=len(x); padded=lam+(0,)*(n-len(lam)); ans=R.zero()
for sigma in permutations(range(n)):
inversions=sum(sigma[i]>sigma[j] for i in range(n) for j in range(i+1,n))
ans+=(-1)**inversions*prod((x[i]**(padded[sigma[i]]+n-1-sigma[i]) for i in range(n)),start=R.one())
return R(R.fraction_field()(ans)/vandermonde(x))
def stored_values(doc):
block=doc['Numbers']
if isinstance(block,list):
records=block
else:
records=[{'params':{'n':n,'lambda':lam},'number':value['number'] if isinstance(value,dict) else value}
for n,group in block.items() for lam,value in group.items()]
return {(int(r['params']['n']),tuple(map(int,str(r['params']['lambda']).split(',')))):
ring(int(r['params']['n']))(r['number']) for r in records}
def get_neighbor(tid):
path=Path(__file__).with_name(tid+'.json')
return json.loads(path.read_text()) if path.exists() else numberdb.table(tid)
def run(stored=False):
gen=generate.MacdonaldPolynomials()
enumeration=[(int(p['n']),tuple(map(int,p['lambda'].split(',')))) for p in gen.enumerate()]
expected=[(n,tuple(map(int,lam))) for n,bound in ((2,4),(3,3),(4,2))
for d in range(2,bound+1) for lam in Partitions(d) if len(lam)<=n]
assert enumeration==expected and len(expected)==14
values=stored_values(numberdb.table('T578')) if stored else {key:generate.polynomial(*key) for key in expected}
assert list(values)==expected
assert not exactness(values)
assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
# Controls first: D(1) and D(p1) have known eigenvalues; a wrong quadratic fails.
for n in (2,3,4):
R=ring(n);x=R.gens()[:-2];q,t=R.gens()[-2:]
assert eigen_residual(R.one(),())==0
assert eigen_residual(sum(x),(1,))==0
assert eigen_residual(sum(z*z for z in x),(2,))!=0
assert schur_alternant(R,(1,))==sum(x)
assert schur_alternant(R,(1,1))==sum((a*b for a,b in combinations(x,2)),R.zero())
print('Controls: constants, p1, e2 and a deliberately wrong eigenfunction pass.',flush=True)
QT=PolynomialRing(QQ,['q','t']);q0,t0=QT.gens()
J=SymmetricFunctions(QT.fraction_field()).macdonald(q=q0,t=t0).J()
def sage_j(key):
n,lam=key
return ring(n)(str(J[list(lam)].expand(n,alphabet=['x%d'%(i+1) for i in range(n)])))
mismatches=agrees_with(values,sage_j)
assert not mismatches, mismatches
print('Independent Sage J: all 14 agree.',flush=True)
schur_doc=get_neighbor('T122'); hall_doc=get_neighbor('T261')
schurs=stored_values(schur_doc); halls=stored_values(hall_doc)
counts=Counter()
for (n,lam),f in values.items():
R=f.parent();x=R.gens()[:-2];q,t=R.gens()[-2:];d=sum(lam)
assert all(sum(exp[:n])==d for exp in f.dict())
for sigma in permutations(range(n)):
assert f(*[x[i] for i in sigma],q,t)==f
leading=R.zero(); padded=lam+(0,)*(n-len(lam))
for exponent,coefficient in f.dict().items():
shape=tuple(sorted(exponent[:n],reverse=True))
assert all(sum(shape[:k])<=sum(padded[:k]) for k in range(1,n+1))
if tuple(exponent[:n])==padded:
leading+=coefficient*q**exponent[n]*t**exponent[n+1]
c=independent_hook(R,lam)
assert leading==c
assert eigen_residual(f,lam)==0
counts['difference_eigenidentity']+=1
s=schur_alternant(R,lam)
assert f.subs({t:q})==c.subs({t:q})*s
assert s==R(schurs[(n,lam)])
counts['schur_specialization_and_corpus']+=1
b=prod((1-t**j for m in Counter(lam).values() for j in range(1,m+1)),start=R.one())
assert f.subs({q:0})==b*R(halls[(n,lam)])
counts['hall_littlewood_corpus']+=1
reduced=f.subs({x[-1]:0})
expected_reduced=R.zero() if len(lam)==n else R(generate.polynomial(n-1,lam))
assert reduced==expected_reduced
counts['stability']+=1
if all(a==1 for a in lam):
elementary=sum((prod(choice,start=R.one()) for choice in combinations(x,d)),R.zero())
factor=prod((1-t**j for j in range(1,d+1)),start=R.one())
assert f==factor*elementary
counts['column']+=1
# Omitted boundary families, through the largest retained degree.
for n in range(1,5):
R=ring(n);x=R.gens()[:-2];q,t=R.gens()[-2:]
assert R(generate.polynomial(n,()))==1
assert R(generate.polynomial(n,(1,)))==(1-t)*sum(x)
R=ring(1);x,q,t=R.gens()
for d in range(1,5):
assert generate.polynomial(1,(d,))==x**d*prod((1-q**j*t for j in range(d)),start=R.one())
result={'table':'T578','mode':'stored' if stored else 'local','entries':len(values),
'checks':dict(counts),'sage_j':14,'exactness':True,'controls':True,
'measure':measure(values),'versions':{'sage':SAGE_VERSION,'numberdb':numberdb.__version__},
'skill_manifest':'../20261028T101159Z-build-skills/manifest.json',
'skill_sha256':'e37efb7e2ededf6c1d7134c673f152f648f26e0267c556aebdf3ec65765d8b4c'}
print('RESULT_JSON',json.dumps(result),flush=True)
print('VALUES_JSON',json.dumps([{'params':{'n':str(n),'lambda':','.join(map(str,lam))},'number':str(f)} for (n,lam),f in values.items()]))
return result
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
key=sys.stdin.read().strip()
if '=' in key and key.split('=',1)[0].strip().isidentifier(): key=key.split('=',1)[1].strip().strip('\"\'')
numberdb.configure(api_key=key)
run('--stored' in sys.argv)