checks.py

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')