back to table · edit · history · where entries came from · files · download
6045 bytes, as of the version from 2026-10-25 17:24 (current). Recorded here, not run.
"""Orders of the automorphism groups of the classical lattices -- numberdb.org/T495
Run with SageMath (install numberdb once with sage -pip install numberdb):
sage -python generate.py # verify all stored entries
sage -python generate.py --fill # write entries to the existing draft
sage -python checks.py # independent exact checks
The draft needs NUMBERDB_API_KEY even for reading. --fill sends numbers; it
never publishes or offers the draft. For a key piped on stdin, set
NUMBERDB_KEY_FROM_STDIN=1. NUMBERDB_PUBLISH=1 is the build-wrapper equivalent
of --fill. Companion files are available on https://numberdb.org/files/T495.
The quantity is the full real linear isometry group, with both determinants
allowed, not the Weyl group or the group modulo its centre. The family names,
dimension domains and Gram matrices match T147. For dimensions 11, 12 and 13,
Lambda means the Catalogue's MAX variant. Root lattices have minimal norm 2,
Z^n has minimal norm 1, and the laminated and Coxeter-Todd matrices have minimal
norm 4. Scaling does not change an automorphism group; duality preserves its
order. All families stop at dimension 24, the Leech lattice endpoint of this
classical-lattice comparison.
Classical orders are generated by integer products. The other orders are
recomputed by PARI qfauto from the exact, attached grams.json matrices,
transcribed from the Nebe-Sloane Catalogue of Lattices and compared with T147.
qfauto computes the full group and returns its order and matrix generators.
Each returned generator must preserve the Gram matrix. Results are memoized
within one run. No floating point division, numerical precision or rounding
is involved. checks.py compares independent group-order formulas, the stored
Coxeter Poincare polynomials at 1, sporadic group orders and catalogue facts.
"""
import functools
import json
import os
from pathlib import Path
import sys
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.matrix.constructor import matrix
from sage.libs.pari.all import pari
TOP = 24
FAMILIES = (
('Z', range(1, TOP + 1)),
('A', range(1, TOP + 1)),
('A*', range(3, TOP + 1)),
('D', range(4, TOP + 1)),
('D*', range(5, TOP + 1)),
('E', (6, 7, 8)),
('E*', (6, 7)),
('Lambda', range(9, TOP + 1)),
('K', (12,)),
)
def product(items):
result = ZZ(1)
for item in items:
result *= ZZ(item)
return result
def catalogue_gram(name):
rows = json.loads(Path(__file__).with_name('grams.json').read_text())[name]
n = len(rows)
assert all(len(row) == i + 1 for i, row in enumerate(rows))
return matrix(ZZ, n, n, lambda i, j: rows[max(i, j)][min(i, j)])
@functools.lru_cache(None)
def catalogue_order(name):
gram = pari(catalogue_gram(name))
order, generators = gram.qfauto()
for g in generators:
assert g.mattranspose() * gram * g == gram
assert abs(g.matdet()) == 1
return ZZ(order)
def order(family, n):
n = int(n)
if family.endswith('*'):
return order(family[0], n)
if family == 'Z':
return product(2 * k for k in range(1, n + 1))
if family == 'A':
return ZZ(2) if n == 1 else 2 * product(range(1, n + 2))
if family == 'D':
return ZZ(1152) if n == 4 else product(2 * k for k in range(1, n + 1))
if family == 'E':
degrees = {6: (2, 5, 6, 8, 9, 12),
7: (2, 6, 8, 10, 12, 14, 18),
8: (2, 8, 12, 14, 18, 20, 24, 30)}[n]
return (2 if n == 6 else 1) * product(degrees)
if family == 'Lambda':
return catalogue_order('Leech' if n == 24 else 'LAMBDA%d' % n)
if family == 'K' and n == 12:
return catalogue_order('K12')
raise ValueError('unsupported lattice: %s, %s' % (family, n))
COMMENTS = {
('Z', 2): 'The square lattice.',
('A', 2): 'The hexagonal lattice.',
('A', 3): 'The face-centred cubic lattice.',
('A*', 3): 'The body-centred cubic lattice.',
('D', 4): r'Triality contributes the factor $6$ over the Weyl group of type $D_4$.',
('E', 6): r'The full group has twice the order of the Weyl group of type $E_6$.',
('Lambda', 11): r'The catalogue lattice $\Lambda_{11}^{\mathrm{max}}$.',
('Lambda', 12): r'The catalogue lattice $\Lambda_{12}^{\mathrm{max}}$.',
('Lambda', 13): r'The catalogue lattice $\Lambda_{13}^{\mathrm{max}}$.',
('Lambda', 16): 'The Barnes-Wall lattice.',
('Lambda', 23): r'The order is twice that of HREF{Orders_of_sporadic_finite_simple_groups#Co2}[$\mathrm{Co}_2$].',
('Lambda', 24): r'The Leech lattice; its full group is $\mathrm{Co}_0$, of twice the order of HREF{Orders_of_sporadic_finite_simple_groups#Co1}[$\mathrm{Co}_1$].',
('K', 12): r'The Coxeter-Todd lattice as a real lattice. Its complex-linear isometry group has index $2$, CITE{CoxeterTodd}.',
}
class ClassicalLatticeAutomorphisms(numberdb.Generator):
table = 'T495'
parameters = ('family', 'n')
type = 'Z'
rigour = 'exact'
files = ('generate.py', 'grams.json', 'checks.py', 'root_checks.py', 'check.py', 'catalogue-sources.json')
def enumerate(self):
for family, dimensions in FAMILIES:
for n in dimensions:
yield {'family': family, 'n': str(n)}
def value(self, params, digits):
family, n = params['family'], int(params['n'])
result = {'number': order(family, n)}
if (family, n) in COMMENTS:
result['comment'] = COMMENTS[family, n]
return result
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
numberdb.configure(api_key=sys.stdin.read().strip())
generator = ClassicalLatticeAutomorphisms()
if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
print(generator.publish(message='Fill 133 exact full automorphism-group orders'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)