back to table · edit · history · where entries came from · files · download
7930 bytes, as of the version from 2026-10-28 09:51 (current). Recorded here, not run.
"""Independent checks for Schur Q-polynomials -- numberdb.org/T577.
Run: sage -python checks.py
Or: sage -python checks.py --stored (authorized API key needed for draft).
Requires generate.py and the NumberDB campaign's unmodified check.py.
Independent coefficient enumeration follows marked shifted tableaux in
Rosengren, arXiv:math/0603086v3, Section 2.1, equations (2.1)-(2.3).
The tableau enumerator uses no q-series or Pfaffian code.
Sage's Hall-Littlewood Q basis supplies a second coefficient comparison.
"""
import sys, os, json
from collections import Counter
from itertools import combinations
from pathlib import Path
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
import sage.rings.polynomial.laurent_polynomial_ring
from sage.symbolic.ring import SR # initializes expand() dependencies
from sage.combinat.sf.sf import SymmetricFunctions
from sage.env import SAGE_VERSION
from generate import SchurQ, ring, schur_q, strict_partitions, DEGREE_BOUNDS
from check import exactness, measure, agrees_with, names_its_rings
def tableaux(n, lam):
"""Count weight monomials of marked shifted semistandard tableaux.
Symbols 0,1,2,3,... stand for 1',1,2',2,... . Marked diagonals allowed.
Cells are (row,column), with row i starting in column i (zero based).
"""
cells = [(i,j) for i,a in enumerate(lam) for j in range(i,i+a)]
filling = {}
row_marked, column_unmarked = set(), set()
exponent = [0]*n
counts = Counter()
def visit(k):
if k == len(cells):
counts[tuple(exponent)] += 1
return
i,j = cells[k]
lower = max(filling.get((i,j-1),0), filling.get((i-1,j),0))
for symbol in range(lower,2*n):
letter = symbol//2
marked = symbol%2 == 0
seen = row_marked if marked else column_unmarked
identity = (i,letter) if marked else (j,letter)
if identity in seen:
continue
seen.add(identity)
filling[(i,j)] = symbol
exponent[letter] += 1
visit(k+1)
exponent[letter] -= 1
del filling[(i,j)]
seen.remove(identity)
visit(0)
return ring(n)(dict(counts))
def flatten(document):
block = document['Numbers']
if isinstance(block,list):
return [(e['params'],e['number']) for e in block]
out=[]
for n,sub in block.items():
for lam,entry in sub.items():
if not isinstance(entry,dict) or 'number' in entry:
out.append(({'n':str(n),'lambda':str(lam)},entry['number'] if isinstance(entry,dict) else entry))
return out
def run(stored=False):
# Controls run before any general comparisons.
R=ring(2); x,y=R.gens()
assert tableaux(2,()) == 1
assert tableaux(2,(1,)) == 2*x+2*y
assert tableaux(2,(2,)) == 2*x*x+4*x*y+2*y*y
assert tableaux(2,(2,1)) == 4*x*x*y+4*x*y*y
assert tableaux(1,(2,1)) == 0
Sym=SymmetricFunctions(QQ)
H=Sym.hall_littlewood(t=-1).Q()
P=Sym.hall_littlewood(t=-1).P()
m=Sym.monomial(); p=Sym.power(); s=Sym.schur()
def expand(f,n):
return ring(n)(str(f.expand(n,alphabet=list(ring(n).variable_names()))))
assert expand(H[2],2)==2*x*x+4*x*y+2*y*y
assert expand(H[2,1],2)==4*x*x*y+4*x*y*y
assert expand(s[2,1],2)==x*x*y+x*y*y
print('Controls: empty, linear, quadratic, (2,1), impossible shape and Sage normalizations pass.',flush=True)
gen=SchurQ()
wanted=list(gen.enumerate())
if stored:
doc=numberdb.table('T577')
records=flatten(doc)
assert [par for par,v in records]==wanted, 'stored identities/order differ from enumeration'
values={(int(par['n']),tuple(map(int,par['lambda'].split(',')))):ring(int(par['n']))(v)
for par,v in records}
else:
values={(int(par['n']),tuple(map(int,par['lambda'].split(',')))):gen.value(par) for par in wanted}
# Selection independently enumerated by subsets of {1,...,bound}.
expected=[]
for n,bound in sorted(DEGREE_BOUNDS.items()):
shapes=[c for k in range(1,n+1) for c in combinations(range(bound,0,-1),k)
if 2<=sum(c)<=bound]
shapes.sort(key=lambda lam:(sum(lam),tuple(-a for a in lam)))
expected.extend((n,l) for l in shapes)
assert list(values)==expected
assert len(values)==40
assert not exactness(values)
assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
independent={key:tableaux(*key) for key in values}
assert not agrees_with(values,lambda key:independent[key])
print('All 40 polynomials agree coefficient by coefficient with marked shifted tableau enumeration.',flush=True)
counts=Counter()
for (n,lam),f in values.items():
xs=ring(n).gens(); d=sum(lam)
assert f==expand(H[list(lam)],n)
assert f==2**len(lam)*expand(P[list(lam)],n)
counts['sage_hall_littlewood']+=1
assert all(sum(e)==d for e in f.dict())
assert all(c>0 and c%2**len(lam)==0 for c in f.coefficients())
for j in range(n-1):
xx=list(xs); xx[j],xx[j+1]=xx[j+1],xx[j]
assert f(*xx)==f
counts['adjacent_transpositions']+=1
# Independent lower-alphabet tableau polynomial, including vanished shapes.
assert ring(n)(str(tableaux(n-1,lam)))==f(*list(xs[:-1])+[0])
counts['alphabet_stability']+=1
xx=list(xs); xx[-1]=-xx[-2]
expected_cancel=R.zero() if n==2 else tableaux(n-2,lam)
assert f(*xx)==ring(n)(str(expected_cancel))
counts['opposite_pair_cancellation']+=1
if lam==tuple(range(len(lam),0,-1)):
assert f==2**len(lam)*expand(s[list(lam)],n)
counts['staircase_schur']+=1
power=p(H[list(lam)])
assert all(all(a%2 for a in mu) for mu in power.monomial_coefficients())
counts['odd_power_sum_support']+=1
for n in range(1,6):
assert schur_q(n,())==1
assert schur_q(n,(1,))==2*sum(ring(n).gens())
for d in range(1,7):
assert schur_q(1,(d,))==2*ring(1).gen()**d
# Published neighbor values, read from API in both run modes.
hall_doc=numberdb.table('T261')
for par,text in flatten(hall_doc):
n=int(par['n']); lam=tuple(map(int,par['lambda'].split(',')))
if (n,lam) in values:
A=PolynomialRing(ZZ,list(ring(n).variable_names())+['t'])
f=A(text); result=f(*list(ring(n).gens())+[-1])
assert values[(n,lam)]==2**len(lam)*result
counts['stored_hall_littlewood']+=1
schur_doc=numberdb.table('T122')
for par,text in flatten(schur_doc):
n=int(par['n']); lam=tuple(map(int,par['lambda'].split(',')))
if (n,lam) in values and lam==tuple(range(len(lam),0,-1)):
assert values[(n,lam)]==2**len(lam)*ring(n)(text)
counts['stored_staircase_schur']+=1
# Test the exact Programs snippet, one non-boundary entry.
assert expand(H[3,2],3)==values[(3,(3,2))]
print(json.dumps({'mode':'stored' if stored else 'computed','sage':SAGE_VERSION,
'numberdb':numberdb.__version__,'counts':dict(counts),
'measure':measure(values)},indent=2,default=str),flush=True)
print('VALUES_JSON='+json.dumps([{'params':{'n':str(n),'lambda':','.join(map(str,lam))},
'number':str(v)} for (n,lam),v in values.items()]))
if __name__=='__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN'):
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 or os.environ.get('CHECK_STORED')=='1')