alkahest
Python API · Submodule

alkahest.pari

Optional PARI/GP interop: exact objects from PARI, checked by Alkahest.

Alkahest's number-field layer has arithmetic only: no ring of integers, class group, units, prime-ideal factorisation or Galois group; it has no elliptic curve invariants (conductor, a_p, rank, L-values) and no modular forms. PARI/GP has all of these. This module asks PARI for them and returns the answers as exact Alkahest objects (Python int / Fraction, experimental.NumberFieldElement, ArbBall), then re-checks every cheap consequence of the answer with Alkahest's own exact arithmetic and records what was and was not checked in a verification mapping. A PARI answer that contradicts a check raises PariError (E-PARI-003) instead of being returned.

Licence boundary

PARI/GP is GPL-licensed. Alkahest is not, and never links, bundles, imports at import alkahest time or redistributes PARI. This module is pure Python, loads a PARI binding lazily on first use, and works only if you have installed one yourself:

  • cypari2 (needs a system libpari; pip install cypari2 builds against it), or
  • cypari (the SnapPy project's binding; its wheels carry their own libpari — pip install cypari).

Nothing in Alkahest's wheels, Rust crates or default dependencies depends on either. Without a binding, available() is False and every function raises PariUnavailable (E-PARI-001). A gp subprocess is not supported. If you redistribute an application that installs a PARI binding next to Alkahest, the GPL applies to that combination; Alkahest itself is unaffected.

Trust levels

Each result states how far it is trusted, in plain strings:

certified PARI's answer carries a proof (bnfcertify) or Alkahest verified it outright. grh_conditional PARI's class group / units are correct only under the Generalised Riemann Hypothesis (bnfinit without bnfcertify). proven / numerical / bounds_only For elliptic-curve ranks and L-values; see elliptic_curve_invariants().

Error codes

E-PARI-001 no PARI binding installed; E-PARI-002 unsupported input; E-PARI-003 a PARI answer failed Alkahest's verification; E-PARI-004 PARI itself raised.

class
class pari.EigenformOrbit

A Galois orbit of normalised Hecke eigenforms of the new subspace.

field is None for rational eigenvalues, otherwise the NumberField Q[y]/(polynomial) containing them. coefficients is [a_1, ..., a_nmax] with a_n an int / Fraction (rational orbit) or a NumberFieldElement.

Properties
degree: int
polynomial: list[int]
field: Any
coefficients: list[Any]
class
class pari.EllipticCurveData

What elliptic_curve_invariants() returns.

ainvs (a1, a2, a3, a4, a6) as Fraction s. discriminant, conductor int s (discriminant is of the given model). ap {p: a_p} for primes p <= ap_bound (a_p for bad p is the PARI convention 0/±1). torsion {"order", "structure", "generators"}; generators are exact (x, y) Fraction pairs on the given model. root_number ±1. analytic_rank / analytic_rank_status PARI's numerical analytic rank; status "proven" when Alkahest's own series evaluation shows the leading L-value is non-zero for rank 0 or 1, otherwise "numerical". l_value L(E, 1) (rank 0) or L'(E, 1) (rank 1; L(E,1) = 0 exactly by the functional equation) as an ArbBall computed from a_n with a rigorous truncation bound, or None when the analytic rank is >= 2 (value not enclosed). mw_rank_bounds / mw_rank_status (lower, upper) from ellrank (2-descent); status "proven" when they agree, else "bounds_only" (the true rank may be anywhere in the range; only conditional statements such as BSD give more). mw_points Independent points of infinite order found by ellrank. They need not generate the Mordell–Weil group (no saturation).

Properties
ainvs: tuple[Fraction, ...]
discriminant: int
conductor: int
ap: dict[int, int]
torsion: dict[str, Any]
root_number: int
analytic_rank: int | None
analytic_rank_status: str
l_value: Any
mw_rank_bounds: tuple[int, int] | None
mw_rank_status: str
mw_points: list[tuple[Fraction, Fraction]]
verification: dict[str, Any]
class
class pari.ModularFormsData

Cusp forms S_k(Gamma_0(N)) with trivial character.

dimension dim S_k(Gamma_0(N)) (full space, old forms included). basis dim lists [a_0, ..., a_nmax] of exact Fraction / int q-expansion coefficients (PARI's basis; not an eigenbasis). new_dimension / orbits New-subspace dimension and its Hecke eigenform orbits.

Properties
level: int
weight: int
nmax: int
dimension: int
basis: list[list[Any]]
new_dimension: int
orbits: list[EigenformOrbit]
verification: dict[str, Any]
class
class pari.NumberFieldData

What number_field_invariants() returns.

Attributes
field

The NumberField the data is expressed in.

discriminant

d_K, an int.

signature

(r1, r2).

integral_basis

A Z-basis of the ring of integers as NumberFieldElement s.

class_number, class_group

h and the cyclic invariants [d1, d2, ...] (d1 | d2 | ...) or None when class_group=False.

torsion_units

(w, zeta): the order of the roots of unity in K and a generator.

fundamental_units

r1 + r2 - 1 multiplicatively independent units (NumberFieldElement).

regulator

PARI's decimal string for the regulator (not an exact object).

primes

{p: [PrimeIdeal, ...]} for the primes requested.

galois

{"order", "parity", "name", "transitive_index"} or None.

certification

"certified" (bnfcertify succeeded: h, units, regulator are proven), "grh_conditional" (not run, too expensive, or failed), or "not_computed" when class_group=False.

verification

{check name: True} for every check Alkahest ran, and a "not_checked" list naming what rests on PARI alone.

Properties
field: Any
discriminant: int
signature: tuple[int, int]
integral_basis: list[Any]
class_number: int | None
class_group: list[int] | None
torsion_units: tuple[int, Any] | None
fundamental_units: list[Any] | None
regulator: str | None
primes: dict[int, list[PrimeIdeal]]
galois: dict[str, Any] | None
certification: str
verification: dict[str, Any]
class
class pari.PrimeIdeal

A prime ideal P = (p, alpha) above a rational prime p.

e is the ramification index, f the residue degree, norm = p**f and generators = (p, alpha) a two-element presentation with alpha a NumberFieldElement of the field passed in.

Properties
p: int
e: int
f: int
norm: int
generators: tuple[Any, Any]
exception
exception pari.PariError(message: str, code: str | None=None)

Base error of pari; .code is the stable E-PARI-NNN.

exception
exception pari.PariUnavailable(PariError, ImportError)

No PARI binding (cypari2 / cypari) is installed (E-PARI-001).

function
pari.available() → bool

True when a PARI binding (cypari2 or cypari) can be loaded.

function
pari.backend() → str | None

Name of the binding in use ("cypari2" / "cypari") or None.

function
pari.elliptic_curve_invariants(
    ainvs: Sequence[Any],
    *,
    ap_bound: int=100,
    rank: bool=True,
    l_value: bool=True
) → EllipticCurveData

Conductor, a_p, torsion, rank and L-value of an elliptic curve over Q.

The curve is y^2 + a1 xy + a3 y = x^3 + a2 x^2 + a4 x + a6.

Parameters
ainvs

[a1, a2, a3, a4, a6] or [a4, a6] (integers or Fractions; the curve must be non-singular). Non-integral coefficients are refused with E-PARI-002.

ap_bound

Return a_p for all primes p <= ap_bound.

rank

Run ellrank (2-descent) and ellanalyticrank.

l_value

Evaluate the leading L-value as a ball.

Verification

discriminant is recomputed from the b-invariants; the conductor's primes must divide it; a_p is recounted by brute-force point counting for good primes p <= 1000 and checked against Hasse; each torsion generator is checked to lie on the curve and its order is recomputed in Alkahest's own Pic^0 (FunctionField / DivisorClass) on the isomorphic short model — it must equal the order PARI claims (this certifies the listed points, not that no further torsion exists); the analytic rank must match the parity of the root number; L(E,1) (or L'(E,1)) is recomputed from the a_n with a rigorous truncation bound and compared with PARI's lfun; each Mordell–Weil point lies on the curve and is shown to have infinite order by Pic^0.

Trust

analytic_rank_status / mw_rank_status as described on EllipticCurveData; modularity (the theorem that L(E,s) has the functional equation used above) is assumed.

Examples
Python
>>> from alkahest import pari
>>> e = pari.elliptic_curve_invariants([0, -1, 1, -10, -20])   # 11a1  # doctest: +SKIP
>>> e.conductor, e.torsion["order"], e.analytic_rank           # doctest: +SKIP
(11, 5, 0)
function
pari.modular_forms(N: int, k: int, *, nmax: int=20) → ModularFormsData

q-expansions and Hecke eigenvalues of S_k(Gamma_0(N)) via PARI's mf* suite.

Parameters
N, k

Level N >= 1 and integer weight k >= 1 (trivial character).

nmax

Number of coefficients to return (a_0..a_nmax / a_1..a_nmax).

Returns exact coefficients: int / Fraction for the basis and for rational eigenforms, NumberFieldElement in the orbit's coefficient field otherwise.

Verification

Every eigenform is re-checked in exact arithmetic: a_1 = 1, full multiplicativity and the prime-power Hecke recursion a_{p^{r+1}} = a_p a_{p^r} - [p ∤ N] p^{k-1} a_{p^{r-1}} for all indices <= nmax; each a_n is an algebraic integer (integral minimal polynomial); the orbit degrees sum to dim S_k^new. For (N, k) = (1, 12)` the form is compared with `q prod (1 - q^n)^24 (Ramanujan's tau). A failure raises E-PARI-003. The list of orbits being complete is PARI's claim (not re-derived).

Examples
Python
>>> from alkahest import pari
>>> pari.modular_forms(1, 12).basis[0][:4]      # tau(n)  # doctest: +SKIP
[0, 1, -24, 252]
function
pari.number_field_invariants(
    field_or_poly: Any,
    *,
    primes: Sequence[int]=(),
    class_group: bool=True,
    galois: bool=True,
    certify: bool | str='auto'
) → NumberFieldData

Ring of integers, class group, units, prime splitting and Galois group of a number field.

Parameters
field_or_poly

An experimental.NumberField, or an ascending integer coefficient list of a monic irreducible polynomial.

primes

Rational primes whose prime-ideal factorisation to return.

class_group

Run bnfinit (class group, units, regulator). Disable for large discriminants.

galois

Compute the Galois group with polgalois (degree <= 7 in a stock PARI; refused with E-PARI-002 above that unless the galdata package is installed, in which case PARI answers up to degree 11).

certify

True always runs bnfcertify (can be very slow), False never, "auto" (default) runs it when degree <= 6 and |d_K| < 10**15. The outcome decides certification.

Verification

All of the following are recomputed in Alkahest's exact arithmetic and a failure raises E-PARI-003: the integral basis spans an order of discriminant d_K (Gram determinant of Tr(w_i w_j)), is closed under multiplication, consists of algebraic integers, disc(f) = index^2 d_K, d_K has the right sign and residue mod 4; every unit has norm ±1 and the torsion generator has the stated order; each prime ideal's two-element presentation spans a lattice of index exactly p**f, sum e f = n, and p | d_K exactly when some e > 1; the Galois parity matches whether disc(f) is a square; for imaginary quadratic fields h is recounted from reduced binary quadratic forms.

Maximality of the order, fundamentality of the units and the value of the class group are not re-derived (they are listed under verification["not_checked"]); they hold unconditionally only when certification == "certified".

Raises
PariUnavailable

No PARI binding (E-PARI-001).

PariError

E-PARI-002 for non-monic or reducible input, E-PARI-003 for a failed check, E-PARI-004 when PARI itself fails.

Examples
Python
>>> from alkahest import pari
>>> d = pari.number_field_invariants([5, 0, 1])      # Q(sqrt(-5))   # doctest: +SKIP
>>> d.discriminant, d.class_number                                   # doctest: +SKIP
(-20, 2)
function
pari.pari_version() → str | None

The PARI library version string, or None when unavailable.

function
pari.ramanujan_tau(n: int) → int

Ramanujan's tau(n) from the product q prod (1 - q^m)^24, exactly.

Pure Python (no PARI); the independent oracle used to check Delta.