back to table · edit · history · where entries came from · files · download
7903 bytes, as of the version from 2026-10-27 15:20 (current). Recorded here, not run.
"""Independent exact checks for Chiral unitary Weingarten functions, T552.
Download this file beside generate.py. Run:
sage -python checks.py # independently check local values
sage -python checks.py --stored # check all stored values (key for draft)
No generator routines are used in the independent Gram systems or moment checks.
The optional built-in control checks run before evaluating table values.
"""
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from itertools import permutations
from collections import Counter
from functools import lru_cache
from math import factorial, comb, prod
import json
import sys
import os
def read_rational(text):
parts=str(text).split('/')
assert len(parts) in (1,2)
return QQ(int(parts[0]))/QQ(int(parts[1]) if len(parts)==2 else 1)
@lru_cache(None)
def symmetric_group(k):
return tuple(permutations(range(k)))
def cycle_type(p):
unused=set(range(len(p))); cycles=[]
while unused:
j=min(unused); length=0
while j in unused:
unused.remove(j); length+=1; j=p[j]
cycles.append(length)
return tuple(sorted(cycles,reverse=True))
def compose_inverse(p,q):
inverse=[0]*len(p)
for i,j in enumerate(p): inverse[j]=i
return tuple(inverse[q[i]] for i in range(len(q)))
def gaussian_elimination(rows, rhs):
a=[[QQ(x) for x in row]+[QQ(y)] for row,y in zip(rows,rhs)]
size=len(rhs)
for col in range(size):
pivot=next(row for row in range(col,size) if a[row][col])
a[col],a[pivot]=a[pivot],a[col]
divisor=a[col][col]
a[col]=[x/divisor for x in a[col]]
for row in range(size):
if row!=col:
multiple=a[row][col]
a[row]=[x-multiple*y for x,y in zip(a[row],a[col])]
return [row[-1] for row in a]
@lru_cache(None)
def independent_coefficients(a,b,k):
group=symmetric_group(k)
representatives={}
for p in group: representatives.setdefault(cycle_type(p),p)
types=sorted(representatives,reverse=True); index={mu:i for i,mu in enumerate(types)}
rows=[]; rhs=[]; n=a+b
for mu in types:
sigma=representatives[mu]; row=[0]*len(types)
for tau in group:
row[index[cycle_type(compose_inverse(tau,sigma))]] += n**len(cycle_type(tau))
rows.append(row)
rhs.append(prod(n if part%2==0 else a-b for part in mu))
solution=gaussian_elimination(rows,rhs)
return dict(zip(types,solution))
def diagonal_moment(a,b,k):
# Q=sum_{j<=a}|U_{j1}|^2 is Beta(a,b), and W_11=2Q-1.
# Rising factorial moments E Q^j=(a)_j/(a+b)_j.
return sum((QQ(comb(k,j)*2**j*(-1)**(k-j)*prod(range(a,a+j)))
/QQ(prod(range(a+b,a+b+j)))) for j in range(k+1))
def controls():
assert cycle_type((1,0,2)) == (2,1)
assert compose_inverse((1,2,0),(1,2,0)) == (0,1,2)
assert gaussian_elimination([[2,1],[1,3]],[1,2]) == [QQ(1)/QQ(5),QQ(3)/QQ(5)]
known=independent_coefficients(1,1,2)
assert known[(1,1)] == -QQ(1)/QQ(3) and known[(2,)] == QQ(2)/QQ(3)
assert diagonal_moment(1,1,2) == QQ(1)/QQ(3)
assert diagonal_moment(1,1,4) == QQ(1)/QQ(5)
assert diagonal_moment(2,1,1) == QQ(1)/QQ(3)
bad=dict(known); bad[(2,)]+=1
assert sum(bad[cycle_type(p)] for p in symmetric_group(2)) != diagonal_moment(1,1,2)
return 8
def validate(values):
expected={}
for a in range(1,8):
for b in range(1,min(a,8-a)+1):
for k in range(1,min(4,a+b)+1):
for mu in {cycle_type(p) for p in symmetric_group(k)}:
expected[(a,b,mu)]=independent_coefficients(a,b,k)[mu]
assert len(expected)==163 and set(values)==set(expected)
for key,value in values.items(): assert value==expected[key], (key,value,expected[key])
# Full permutation equation, not just a representative of each class.
equations=0; moments=0; small=0; balanced=0
for a,b in sorted({(a,b) for a,b,mu in values}):
n=a+b
for k in range(1,min(4,n)+1):
group=symmetric_group(k)
for sigma in group:
lhs=sum(QQ(n**len(cycle_type(tau)))*values[(a,b,cycle_type(compose_inverse(tau,sigma)))] for tau in group)
rhs=prod(n if part%2==0 else a-b for part in cycle_type(sigma))
assert lhs==rhs,(a,b,k,sigma,lhs,rhs)
equations+=1
moment=sum(values[(a,b,cycle_type(p))] for p in group)
assert moment==diagonal_moment(a,b,k),(a,b,k,moment)
moments+=1
known={(1,):QQ(a-b)/QQ(n),(1,1):QQ((a-b)**2-1)/QQ(n*n-1),(2,):QQ(4*a*b)/QQ(n*(n*n-1))}
for mu,value in known.items():
assert values[(a,b,mu)]==value
small+=1
for (a,b,mu),value in values.items():
if a==b and sum(mu)%2:
assert value==0
balanced+=1
return {'independently_solved_entries':len(values),'full_permutation_equations':equations,
'beta_moment_checks':moments,'source_small_cases':small,'balanced_odd_zeros':balanced}
def check_unitary_convolution(values, unitary):
# T549 is an independently built table in the same Haar normalization.
block=unitary['Numbers']
if isinstance(block,dict):
kernel={(int(n),tuple(map(int,mu.split(',')))):read_rational(value if not isinstance(value,dict) else value['number'])
for n,mus in block.items() for mu,value in mus.items()}
else:
kernel={(int(r['params']['N']),tuple(map(int,r['params']['mu'].split(',')))):read_rational(r['number']) for r in block}
for (a,b,mu),value in values.items():
k=sum(mu); n=a+b
sigma=next(p for p in symmetric_group(k) if cycle_type(p)==mu)
answer=sum(QQ(prod(n if part%2==0 else a-b for part in cycle_type(tau)))
*kernel[(n,cycle_type(compose_inverse(tau,sigma)))] for tau in symmetric_group(k))
assert answer==value,(a,b,mu,answer,value)
return len(values)
def main():
control_count=controls()
stored='--stored' in sys.argv or os.environ.get('NUMBERDB_CHECK_STORED')=='1'
if stored:
if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
token=sys.stdin.read().strip()
if '=' in token and token.split('=',1)[0] in ('NUMBERDB_KEY','NUMBERDB_API_KEY'):
token=token.split('=',1)[1].strip().strip('\"\'')
numberdb.configure(api_key=token)
doc=numberdb.table('T552')
values={}
block=doc['Numbers']
if isinstance(block,dict):
records=[{'params':{'a':a,'b':b,'mu':mu},'number':value if not isinstance(value,dict) else value['number']}
for a,bs in block.items() for b,mus in bs.items() for mu,value in mus.items()]
else:
records=block
for entry in records:
p=entry['params']; mu=tuple(map(int,str(p['mu']).split(',')))
key=(int(p['a']),int(p['b']),mu)
assert key not in values
values[key]=read_rational(entry['number'])
else:
from generate import ChiralUnitaryWeingarten, weingarten
gen=ChiralUnitaryWeingarten()
values={(p['a'],p['b'],tuple(map(int,p['mu'].split(',')))):gen.value(p,100) for p in gen.enumerate()}
for (a,b,mu),value in values.items():
assert weingarten(b,a,mu)==(-1)**sum(mu)*value
report=validate(values)
for (a,b,mu),value in values.items():
assert independent_coefficients(b,a,sum(mu))[mu] == (-1)**sum(mu)*value
report['signature_reversal_checks']=len(values)
if stored:
report['unitary_convolution_checks']=check_unitary_convolution(values,numberdb.table('T549'))
report.update(controls=control_count,mode='stored' if stored else 'local',success=True)
print(json.dumps(report,indent=2))
return values,report
if __name__=='__main__':
main()