One-modulus Calabi-Yau models#
What’s in this notebook? This notebook introduces the
hypergeometric_modelsmodule, which provides closed-form period data for one-modulus Calabi-Yau (CY) threefolds near three moduli-space points: LCS, K-point and C-point.The public entry point is the static factory
HypergeometricModels, which returns a fully-configuredFluxVacuaFinderfor any of the 18 registered models. We cover the physical background on singularity types, demonstrate how to load models, inspect their boundary data, and run a SUSY flux-vacuum search at LCS.The period data is taken from: B. Bastian, D. van de Heisteeg, L. Schlechter, Beyond Large Complex Structure: Quantized Periods and Boundary Data for One-Modulus Singularities, arXiv:2306.01059.
(Created: Andreas Schachner, Nov. 2025)
Outline#
Background#
One-modulus CY threefolds#
A Calabi-Yau threefold with \(h^{2,1} = 1\) has a one-dimensional complex structure moduli space \(\mathcal{M}\). The most studied examples come from the 14 hypergeometric one-parameter families (AESZ database), defined by a Picard-Fuchs operator
\(z = 0\): large complex structure (LCS) point, always present;
\(z = \mu^{-1}\): conifold singularity;
\(z = \infty\): a boundary whose type depends on the exponents \(\alpha_i\).
The module also includes four non-hypergeometric models studied in arXiv:2306.01059 as additional examples.
Singularity types and monodromy#
The boundary at \(z \to \infty\) is classified by the monodromy matrix \(T_\infty\) acting on \(H^3(X, \mathbb{Z})\) (a \(4\times4\) symplectic matrix). Four types arise, distinguished by the pattern of Picard-Fuchs exponents \(\alpha_i\) at \(z = \infty\):
Type |
Condition on \(\alpha_i\) |
Monodromy \(T_\infty\) |
Physical character |
|---|---|---|---|
M (MUM) |
\(\alpha_1 = \alpha_2 = \alpha_3 = \alpha_4\) |
Maximally unipotent |
Second LCS point |
K |
\(\alpha_1 = \alpha_2\), \(\alpha_3 = \alpha_4\), \(\alpha_2 \neq \alpha_3\) |
Finite-order semisimple \(\times\) unipotent |
K-point |
C |
\(\alpha_2 = \alpha_3\) only |
Unipotent, rank 1 |
Conifold |
F |
All \(\alpha_i\) distinct |
Generic finite order |
F-point |
The type determines what kind of period expansion is natural near \(z = \infty\), and hence what form the prepotential takes.
The LCS prepotential#
Near the large complex structure point at \(z \to 0\), with projective coordinates \(X = (X^0, X^1)\) and \(t = X^1/X^0\), the prepotential takes the form
\(\kappa\) is the triple intersection number of the mirror CY;
\(c_2\) is the integrated second Chern class;
\(\chi\) is the Euler characteristic;
\(\sigma = (\kappa/2) \bmod 1\);
\(K_0 = \zeta(3)\chi / (2(2\pi i)^3)\) encodes the constant map contribution.
The period vector is \(\Pi = (\partial_{X^0}\mathcal{F},\, \partial_{X^1}\mathcal{F},\, X^0,\, X^1)\).
The K-point prepotential#
A K-point arises when the monodromy \(T_\infty\) has finite semisimple part and a unipotent block. It is characterised by a rigid period \(\tau \in \mathbb{H}\) (the upper half-plane), which plays the role of the complexified gauge coupling of a heavy BPS vector multiplet becoming massless.
Setting \(s = (X^1 - \tau)/X^0\) as a local coordinate, the prepotential expands as (eqs. 1060–1063 of arXiv:2306.01059)
Here:
\(\gamma, \delta\) are extension data encoding the mixing between the light and heavy periods;
\(c\) is the lower-right entry of the log-monodromy matrix \(N_K\);
\(B_k = \hat{B}_k \times B_{\rm norm}\) are instanton coefficients, with \(B_{\rm norm}\) a transcendental prefactor involving the special \(L\)-value \(L(f, 1)\) of an associated weight-3 modular form \(f\).
The C-point (conifold) prepotential#
A C-point is a conifold singularity, where a single BPS hypermultiplet (a 3-cycle) shrinks to zero size. In type IIB string theory this corresponds to an \(\mathcal{N}=2\) conifold transition. The rigid period \(\tau\) is again the gauge coupling, but the monodromy is purely unipotent of rank 1.
With the same local coordinate \(s = (X^1 - \tau)/X^0\), the prepotential is (eqs. 846–849 of arXiv:2306.01059)
The logarithmic term in \(F_2\) reflects the characteristic \(s^2 \log s\) branching of the periods at the conifold. The integer \(k\) is the conifold order, \(A_k = \hat{A}_k \times A_{\rm norm}\) are instanton coefficients, and \(A_{\rm norm}\) involves \(L(f, 1)\) for an associated weight-4 modular form.
L-function values and instanton normalisations#
A key result of arXiv:2306.01059 is that the instanton normalisations \(B_{\rm norm}\) (K-point) and \(A_{\rm norm}\) (C-point) are not free parameters — they are fixed by quantisation conditions on the period lattice. They are expressed in terms of special values \(L(f, 1)\) of \(L\)-functions of modular forms, which in turn admit closed-form expressions in terms of \(\Gamma\)-function values. For example:
Model |
Modular form |
\(L(f, 1)\) |
|---|---|---|
\(X_{3,3}\) K-point |
27.3.b.a |
\(\Gamma(1/3)^6 / (8\sqrt{3}\,\pi^3)\) |
\(X_{4,4}\) K-point |
16.3.c.a |
\(\Gamma(1/4)^4 \Gamma(1/2)^2 / (32\pi^3)\) |
\(X_{4,2}\) C-point |
32.4.a.b |
\(\Gamma(1/4)^6 \Gamma(1/2)^3 / (16\sqrt{2}\,\pi^5)\) |
\(X_{3,2,2}\) C-point |
9.4.a.a |
\(\Gamma(1/3)^9 / (32\sqrt{3}\,\pi^5)\) |
All of these are precomputed in hypergeometric_models.py.
Setup#
# General imports
import warnings
import numpy as np
from scipy.special import gamma as gamma_func
from scipy.special import zeta as scipy_zeta
from scipy.optimize import root
# JAX imports
import jax
import jax.numpy as jnp
jax.config.update("jax_enable_x64", True)
# JAXVacua
import jaxvacua as jvc
from jaxvacua import HypergeometricModels, list_hypergeometric_models
from jaxvacua.hypergeometric_models import (
HYPERGEOMETRIC_MODELS, KPOINT_DATA, CPOINT_DATA,
make_prepot_LCS, make_prepot_Kpoint, make_prepot_Cpoint,
)
warnings.filterwarnings('ignore')
Discovering available models#
list_hypergeometric_models() prints a summary of all 18 models (14 hypergeometric + 4 non-hypergeometric) together with the singularity types for which prepotential data is available. For programmatic use, HypergeometricModels.list() returns the labels as a sorted list and HypergeometricModels.available_limits(label) returns the moduli-space points where each model has closed-form data.
list_hypergeometric_models()
The column ∞-type records the singularity type at \(z \to \infty\). For the 6 models with K- or C-type singularities at infinity, dedicated boundary data is stored in KPOINT_DATA and CPOINT_DATA respectively. Every hypergeometric model with fixed topological data also supports an LCS prepotential.
Loading a model via HypergeometricModels.build#
The factory HypergeometricModels.build(label, limit=...) returns a fully-configured FluxVacuaFinder for the requested model at the requested moduli-space point:
Limit |
What gets built |
|---|---|
|
Standard |
|
|
|
Same pattern as K-point with the C-point prepotential. |
The closed-form callable is also accessible programmatically via HypergeometricModels.resolve_prepotential(model_ID, limit). Raw boundary parameters are exposed via HypergeometricModels.lcs_data(label) and HypergeometricModels.boundary_data(label, limit).
LCS point#
Example: quintic \(X_5\)#
The quintic has \(\kappa = 5\), \(c_2 = 50\), \(\chi = -200\), and a generic F-type singularity at \(z \to \infty\). Near \(z = 0\) only the LCS prepotential is available.
m_lcs = HypergeometricModels.build("X5", limit="LCS")
print(m_lcs)
print()
print("LCS topological data:", HypergeometricModels.lcs_data("X5"))
Choose a point in the LCS region, \(X = (1,\, it)\) with \(t \gg 1\). The closed-form LCS prepotential is most directly accessed through the make_prepot_LCS(kappa, c2, chi) factory; the period vector is then \(\Pi(X) = (\partial_{X^I} F,\, X^I)\) via JAX autodiff.
# Closed-form LCS prepotential for the quintic
data_X5 = HypergeometricModels.lcs_data("X5")
F_X5 = make_prepot_LCS(data_X5["kappa"], data_X5["c2"], data_X5["chi"])
X_lcs = jnp.array([1.0 + 0j, 2.0j]) # t = X1/X0 = 2i, deep LCS
F_val = F_X5(X_lcs)
gF = jax.grad(lambda x: F_X5(x, conj=False), holomorphic=True)(X_lcs)
Pi_val = jnp.concatenate([gF, X_lcs])
print(f"F(X) = {F_val:.6f}")
print(f"Pi(X) = {Pi_val}")
The period vector ordering is \((F_0, F_1, X^0, X^1)\) where \(F_I = \partial_{X^I}\mathcal{F}\), consistent with the JAXVacua prepotential_input convention.
LCS data for all hypergeometric models#
print(f"{'Label':8s} {'kappa':5s} {'c2':5s} {'chi':7s} {'sigma':6s}")
print("-" * 40)
for label, d in HYPERGEOMETRIC_MODELS.items():
kappa = d['kappa']
sigma = round((kappa / 2) % 1, 3)
print(f"{label:8s} {kappa:5d} {d['c2']:5d} {d['chi']:7d} {sigma:6.3f}")
K-point#
K-point data is available for five models: X33, X44, X66, 4.47, 3.7. The X66 model is a special case where the leading instanton vanishes (\(B_1 = 0\)) and the standard formula does not apply directly; HypergeometricModels.build("X66", limit="Kpoint") raises NotImplementedError with a clear pointer to the alternate expansion in arXiv:2306.01059 §5.
Inspecting K-point boundary data#
print("K-point models and their rigid periods:\n")
for label, bd in KPOINT_DATA.items():
tau = bd['tau']
mf = bd.get('modular_form', '?')
Lf1 = bd['Lf1']
print(f" {label:6s} tau = {tau.real:.4f} + {tau.imag:.4f}i "
f"L(f,1) = {Lf1:.8f} [{mf}]")
The rigid period \(\tau\) lives in the fundamental domain of the modular group. For \(X_{3,3}\) and \(X_{6,6}\), \(\tau = e^{2\pi i/3}\) (the CM point of order 3); for \(X_{4,4}\), \(\tau = i\) (the CM point of order 4).
Example: \(X_{3,3}\) at the K-point#
\(X_{3,3}\) is the complete intersection of two cubics in \(\mathbb{P}^5\). It has Picard-Fuchs exponents \(\alpha = (1/3, 1/3, 2/3, 2/3)\), so the singularity at \(z \to \infty\) is of K-type. The rigid period is \(\tau = -1/2 + i\sqrt{3}/2 = e^{2\pi i/3}\), and the instanton series runs in powers of \(q^3 = e^{2\pi i \cdot 3\tau}\).
m_K = HypergeometricModels.build("X33", limit="Kpoint")
print(m_K)
print()
bd = HypergeometricModels.boundary_data("X33", "Kpoint")
print(f" tau = {bd['tau'].real:.6f} + {bd['tau'].imag:.6f}i")
print(f" gamma = {bd['gamma']}")
print(f" delta = {bd['delta']}")
print(f" c = {bd['c']}")
print(f" L(f,1) = {bd['Lf1']:.10f}")
print(f" Bnorm = {bd['Bnorm']:.10f}")
print(f" Bhat = {bd['Bhat']}")
Note that \(B_{\rm norm} = -1/(3 L(f,1)^2)\) where \(L(f,1) = \Gamma(1/3)^6 / (8\sqrt{3}\,\pi^3)\).
# Verify the closed-form expression
LF1_exact = gamma_func(1/3)**6 / (8 * np.sqrt(3) * np.pi**3)
Bnorm_exact = -1.0 / (3.0 * LF1_exact**2)
print(f"L(f,1) from formula : {LF1_exact:.10f}")
print(f"L(f,1) from module : {bd['Lf1']:.10f}")
print(f"Bnorm from formula : {Bnorm_exact:.10f}")
print(f"Bnorm from module : {bd['Bnorm']:.10f}")
Evaluate the K-point prepotential. We choose a point \(X = (X^0, X^1)\) near the K-point, i.e. \(s = (X^1 - \tau)/X^0\) small:
tau = bd['tau']
# Small displacement from tau: s = 0.1i
X_K = jnp.array([1.0 + 0j, tau + 0.1j])
F_K = m_K.periods.prepotential_input(X_K)
gF_K = jax.grad(lambda x: m_K.periods.prepotential_input(x, conj=False),
holomorphic=True)(X_K)
Pi_K = jnp.concatenate([gF_K, X_K])
print(f"F(X) = {F_K:.8f}")
print(f"Pi(X) = {Pi_K}")
Example: \(X_{4,4}\) at the K-point#
\(X_{4,4}\) has \(\alpha = (1/4, 1/4, 3/4, 3/4)\) and \(\tau = i\) (order-4 CM point). The instanton series runs in \(q^2 = e^{4\pi i\tau}\), so \(B_2 = 0\), \(B_3 = 12 B_{\rm norm} \neq 0\).
m_X44 = HypergeometricModels.build("X44", limit="Kpoint")
bd44 = HypergeometricModels.boundary_data("X44", "Kpoint")
print(f" tau = {bd44['tau']}")
print(f" gamma = {bd44['gamma']}, delta = {bd44['delta']}")
print(f" Bnorm = {bd44['Bnorm']:.8f}")
print(f" B1 = {bd44['B1']:.8f}")
print(f" B3 = {bd44['B3']:.8f} (leading non-zero after B1)")
print(f" Bhat = {bd44['Bhat']}")
The X66 special case#
\(X_{6,6}\) has \(\alpha = (1/6, 1/6, 5/6, 5/6)\) and its leading instanton vanishes at \(k = 1\) (\(B_1 = 0\)). The instanton series starts at \(k = 2\). This means the standard prepotential formula (which requires \(B_1 \neq 0\) in the logarithm argument) cannot be applied directly.
# HypergeometricModels.build raises NotImplementedError for X66 at K-point
try:
m_X66 = HypergeometricModels.build("X66", limit="Kpoint")
except NotImplementedError as e:
print(f"NotImplementedError: {e}")
# X66 is still loadable at LCS:
m_X66_lcs = HypergeometricModels.build("X66", limit="LCS")
print(f"\nX66 at LCS — built OK: h12={m_X66_lcs.h12}")
# The raw K-point data is still accessible programmatically:
bd66 = HypergeometricModels.boundary_data("X66", "Kpoint")
print(f"\nB1 = {bd66['B1']}, B2 = {bd66['B2']:.8f} (leading instanton at k=2)")
print(f"Bhat = {bd66['Bhat']}")
C-point (conifold)#
C-point data is available for four models: X42, X322, X62, 2.62. These all have a conifold-type singularity at \(z \to \infty\) (C-type Picard-Fuchs exponents).
Inspecting C-point boundary data#
print("C-point models:\n")
for label, bd in CPOINT_DATA.items():
tau = bd['tau']
mf = bd.get('modular_form', '?')
Lf1 = bd['Lf1']
k = bd['k']
print(f" {label:6s} tau = {tau.real:.4f} + {tau.imag:.4f}i "
f"k = {k:3d} L(f,1) = {Lf1:.8f} [{mf}]")
The conifold order \(k\) counts how many times the standard conifold monodromy is iterated. Note that operator 2.62 has \(k = 42\) — a large value reflecting a non-hypergeometric geometry.
Example: \(X_{4,2}\) at the C-point#
\(X_{4,2}\) is a complete intersection with \(\alpha = (1/4, 1/2, 1/2, 3/4)\) and \(\kappa = 8\). The C-point at infinity has \(\tau = 1/2 + i/2\), and the weight-4 modular form is 32.4.a.b.
m_C = HypergeometricModels.build("X42", limit="Cpoint")
print(m_C)
print()
bd = HypergeometricModels.boundary_data("X42", "Cpoint")
print(f" tau = {bd['tau'].real:.6f} + {bd['tau'].imag:.6f}i")
print(f" gamma = {bd['gamma']}, delta = {bd['delta']}")
print(f" k = {bd['k']}")
print(f" L(f,1) = {bd['Lf1']:.10f}")
print(f" Anorm = {bd['Anorm']:.10f}")
print(f" Ahat = {bd['Ahat']}")
tau = bd['tau']
# Point near the C-point: small s = 0.05i (avoid s=0 where log(s) diverges)
X_C = jnp.array([1.0 + 0j, tau + 0.05j])
F_C = m_C.periods.prepotential_input(X_C)
gF_C = jax.grad(lambda x: m_C.periods.prepotential_input(x, conj=False),
holomorphic=True)(X_C)
Pi_C = jnp.concatenate([gF_C, X_C])
print(f"F(X) = {F_C:.8f}")
print(f"Pi(X) = {Pi_C}")
Example: \(X_{3,2,2}\) at the C-point#
\(X_{3,2,2}\) has \(\alpha = (1/3, 1/2, 1/2, 2/3)\), so it is also a C-type model. The conifold order is \(k = 6\).
m_X322 = HypergeometricModels.build("X322", limit="Cpoint")
bd322 = HypergeometricModels.boundary_data("X322", "Cpoint")
print(f" tau = {bd322['tau'].real:.6f} + {bd322['tau'].imag:.6f}i")
print(f" k = {bd322['k']}")
print(f" Anorm = {bd322['Anorm']:.10f}")
print(f" Ahat = {bd322['Ahat']}")
Comparing LCS and boundary prepotentials for the same model#
For models with both LCS and K/C-point data, we can load both and compare. They are valid in different regions of moduli space and should not be expected to agree numerically.
m_X33_lcs = HypergeometricModels.build("X33", limit="LCS")
m_X33_K = HypergeometricModels.build("X33", limit="Kpoint")
print("X33 LCS model: ", m_X33_lcs)
print("X33 Kpoint model:", m_X33_K)
# LCS prepotential is valid for Im(t) >> 1.
data_X33 = HypergeometricModels.lcs_data("X33")
F_X33_lcs = make_prepot_LCS(data_X33["kappa"], data_X33["c2"], data_X33["chi"])
X_deep_lcs = jnp.array([1.0 + 0j, 5.0j])
print(f"\nF_LCS(t=5i) = {F_X33_lcs(X_deep_lcs):.8f}")
# K-point prepotential is valid for small |s| = |X1/X0 - tau|.
tau_K = HypergeometricModels.boundary_data("X33", "Kpoint")['tau']
X_near_K = jnp.array([1.0 + 0j, tau_K + 0.1j])
F_X33_K = m_X33_K.periods.prepotential_input(X_near_K)
print(f"F_Kpt(s=0.1i) = {F_X33_K:.8f}")
End-to-end: Newton-solve a SUSY flux vacuum#
HypergeometricModels.build returns a FluxVacuaFinder ready to plug into the standard JAXVacua pipeline. Here we sketch the simplest end-to-end use: build the model at LCS, evaluate the F-term residual \(D_I W\) for a small flux configuration, and run scipy’s hybr Newton solver to refine to a SUSY vacuum. The same call signature works at K-point and C-point — just change limit=.
m = HypergeometricModels.build("X33", limit="LCS")
# Initial moduli + axio-dilaton in the LCS region
z0 = jnp.array([3.0 + 1.0j])
tau0 = 0.5 + 2.0j
# Simple unit flux (NS = 1 in slot 0, RR = 1 in the dilaton-shifted slot)
n_fl = 2 * m.n_fluxes
flux = jnp.zeros(n_fl, dtype=jnp.float64).at[jnp.array([0, m.n_fluxes])].set(1.0)
x0 = m._convert_complex_to_real(z0, jnp.conj(z0), tau0, jnp.conj(tau0))
DW = m.DW_x(x0, flux)
print(f"|DW|_max at the initial guess: {float(jnp.max(jnp.abs(DW))):.3e}")
# Newton refinement via scipy.root + analytic Jacobian
res = root(m.DW_x, x0, args=(flux,), jac=m.dDW_x, tol=1e-10, method="hybr")
z_sol, _, tau_sol, _ = m._convert_real_to_complex(res.x)
print(f"Newton: success={res.success} |fun|_max={float(jnp.max(jnp.abs(res.fun))):.3e}")
print(f"Solution: z={z_sol} tau={tau_sol}")
For K-point and C-point models, m.periods.prepotential_input is a JIT-compiled closure delivered by the registry — m.DW_x, m.dDW_x etc. work identically because the F-terms are computed via JAX autodiff through the closed-form prepotential. The example below shows the Cpoint-flavoured construction:
m_c = HypergeometricModels.build("X42", limit="Cpoint")
print(m_c)
print(f" m_c.periods.limit = {m_c.periods.limit!r}")
print(f" m_c.periods.model_type = {m_c.periods.model_type!r}")
print(f" prepotential_input type = {type(m_c.periods.prepotential_input).__name__}")
Using make_prepot_* directly#
For custom parameters or modifications, the factory functions make_prepot_LCS, make_prepot_Kpoint, and make_prepot_Cpoint can be called directly.
# Build a custom LCS prepotential for the quintic
F_quintic = make_prepot_LCS(kappa=5, c2=50, chi=-200)
X = jnp.array([1.0+0j, 3.0j])
print("F_quintic =", F_quintic(X))
# Build a K-point prepotential from raw parameters
from scipy.special import gamma as gamma_func
import numpy as np
LF1 = gamma_func(1/3)**6 / (8 * np.sqrt(3) * np.pi**3)
Bnorm = -1.0 / (3.0 * LF1**2)
F_kpt = make_prepot_Kpoint(
tau=complex(-0.5, np.sqrt(3)/2),
gamma=1/6, delta=1/3, c=2,
B1=Bnorm, B2=0.0, B3=0.0,
)
tau_val = complex(-0.5, np.sqrt(3)/2)
X_kpt = jnp.array([1.0+0j, tau_val + 0.2j])
print("F_kpt =", F_kpt(X_kpt))
Take-aways#
Singularity |
Models with data |
Key parameters |
Valid region |
|---|---|---|---|
LCS |
All 14 hypergeometric |
\(\kappa, c_2, \chi\) |
\(\text{Im}(t) \gg 1\) |
K-point |
X33, X44, X66*, 4.47, 3.7 |
\(\tau, \gamma, \delta, c, B_k\) |
$ |
C-point |
X42, X322, X62, 2.62 |
\(\tau, \gamma, \delta, k, A_k\) |
$ |
* X66 K-point has \(B_1 = 0\); get_model raises ValueError — use KPOINT_DATA['X66'] for raw data.
All transcendental prefactors (\(B_{\rm norm}\), \(A_{\rm norm}\)) are computed from closed-form \(\Gamma\)-function expressions for \(L(f,1)\) values, as derived in arXiv:2306.01059.
Further reading#
NB09 — Moduli-space limits and custom prepotentials (custom-period API used end-to-end here)
NB10 — coni-LCS limit and PFV pipeline (PFV mechanism using these prepotentials)
arXiv:2306.06160 — JAXVacua framework paper (Section 7 covers hypergeometric models)