generate.py

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

5959 bytes, as of the version from 2026-10-24 21:50 (current). Recorded here, not run.

"""Mean distance to a region's centroid -- numberdb.org/T469.

Run with SageMath (or passagemath and the numberdb package):

    sage -pip install numberdb
    sage -python generate.py                 # verify all stored values
    sage -python generate.py --fill          # fill the draft with an API key
    sage -python checks.py                   # independent local checks
    sage -python checks.py --stored          # check database values as well

--fill calls Generator.publish to send entries; it does not publish the table
or offer it for review. The API key belongs in NUMBERDB_API_KEY. An unattended
runner may instead set NUMBERDB_KEY_FROM_STDIN=1 and pipe the key on stdin.
NUMBERDB_PUBLISH=1 is equivalent to --fill for the repository's Sage runner.

One symbolic parameter names the region. The square, cube and equilateral
triangle have side 1; the rectangle has sides 1 and 2; the disk, solid ball,
circle, sphere and regular polygons with 5 through 8 sides have radius 1.
The latter radius is the circumradius. Uniformity uses intrinsic length,
area or volume, including arc length and surface area on the boundaries.

Sources: Li and Qiu (2016), DOI 10.1155/2016/8371750, equation (23) and
Examples 1-3; Bailey, Borwein and Crandall, Box integrals,
DOI 10.1016/j.cam.2006.06.010, equations (4), (8), (10) and section 7 of
the author manuscript https://www.davidhbailey.com/dhbpapers/boxintegrals.pdf.
A unit d-ball has radial density d*r**(d-1), hence exact mean d/(d+1).
The circle and sphere have constant distance 1. Exact rationals stay exact.

Arb propagates enclosures through the closed forms. WORKING_GUARD=80 bits
is checked by checks.py, including finiteness and a relative radius smaller
than 1e-120 for every irrational entry at the requested 100 digits.
Independent quadrature is an agreement check, not the certification source.
"""

import os
import sys
import numberdb.sage as numberdb
from sage.rings.rational_field import QQ
from sage.rings.real_arb import RealBallField

WORKING_GUARD = 80
REGIONS = (
    'unit-interval', 'unit-square', 'rectangle-1x2', 'equilateral-triangle',
    'unit-disk', 'regular-pentagon', 'regular-hexagon', 'regular-heptagon',
    'regular-octagon', 'unit-cube', 'unit-ball-3', 'unit-circle', 'unit-sphere-2',
)
POLYGONS = {'regular-pentagon': 5, 'regular-hexagon': 6,
            'regular-heptagon': 7, 'regular-octagon': 8}
EXACT = {'unit-interval': QQ(1)/4, 'unit-disk': QQ(2)/3,
         'unit-ball-3': QQ(3)/4, 'unit-circle': QQ(1), 'unit-sphere-2': QQ(1)}
COMMENTS = {
    'unit-interval': r'The exact mean is $1/4$, the average of $|x-1/2|$ on $[0,1]$.',
    'unit-square': r'The exact mean is $(\sqrt2+\log(1+\sqrt2))/6$ CITE{Box}.',
    'rectangle-1x2': r'The exact mean is $[4\sqrt5+\log(2+\sqrt5)+8\log((1+\sqrt5)/2)]/24$, from the rectangle formula in CITE{LiQiu}.',
    'equilateral-triangle': r'The exact mean is $\sqrt3/9+\log(2+\sqrt3)/18$, obtained from CITE{polygon} with $n=3$ and $h=\sqrt3/6$.',
    'unit-disk': r'The exact mean is $2/3$, from the radial density $2r$ on $0\leq r\leq1$.',
    'regular-pentagon': r'The exact mean is $\frac13[1+\cos(\pi/5)\cot(\pi/5)\log(\sec(\pi/5)+\tan(\pi/5))]$.',
    'regular-hexagon': r'The exact mean is $1/3+\log3/4$, the $n=6$ case of CITE{polygon} with circumradius $1$.',
    'regular-heptagon': r'The exact mean is $\frac13[1+\cos(\pi/7)\cot(\pi/7)\log(\sec(\pi/7)+\tan(\pi/7))]$.',
    'regular-octagon': r'The exact mean is $\frac13[1+\cos(\pi/8)\cot(\pi/8)\log(\sec(\pi/8)+\tan(\pi/8))]$.',
    'unit-cube': r'The exact mean is $\sqrt3/8+\log(2+\sqrt3)/4-\pi/48$, half the corner-distance box integral $B_3(1)$ of CITE{Box}.',
    'unit-ball-3': r'The exact mean is $3/4$, from the radial density $3r^2$ on $0\leq r\leq1$.',
    'unit-circle': r'Every point has distance exactly $1$ from the centroid.',
    'unit-sphere-2': r'Every point has distance exactly $1$ from the centroid.',
}


def polygon_mean(field, n, inradius):
    theta = field.pi()/n
    sec = 1/theta.cos()
    tan = theta.tan()
    return inradius/3 * (sec + (sec+tan).log()/tan)


def compute(region, digits=100):
    if region in EXACT:
        return EXACT[region]
    field = RealBallField(numberdb.bits(digits, losing=WORKING_GUARD))
    if region == 'unit-square':
        return (field(2).sqrt()+(1+field(2).sqrt()).log())/6
    if region == 'rectangle-1x2':
        s = field(5).sqrt()
        return (4*s+(2+s).log()+8*((1+s)/2).log())/24
    if region == 'equilateral-triangle':
        return polygon_mean(field, 3, field(3).sqrt()/6)
    if region in POLYGONS:
        n = POLYGONS[region]
        return polygon_mean(field, n, (field.pi()/n).cos())
    if region == 'unit-cube':
        s = field(3).sqrt()
        return s/8+(2+s).log()/4-field.pi()/48
    raise ValueError('unknown region: '+region)


class CentroidDistance(numberdb.Generator):
    table = 'T469'
    parameters = ('region',)
    type = 'R'
    digits = 100
    rigour = 'proven'
    files = ('generate.py', 'checks.py', 'check.py', 'dry_run.py')

    def enumerate(self):
        for region in REGIONS:
            yield {'region': region}

    def value(self, params, digits):
        region = params['region']
        return {'number': compute(region, digits), 'comment': COMMENTS[region]}


def configure_stdin():
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN') == '1':
        key = sys.stdin.read().strip()
        if '=' in key and key.split('=', 1)[0].isupper():
            key = key.split('=', 1)[1].strip().strip('\"\'')
        numberdb.configure(api_key=key)


if __name__ == '__main__':
    configure_stdin()
    generator = CentroidDistance()
    if '--fill' in sys.argv or os.environ.get('NUMBERDB_PUBLISH') == '1':
        print(generator.publish(message='Fill centroid distances with exact rationals and certified closed forms.'))
    else:
        report = generator.verify(sample=None)
        print(report)
        sys.exit(0 if report.ok else 1)