Source code for surface_dynamics.flat_surfaces.masur_veech_volumes

r"""
Masur-Veech volumes of Abelian strata and their connected components

.. TODO::

    Implement more formulas (Moeller-Sauvaget-Zagier for Abelian differentials,
    Chen-Moeller-Zagier for quadratic principal stratum, Elise tables, Eskin-Okounkov,
    Eskin-Okounkov-Pandharipande, etc)
"""

from sage.all import ZZ, QQ, zeta, pi
from sage.arith.misc import bernoulli, factorial

from .strata import Stratum
from .abelian_strata import AbelianStratum, AbelianStratumComponent
from .quadratic_strata import QuadraticStratum, QuadraticStratumComponent

# In the table below, the volume is normalized by dividing by zeta(2g)
# These values appear in
# - Eskin-Masur-Zorich "principal boundary ..."
abelian_volumes_table = {
    # dim 2
    Stratum((0,), 1).hyperelliptic_component(): QQ((2,1)),
    # dim 4
    Stratum((2,), 1).hyperelliptic_component(): QQ((3,4)),
    # dim 5
    Stratum((1,1), 1).hyperelliptic_component(): QQ((2,3)),
    # dim 6
    Stratum((4,), 1).hyperelliptic_component(): QQ((9,64)),
    Stratum((4,), 1).odd_component(): QQ((7,18)),
    # dim 7
    Stratum((3,1), 1).unique_component(): QQ((16,45)),
    Stratum((2,2), 1).hyperelliptic_component(): QQ((1,10)),
    Stratum((2,2), 1).odd_component(): QQ((7,32)),
    # dim 8
    Stratum((6,), 1).hyperelliptic_component(): QQ((25, 1536)),
    Stratum((6,), 1).odd_component(): QQ((1,4)),
    Stratum((6,), 1).even_component(): QQ((64,405)),
    Stratum((2,1,1), 1).unique_component(): QQ((1,4)),
    # dim 9
    Stratum((5,1), 1).unique_component(): QQ((9,35)),
    Stratum((4,2), 1).odd_component(): QQ((5,42)),
    Stratum((4,2), 1).even_component(): QQ((45,512)),
    Stratum((3,3), 1).non_hyperelliptic_component(): QQ((5,27)),
    Stratum((3,3), 1).hyperelliptic_component(): QQ((1,105)),
    Stratum((1,1,1,1), 1).unique_component(): QQ((7,36)),
    # dim 10
    # Stratum((8,), k=1).hyperelliptic_component()
    # Stratum((8,), k=1).odd_component()
    # Stratum((8,), k=1).even_component()
    Stratum((4,1,1), 1).unique_component(): QQ((275,1728)),
    Stratum((3,2,1), 1).unique_component(): QQ((2,15)),
    Stratum((2,2,2), 1).odd_component(): QQ((155,2304)),
    Stratum((2,2,2), 1).even_component(): QQ((37,720)),
    # dim 11
    # Stratum((7, 1), k=1).unique_component()
    # Stratum((6, 2), k=1).odd_component()
    # Stratum((6, 2), k=1).even_component()
    # Stratum((5, 3), k=1).unique_component()
    # Stratum((4, 4), k=1).hyperelliptic_component()
    # Stratum((4, 4), k=1).odd_component()
    # Stratum((4, 4), k=1).even_component()
    Stratum((3,1,1,1), 1).unique_component(): QQ((124,1215)),
    Stratum((2,2,1,1), 1).unique_component(): QQ((131,1440))
}

[docs] def masur_veech_volume(C, rational=False, method=None): r""" Return the Masur-Veech volume of the stratum or component of stratum ``C``. INPUT: - ``C`` -- a stratum or a connected component of stratum - ``rational`` (boolean) - if ``False`` (default) return the Masur-Veech volume and if ``True`` return the Masur-Veech volume divided by `\zeta(2g)`. - ``method`` - the method to use to compute the volume either - ``"table"`` - for a table lookup (all strata up to dimension 9 and some strata up to dimension 11) - ``"CMSZ"`` - the Chen-Möller-Sauvaget-Zagier recursion (currently only implemented for the principal stratum) TESTS:: sage: from surface_dynamics import Stratum sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import masur_veech_volume sage: H4 = Stratum([4], k=1) sage: masur_veech_volume(H4, False, 'table') 61/108864*pi^6 sage: masur_veech_volume(H4, False, 'CMSZ') 61/108864*pi^6 sage: masur_veech_volume(H4.hyperelliptic_component(), False, 'table') 1/6720*pi^6 sage: masur_veech_volume(H4.hyperelliptic_component(), False, 'CMSZ') 1/6720*pi^6 sage: masur_veech_volume(H4.odd_component(), False, 'table') 1/2430*pi^6 sage: masur_veech_volume(H4.odd_component(), False, 'CMSZ') 1/2430*pi^6 sage: H6 = Stratum([6], k=1) sage: all(masur_veech_volume(C, True, 'table') == masur_veech_volume(C, True, 'CMSZ') for C in H6.components()) True TESTS:: sage: masur_veech_volume(Stratum([1,1,-2], k=1)) Traceback (most recent call last): ... ValueError: meromorphic differentials with higher order poles sage: masur_veech_volume(Stratum([1]*6, k=3)) Traceback (most recent call last): ... NotImplementedError: higher order differentials """ if not C.surface_has_finite_area(): raise ValueError('meromorphic differentials with higher order poles') if C.surface_differential_order() > 2: raise NotImplementedError('higher order differentials') if method is None: if (isinstance(C, AbelianStratum) and len(C.signature()) == 1) or \ (isinstance(C, AbelianStratumComponent) and len(C.stratum().signature()) == 1): method = 'CMSZ' else: method = 'table' if method == 'table': if isinstance(C, AbelianStratumComponent): vol = abelian_volumes_table[C] S = C.stratum() elif isinstance(C, AbelianStratum): vol = sum(abelian_volumes_table[CC] for CC in C.components()) S = C elif isinstance(C, QuadraticStratumComponent): raise NotImplementedError('quadratic differentials') elif isinstance(C, QuadraticStratum): raise NotImplementedError('quadratic differentials') else: raise ValueError('invalid input') return vol if rational else vol * zeta(2 * S.surface_genus()) elif method == 'CMSZ': if isinstance(C, AbelianStratum): if len(C.signature()) != 1: raise NotImplementedError g = C.surface_genus() # be careful, the output starts in genus g=1 return minimal_strata_CMSZ(g+1, rational=rational)[g-1] elif isinstance(C, AbelianStratumComponent): S = C.stratum() if len(S.signature()) != 1: raise NotImplementedError g = S.surface_genus() if C._name == 'hyp': return minimal_strata_hyp(g, rational) stratum_volume = minimal_strata_CMSZ(g + 1, rational)[g-1] spin_diff = minimal_strata_spin_diff(g + 1,rational)[g-1] volume = (stratum_volume + spin_diff) / 2 if C.spin() == 0 else (stratum_volume - spin_diff) / 2 if C.spin() == S.hyperelliptic_component().spin(): volume -= minimal_strata_hyp(g, rational) return volume else: raise NotImplementedError else: raise ValueError("unknown method {!r}".format(method))
[docs] def minimal_strata_CMSZ(gmax, rational=False): r""" Return the volumes of `\cH(2g-2)` for the genus `g` going from ``1`` up to ``gmax-1``. The algorithm is the one from Sauvaget [Sau18]_ involving an implicit equation. As explained in [CheMoeSauZag20]_, one could go through Lagrange inversion. Note that they miss factor 2 in their theorem 4.1. EXAMPLES:: sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import minimal_strata_CMSZ sage: minimal_strata_CMSZ(6, True) [2, 3/4, 305/576, 87983/207360, 1019547/2867200] sage: minimal_strata_CMSZ(6, False) [1/3*pi^2, 1/120*pi^4, 61/108864*pi^6, 12569/279936000*pi^8, 12587/3311616000*pi^10] sage: from surface_dynamics import Stratum sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import masur_veech_volume sage: for rat in [True, False]: ....: V0, V2, V4, V6 = minimal_strata_CMSZ(5, rational=rat) ....: MV0 = masur_veech_volume(Stratum([0], k=1), rat, 'table') ....: assert V0 == MV0, (V0, MV0, rat) ....: MV2 = masur_veech_volume(Stratum([2], k=1), rat, 'table') ....: assert V2 == MV2, (V2, MV2, rat) ....: MV4 = masur_veech_volume(Stratum([4], k=1), rat, 'table') ....: assert V4 == MV4, (V4, MV4, rat) ....: MV6 = masur_veech_volume(Stratum([6], k=1), rat, 'table') ....: assert V6 == MV6, (V6, MV6, rat) """ n = 2 * gmax - 1 R = QQ['u'] u = R.gen() # B(u) = formula (15) in [CMSZ20] B = (2 * (u/2)._sinh_series(n+1).shift(-1)).inverse_series_trunc(n+1) Q = u * (sum(factorial(j-1) * B[j] * u**(j) for j in range(1,n)))._exp_series(n+1) # A = formula (14) in [CSMZ20] tA = Q.revert_series(n+1).shift(-1).inverse_series_trunc(n) # normalized values of volumes in [CMSZ20] are # v(m_1, ..., m_n) = (2m_1+1) (2m_2+1) ... (2m_n+1) Vol(m_1, m_2, ..., m_n) if rational: return [-4 * (2*g) / ZZ(2*g-1) / bernoulli(2*g) * tA[2*g] for g in range(1,gmax)] else: return [2 * (2*pi)**(2*g) * (-1)**g / ZZ(2*g-1) / factorial(2*g - 1) * tA[2*g] for g in range(1,gmax)]
[docs] def minimal_strata_hyp(g, rational=False): r""" Return the volume of the hyperelliptic component H^{hyp}(2g-2). The explicit formula appears in section 6.5 of [CheMoeSauZag20]_. EXAMPLES:: sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import minimal_strata_hyp sage: minimal_strata_hyp(2) 1/120*pi^4 sage: minimal_strata_hyp(4) 1/580608*pi^8 sage: minimal_strata_hyp(10) 1/137733277917118464000*pi^20 sage: minimal_strata_hyp(10, rational=True) 668525/10499279483305984 """ if rational: return (-1)**(g+1) * 4 * factorial(2*g) / ((2*g-1)*2*g*(2*g+1) * 2**(4*g-2) * bernoulli(2*g) * factorial(g-1)**2) else: return 2*pi**(2*g) / ((2*g-1)*2*g*(2*g+1) * 2**(2*g-2) * factorial(g-1)**2)
[docs] def minimal_strata_spin_diff(gmax, rational=False): r""" Return the differences of volumes between even and odd components in H(2g-2) for the genus `g` going from ``1`` up to ``gmax-1``. If there are no even/odd components, the corresponding total volume is 0. Formulas are from [CheMoeSauZag20]_. EXAMPLES:: sage: from surface_dynamics.flat_surfaces.masur_veech_volumes import minimal_strata_spin_diff sage: minimal_strata_spin_diff(5) [-1/3*pi^2, -1/120*pi^4, -143/544320*pi^6, -15697/1959552000*pi^8] sage: minimal_strata_spin_diff(5, rational=True) [-2, -3/4, -143/576, -15697/207360] """ n = 2 * gmax R = QQ['u'] u = R.gen() # B(u) = formula (15) in [CMSZ20] B = (2 * (u/2)._sinh_series(n).shift(-1)).inverse_series_trunc(n) # Pz and a in section 6.3 of [CMSZ20] Pz = (sum(-bernoulli(2*j) * u**(2*j) / (2*j) / 2**j for j in range(1, n // 2)))._exp_series(n) a = ((u*Pz.inverse_series_trunc(n)).revert_series(n).shift(-1)).inverse_series_trunc(n) # theorem 6.11 in [CMSZ20], normalized volume v(2g-2)=(2g-1)*Vol(2g-2), note the missing factor 2 if rational: return [2* (-2) * a[2*g] * 2 * g / (2*g - 1) / bernoulli(2*g) for g in range(1, gmax)] else: return [2* (-1)**(g) * (2*pi)**(2*g) * a[2 * g] /(2*g - 1) / factorial(2*g - 1) for g in range(1, gmax)]