back to table · edit · history · where entries came from · files · download
5734 bytes, as of the version from 2026-11-02 08:29. Recorded here, not run.
"""Power sum of character degrees of abelian wreath products -- numberdb.org/T661.
Run with SageMath (or a sufficiently complete passagemath installation):
sage -pip install numberdb
sage -python generate.py # verify every stored entry
sage -python generate.py --fill # write entries to this draft
Set NUMBERDB_API_KEY for authenticated draft reads and writes. --fill writes
values, not publication or review status. Download argument-selection.md and
checks.py alongside this file for the selection rationale and independent checks.
Every colored multipartition is counted, including multiplicities of equal
degrees. Degrees use integer hook products and exact quotients. At n=2 the
spectrum is 2q linear characters and q(q-1)/2 characters of degree 2, so large
base orders do not require explicit multipartition enumeration. For any abelian
base of order q the degree multiset agrees with that of the cyclic base.
The default slices are (1,3..20), (2,2..12), (3,2..10), and (m^3,2) for
m=2..40. Each includes s=2,9/4,3. The first three slices also include
s=0,1,4,-2,-4, giving 421 entries on the same 77 group pairs. The domain
of s is all rational numbers. See argument-selection.md for the intrinsic
and application-derived meanings, rather than an interpolation grid.
Nonintegral powers use positive real Arb balls with 64 extra working bits.
At s=9/4, d^(9/4) is d^2*sqrt(sqrt(d)). Integral exponents give exact
integers or rational numbers, including negative exponents.
checks.py measures the retained accuracy and independently encloses roots by
integer fourth-root isolation. No rounded input is used.
"""
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField
from sage.combinat.partition import Partitions
from collections import Counter
from functools import lru_cache
from math import factorial, isqrt
import os
import sys
WORKING_GUARD = 64
def selected_pairs():
for q, first, last in ((1, 3, 20), (2, 2, 12), (3, 2, 10)):
for n in range(first, last + 1):
yield q, n
for m in range(2, 41):
yield m**3, 2
def selected_exponents(base_order):
if base_order in (1, 2, 3):
return ('0', '1', '2', '9/4', '3', '4', '-2', '-4')
return ('2', '9/4', '3')
@lru_cache(maxsize=None)
def hook_products(n):
"""Hook products of ordinary partitions, with multiplicities."""
out = Counter()
for partition in Partitions(n):
product = 1
for i, row in enumerate(partition):
for j in range(row):
leg = sum(partition[k] > j for k in range(i + 1, len(partition)))
product *= row - j + leg
out[product] += 1
return out
@lru_cache(maxsize=None)
def degree_histogram(q, n):
"""Map each exact degree to its character multiplicity."""
q, n = int(q), int(n)
if q < 1 or n < 2:
raise ValueError('q>=1 and n>=2 are required')
if n == 2:
out = {1: 2*q}
if q > 1:
out[2] = q*(q-1)//2
return out
# Convolve the hook-product distributions of the q ordered components.
state = {(0, 1): 1}
for color in range(q):
nxt = Counter()
for (size, denominator), multiplicity in state.items():
for extra in range(n-size+1):
for hooks, count in hook_products(extra).items():
nxt[size+extra, denominator*hooks] += multiplicity*count
state = nxt
out = Counter()
numerator = factorial(n)
for (size, denominator), multiplicity in state.items():
if size == n:
degree, remainder = divmod(numerator, denominator)
assert remainder == 0 and degree > 0
out[degree] += multiplicity
return dict(sorted(out.items()))
def moment(q, n, s, digits=100):
s = QQ(s)
spectrum = degree_histogram(int(q), int(n))
if s.denominator() == 1:
return sum((QQ(count)*QQ(degree)**int(s)
for degree, count in spectrum.items()), QQ(0))
if s == QQ(9)/QQ(4):
roots = {d: isqrt(isqrt(d)) for d in spectrum}
if all(roots[d]**4 == d for d in spectrum):
return sum((ZZ(count)*ZZ(d)**2*ZZ(roots[d]) for d, count in spectrum.items()), ZZ(0))
field = RealBallField(numberdb.bits(digits, losing=WORKING_GUARD))
if s == QQ(9)/QQ(4):
value = sum((field(count)*field(d)**2*field(d).sqrt().sqrt()
for d, count in spectrum.items()), field(0))
else:
value = sum((field(count)*field(d)**field(s) for d, count in spectrum.items()), field(0))
assert value.is_finite() and value > 0
return value
class CharacterDegreePowerSum(numberdb.Generator):
table = 'T661'
parameters = ('q', 'n', 's')
type = 'R'
digits = 100
rigour = 'proven'
files = ('generate.py', 'checks.py', 'argument-selection.md',
'coverage-evidence.json', 'program.py')
def enumerate(self):
for q, n in selected_pairs():
for s in selected_exponents(q):
yield {'q': str(q), 'n': str(n), 's': s}
def value(self, params, digits):
return moment(int(params['q']), int(params['n']), params['s'], digits)
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
os.environ['NUMBERDB_API_KEY'] = sys.stdin.read().strip()
generator = CharacterDegreePowerSum()
if '--fill' in sys.argv:
print(generator.publish(message='Recompute the structured character-degree moments'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)