generate.py

back to table · edit · history · where entries came from · files · download

4072 bytes, as of the version from 2026-11-01 16:24 (current). Recorded here, not run.

"""Augmentation Hilbert series of finite p-groups -- numberdb.org/T653.

Run with SageMath (or a suitable passagemath installation):
    sage -pip install numberdb
    sage -python generate.py              # verify all stored entries
    sage -python generate.py --write      # fill the existing draft, with a key
    sage -python checks.py                # independent exact checks

The generator multiplies Jennings factors in ZZ[t], with no floating-point
arithmetic. The single parameter G denotes an actual finite group, using
UT-n-p for U_n(F_p) and Heis-r-p for the triple-law Heisenberg group. The
rank-one Heisenberg group is represented only by UT-3-p. Enumeration is by
increasing prime, then unitriangular size, then Heisenberg rank. See the
attached argument-selection.md for the structural families and finite bounds.

--write (or NUMBERDB_PUBLISH=1 for the campaign wrapper) writes entries into
the existing table. It does not offer, review, or publish the table.
NUMBERDB_KEY_FROM_STDIN=1 reads an API token from standard input.
"""
import os
import sys
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing

R = PolynomialRing(ZZ, 't')
t = R.gen()


def parse_group(identity):
    family, size, prime = identity.split('-')
    size, prime = int(size), int(prime)
    if not ZZ(prime).is_prime():
        raise ValueError('The characteristic must be prime')
    if family == 'UT' and size >= 2:
        return family, size, prime
    if family == 'Heis' and size >= 1:
        return family, size, prime
    raise ValueError('Use UT-n-p with n >= 2 or Heis-r-p with r >= 1')


def jennings_degrees(identity):
    family, size, prime = parse_group(identity)
    if family == 'UT':
        return prime, [(j, size-j) for j in range(1, size)]
    return prime, [(1, 2*size), (2, 1)]


class AugmentationHilbert(numberdb.Generator):
    table = 'T653'
    parameters = ('G',)
    files = ('generate.py', 'checks.py', 'check.py', 'argument-selection.md')
    type = 'Z[]'
    rigour = 'exact'
    digits = 100  # ignored for exact polynomials

    def enumerate(self):
        for p in (2, 3, 5, 7):
            for n in range(3, 7):
                degree = (p-1)*n*(n*n-1)//6
                if degree <= 80:
                    yield {'G': 'UT-%d-%d' % (n, p)}
            for r in range(2, 5):
                yield {'G': 'Heis-%d-%d' % (r, p)}

    def value(self, params, digits=100):
        identity = params['G']
        family, size, p = parse_group(identity)
        _, degrees = jennings_degrees(identity)
        polynomial = R.one()
        for weight, multiplicity in degrees:
            factor = sum((t**(weight*k) for k in range(p)), R.zero())
            polynomial *= factor**multiplicity
        label = (r'$U_{%d}(\mathbb F_{%d})$' if family == 'UT'
                 else r'$\operatorname{Heis}_{%d}(\mathbb F_{%d})$') % (size, p)
        entry = {'number': polynomial, 'param-latex': label}
        if identity == 'UT-3-2':
            entry['comment'] = (r'This group is the dihedral group of order $8$, '
                r'also $\operatorname{Heis}_1(\mathbb F_2)$. Its Hilbert series '
                r'equals the HREF{Poincaré_polynomials_of_the_finite_Coxeter_groups#B2}'
                r'[Poincaré polynomial of the Coxeter group $B_2$].')
        return entry


def main():
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        token = sys.stdin.read().strip()
        if '=' in token and token.split('=', 1)[0].strip().isidentifier():
            token = token.split('=', 1)[1].strip().strip('\"\'')
        os.environ['NUMBERDB_API_KEY'] = token
    generator = AugmentationHilbert()
    if '--write' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill exact augmentation Hilbert series'))
    else:
        report = generator.verify(sample=None)
        print(report)
        if not report.ok:
            raise SystemExit(1)


if __name__ == '__main__':
    main()