back to table · edit · history · where entries came from · files · download
3867 bytes, as of the version from 2026-10-28 09:51 (current). Recorded here, not run.
"""Schur Q-polynomials -- numberdb.org/T577.
Run with SageMath (install numberdb once with sage -pip install numberdb):
sage -python generate.py # verify every stored entry
sage -python checks.py # independent checks, no writes
sage -python generate.py --fill # fill the existing draft using API key
The Q normalization is q_0=1 and sum q_r z^r=prod (1+x_i*z)/(1-x_i*z).
Two-row Q functions and a recursively expanded Pfaffian give every polynomial.
All arithmetic is in ZZ[x1,...,xn], with no floating point or division.
See argument-selection.md for the discrete families and finite bounds.
Filling sends entries and source files; it does not publish or offer the table.
"""
import os
import sys
from functools import lru_cache
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
DEGREE_BOUNDS = {2: 6, 3: 6, 4: 6, 5: 4}
@lru_cache(None)
def ring(n):
return PolynomialRing(ZZ, n, names=tuple('x%d' % i for i in range(1, n + 1)))
def strict_partitions(d, maximum=None):
"""Strict partitions in descending lexicographic order."""
if d == 0:
yield ()
return
if maximum is None:
maximum = d
for first in range(min(d, maximum), 0, -1):
for rest in strict_partitions(d - first, first - 1):
yield (first,) + rest
@lru_cache(None)
def one_row(n, degree):
R = ring(n)
q = [R.one()] + [R.zero()] * degree
for x in R.gens():
q = [q[r] + sum((2*x**j*q[r-j] for j in range(1, r+1)), R.zero())
for r in range(degree+1)]
return tuple(q)
@lru_cache(None)
def schur_q(n, lam):
"""Exact Schur Q for a strict partition, including zero specializations."""
lam = tuple(lam)
R = ring(n)
if not lam:
return R.one()
if any(a <= 0 for a in lam) or any(a <= b for a,b in zip(lam,lam[1:])):
raise ValueError('positive strictly decreasing parts required')
q = one_row(n, sum(lam))
padded = lam if len(lam) % 2 == 0 else lam + (0,)
def pair(a,b):
return q[a]*q[b] + 2*sum(((-1)**j*q[a+j]*q[b-j]
for j in range(1,b+1)), R.zero())
def pfaffian(parts):
if not parts:
return R.one()
return sum(((-1)**(j+1)*pair(parts[0],parts[j])
*pfaffian(parts[1:j]+parts[j+1:])
for j in range(1,len(parts))), R.zero())
return R(pfaffian(padded))
class SchurQ(numberdb.Generator):
table = 'T577'
parameters = ('n', 'lambda')
type = 'Z[]'
rigour = 'exact'
files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md',
'numerical-evidence.md', 'skill-evidence.json')
def enumerate(self):
for n, bound in sorted(DEGREE_BOUNDS.items()):
for d in range(2,bound+1):
for lam in strict_partitions(d):
if len(lam) <= n:
yield {'n':str(n), 'lambda':','.join(map(str,lam))}
def value(self, params, digits=100):
return schur_q(int(params['n']), tuple(map(int,params['lambda'].split(','))))
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)
generator = SchurQ()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH'):
print(generator.publish(message='Fill Schur Q-polynomials with exact, independently checked expansions',
assisted_by='codex-cli'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)