back to table · edit · history · where entries came from · files · download
3914 bytes, as of the version from 2026-10-29 19:59 (current). Recorded here, not run.
"""Volumes of right-angled hyperbolic antiprisms and Löbell polyhedra -- numberdb.org/T611.
Run with SageMath or passagemath and the numberdb client installed:
sage -pip install numberdb
sage -python generate.py # verify all stored entries
sage -python generate.py --fill # send entries to the existing draft
Use NUMBERDB_API_KEY for authenticated access to the private draft.
A_n is ideal, n >= 3; L_n is compact, n >= 5. Curvature is -1 and
n counts base sides. Default selection: every A_3,...,A_30 and L_5,...,L_30.
See argument-selection.md for both parameter justifications.
The antiprism formula is Thurston's, as quoted by Repovs--Vesnin (2024),
equation (1); the Lobell formula is Inoue (2008), Theorem 4.1.
The equivalent positive argument pi/2-2*theta uses oddness of Lambda.
Lambda(x) = Im(Li_2(exp(2*i*x)))/2, evaluated entirely with Arb balls.
All arguments here are strictly between 0 and pi/2; the principal Li_2
branch is therefore unambiguous. No decimal approximation defines an angle.
80 extra bits allow cancellation and propagation through asin and Li_2.
The checks measure the worst retained accuracy and compare independent
integrals. Generator outputs are enclosures; the client writes 100 significant
decimal digits with its one-unit-last-place interval convention.
"""
import os
import sys
import numberdb.sage as numberdb
from sage.rings.real_arb import RealBallField
from sage.rings.complex_arb import ComplexBallField
WORKING_GUARD = 80
def lobachevsky(angle, complex_field):
z = (complex_field(0, 2) * complex_field(angle)).exp()
result = z.polylog(2).imag() / 2
assert result.is_finite()
return result
def volume(family, n, digits=100):
n = int(n)
bits = numberdb.bits(digits, losing=WORKING_GUARD)
real = RealBallField(bits)
comp = ComplexBallField(bits)
pi = real.pi()
if family == 'antiprism' and n >= 3:
args = [pi / 4 + pi / (2*n), pi / 4 - pi / (2*n)]
assert all(0 < a < pi / 2 for a in args)
answer = 2*n*sum(lobachevsky(a, comp) for a in args)
elif family == 'lobell' and n >= 5:
theta = (1 / (2 * (pi / n).cos())).arcsin()
args = [theta, theta + pi/n, theta - pi/n, pi/2 - 2*theta]
assert all(0 < a < pi / 2 for a in args)
answer = real(n)/2 * (2*lobachevsky(args[0], comp)
+ sum(lobachevsky(a, comp) for a in args[1:]))
else:
raise ValueError('Use antiprism with n >= 3 or lobell with n >= 5')
assert answer.is_finite() and answer > 0
assert real(answer.rad()) < abs(answer) * real(10)**(-digits-10)
return answer
class RightAngledVolumes(numberdb.Generator):
table = 'T611'
parameters = ('family', 'n')
type = 'R'
digits = 100
rigour = 'proven'
files = ('generate.py', 'checks.py', 'argument-selection.md')
def enumerate(self, maximum=30):
for family, minimum in [('antiprism', 3), ('lobell', 5)]:
for n in range(minimum, maximum+1):
yield {'family': family, 'n': n}
def value(self, params, digits):
family, n = str(params['family']), int(params['n'])
result = {'number': volume(family, n, digits)}
if family == 'antiprism' and n == 3:
result['comment'] = 'The regular ideal right-angled octahedron.'
elif family == 'lobell' and n == 5:
result['comment'] = 'The compact regular right-angled dodecahedron.'
return result
if __name__ == '__main__':
if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
os.environ['NUMBERDB_API_KEY'] = sys.stdin.read().strip()
generator = RightAngledVolumes()
if '--fill' in sys.argv:
print(generator.publish(message='Fill 54 volumes from certified Arb balls'))
else:
report = generator.verify(sample=None)
print(report)
sys.exit(0 if report.ok else 1)