generate.py

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

4311 bytes, as of the version from 2026-10-25 17:54 (current). Recorded here, not run.

"""Orders of the automorphism groups of the Niemeier lattices -- numberdb.org/T496.

Run with SageMath (or a compatible modular Sage installation):
    sage -pip install numberdb
    sage -python generate.py              # verify all stored entries
    sage -python generate.py --fill       # write entries to this existing draft
    sage -python checks.py                # independent arithmetic/source checks

For authenticated draft access set NUMBERDB_API_KEY, or pipe the key on stdin
with NUMBERDB_KEY_FROM_STDIN=1. Filling requires the key and does not publish
or offer the draft for review. The table is the complete set of 24 classes.

The input borcherds-norm0.txt is the unchanged file downloaded from
https://math.berkeley.edu/~reb/lattices/norm0 . Its first field is the Coxeter
number, fifth the root system, and sixth the quotient order |O(L)/W(R)|.
For the rootless Leech lattice W(R) is trivial. Chenevier (2020), Section 5.2,
independently identifies all 23 nontrivial-root quotients; Example 1.3 and
Table C.5 identify the Leech group. The computation uses exact integer
products of Weyl-group orders. No enumeration of a huge automorphism group
and no floating-point arithmetic is required.
"""
import os
import re
import sys
from pathlib import Path

import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ

HERE = Path(__file__).resolve().parent


def source_rows():
    rows = []
    for line in (HERE / 'borcherds-norm0.txt').read_text().splitlines():
        fields = line.split()
        h, root, quotient = int(fields[0]), fields[4], ZZ(fields[5])
        key = 'Leech' if root == 'None' else root.upper()
        parts = [(t.upper(), int(n), int(m or 1))
                 for t, n, m in re.findall(r'([ade])(\d+)(?:\^(\d+))?', root)]
        if root != 'None':
            reconstructed = ''.join(t.lower() + str(n) + ('^' + str(m) if m > 1 else '')
                                    for t, n, m in parts)
            assert reconstructed == root
            assert sum(n*m for _, n, m in parts) == 24
        rows.append((key, h, parts, quotient))
    assert len(rows) == len({r[0] for r in rows}) == 24
    return rows


def factorial_z(n):
    result = ZZ(1)
    for k in range(2, n + 1):
        result *= k
    return result


def weyl_order(letter, rank):
    if letter == 'A':
        return factorial_z(rank + 1)
    if letter == 'D':
        return ZZ(2)**(rank - 1) * factorial_z(rank)
    return ZZ({6: 51840, 7: 2903040, 8: 696729600}[rank])


def order_of(row):
    _, _, components, quotient = row
    order = quotient
    for letter, rank, multiplicity in components:
        order *= weyl_order(letter, rank)**multiplicity
    return order


class NiemeierAutomorphismOrders(numberdb.Generator):
    table = 'T496'
    parameters = ('root_system',)
    type = 'Z'
    rigour = 'exact'
    files = ('generate.py', 'checks.py', 'check.py', 'borcherds-norm0.txt',
             'source-notes.md', 'independent-quotients.json')

    def enumerate(self):
        for key, _, _, _ in source_rows():
            yield {'root_system': key}

    def value(self, params, digits):
        row = next(r for r in source_rows() if r[0] == params['root_system'])
        value = order_of(row)
        if row[0] == 'Leech':
            return {'number': value, 'comment':
                    r'The Leech lattice $\Lambda_{24}$ has no roots. Its full '
                    r'automorphism group is $\mathrm{Co}_0=2.\mathrm{Co}_1$.'}
        if row[0] == 'E8^3':
            return {'number': value, 'comment':
                    r'The orthogonal direct sum $E_8\oplus E_8\oplus E_8$.'}
        return value


if __name__ == '__main__':
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        token = sys.stdin.read().strip()
        if '=' in token and token.split('=', 1)[0].isupper():
            token = token.split('=', 1)[1].strip().strip('\"\'')
        os.environ['NUMBERDB_API_KEY'] = token
    gen = NiemeierAutomorphismOrders()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(gen.publish(message='Compute all 24 exact orders; check quotient groups, positive-root heights and the mass formula'))
    else:
        report = gen.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)