Abelian functions

Riemann theta functions generalize the Jacobi theta functions from one complex variable to several. They arose in Riemann’s nineteenth-century theory of Abelian functions and compact Riemann surfaces. Today they are fundamental tools in complex analysis and algebraic geometry, and occur in finite-gap and quasiperiodic solutions of integrable systems.

In genus \(g\), the argument \(\mathbf{z}\in\mathbb C^g\) is a column vector and the period matrix \(\boldsymbol{\tau}\in\mathbb C^{g\times g}\) is symmetric with positive-definite imaginary part.

Theta functions are quasiperiodic. For integer vectors \(\mathbf{m},\mathbf{n}\in\mathbb Z^g\), the zero-characteristic function satisfies

\[\theta(\mathbf{z}+\mathbf{m}+\boldsymbol{\tau}\,\mathbf{n} \mid\boldsymbol{\tau}) = \exp\!\left(-\pi i\,\mathbf{n}^{T}\boldsymbol{\tau}\,\mathbf{n} -2\pi i\,\mathbf{n}^{T}\mathbf{z}\right) \theta(\mathbf{z}\mid\boldsymbol{\tau}).\]

Although theta functions are quasiperiodic, suitable ratios and combinations of them give multiply-periodic Abelian functions. In genus two, such Abelian functions are meromorphic functions of two complex variables with a period lattice of rank four, generated in normalized coordinates by the columns of \(\mathbf{I}_2\) and \(\boldsymbol{\tau}\).

Riemann theta functions

mpmath.rtheta(z, tau, characteristic=None, derivative=0)

Evaluates the Riemann theta function. In genus \(g\), the argument \(\mathbf{z}\in\mathbb C^g\) is a column vector and the period matrix \(\boldsymbol{\tau}\in\mathbb C^{g\times g}\) is symmetric with positive-definite imaginary part. The convention is

\[\theta(\mathbf{z}\mid\boldsymbol{\tau}) = \sum_{\mathbf{n}\in\mathbb Z^g} \exp\!\left(\pi i\,\mathbf{n}^{T}\boldsymbol{\tau}\,\mathbf{n} + 2\pi i\,\mathbf{n}^{T}\mathbf{z}\right).\]

The argument \(\mathbf{z}\) must be a length-\(g\) sequence. The period matrix \(\boldsymbol{\tau}\) must be a square matrix with \(g\) rows and columns. Entries opposite the diagonal that differ only by the current almosteq tolerance are averaged; materially asymmetric matrices raise ValueError.

Characteristics

Passing characteristic=(a, b) evaluates

\[\begin{split}\theta\!\begin{bmatrix}\mathbf{a}\\\mathbf{b}\end{bmatrix} (\mathbf{z}\mid\boldsymbol{\tau}) = \sum_{\mathbf{n}\in\mathbb Z^g} \exp\!\left( \pi i\,(\mathbf{n}+\mathbf{a})^{T}\boldsymbol{\tau}\, (\mathbf{n}+\mathbf{a}) + 2\pi i\,(\mathbf{n}+\mathbf{a})^{T} (\mathbf{z}+\mathbf{b})\right).\end{split}\]

Here \(\mathbf{a},\mathbf{b}\in\mathbb R^g\) are literal real vectors, rather than packed characteristic bits. Arbitrary real characteristics are accepted; half-integer characteristics are the most common.

Derivatives

derivative=(d0, ..., d(g-1)) requests the multi-index derivative

\[\frac{\partial^{d_0+\cdots+d_{g-1}}\theta} {\partial z_0^{d_0}\cdots\partial z_{g-1}^{d_{g-1}}}.\]

The entries must be nonnegative integers. In genus one, an integer derivative order can be supplied directly for consistency with jtheta. Derivatives are with respect to the components of \(\mathbf{z}\), not the angular argument used by jtheta.

Examples

Evaluate a genus-two theta value:

>>> from mpmath import mp, rtheta
>>> mp.pretty = True
>>> tau = [[1.0j, 0.05j], [0.05j, 1.2j]]
>>> rtheta([0.1, 0.2], tau)
(1.08592896533944 + 0.0j)

A half-characteristic and a mixed derivative use literal vectors and a derivative multi-index:

>>> characteristic = ([0.5, 0], [0, 0.5])
>>> rtheta([0.1, 0.2], tau, characteristic, derivative=(1, 1))
(-0.289794493099238 + 0.0j)

In genus one, set \(q=\exp(\pi i\tau)\) and write \(z=w/\pi\). The four standard Jacobi theta functions then correspond to

\[\begin{split}\begin{aligned} \theta\!\begin{bmatrix}1/2\\1/2\end{bmatrix} (w/\pi\mid\tau) &= -\vartheta_1(w,q), & \theta\!\begin{bmatrix}1/2\\0\end{bmatrix} (w/\pi\mid\tau) &= \vartheta_2(w,q), \\ \theta\!\begin{bmatrix}0\\0\end{bmatrix} (w/\pi\mid\tau) &= \vartheta_3(w,q), & \theta\!\begin{bmatrix}0\\1/2\end{bmatrix} (w/\pi\mid\tau) &= \vartheta_4(w,q). \end{aligned}\end{split}\]

For example, the zero-characteristic identity can be checked directly:

>>> from mpmath import almosteq, expjpi, jtheta, pi
>>> tau1 = 0.2+0.9j
>>> w = 0.3+0.1j
>>> almosteq(rtheta([w/pi], [[tau1]]),
...          jtheta(3, w, expjpi(tau1)))
True

Plots

The following plots show two real slices and the modulus over two real variables for genus-two period matrices. Similar slices and surfaces are illustrated in DLMF section 21.4.

import matplotlib.pyplot as plt
from mpmath import j, plot, re, rtheta

tau = [[j, -0.5], [-0.5, j]]
curves = [
    lambda x: re(rtheta([x, x/2], tau)),
    lambda x: re(rtheta([x, 2*x], tau)),
]
fig, ax = plt.subplots()
plot(curves, [-2, 2], axes=ax)
ax.legend([r"$z=(x,x/2)$", r"$z=(x,2x)$"])
fig.savefig("rtheta.png")
../_images/rtheta.png
import matplotlib.pyplot as plt
from mpmath import j, rtheta, splot

tau = [[j, 0.5], [0.5, j]]
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
surface = lambda x, y: abs(rtheta([x, y], tau))
splot(surface, [-1, 1], [-1, 1], points=35, keep_aspect=False, axes=ax, plot3d_kwargs={"cmap": "viridis"})
ax.set_zlabel(r"$|\theta(z\mid\tau)|$")
fig.savefig("rtheta_surface.png")
../_images/rtheta_surface.png

Accuracy

The truncation controls absolute error in the scaled series. Near a zero of theta, cancellation can prevent full relative accuracy.

Performance

This implementation is intended primarily for low-genus applications. Computation time depends strongly on \(\boldsymbol{\tau}\) and can increase rapidly with the genus and requested precision. Automatic reduction can make many otherwise slow inputs practical, but general high-genus evaluation may remain expensive.

References

  1. [DLMF] Chapter 21, Multidimensional Theta Functions.

  2. B. Deconinck, M. Heil, A. Bobenko, M. van Hoeij and M. Schmies, Computing Riemann Theta Functions, Mathematics of Computation 73 (2004), 1417-1442.

  3. Wolfram Language, SiegelTheta.

mpmath.rtheta_jet(z, tau, order, characteristic=None)

Evaluates a derivative jet of the Riemann theta function. A jet is the function value together with all partial derivatives through the requested total order at the same point.

For example, in genus two the order-two jet aligns the multi-indices and derivatives as follows (all evaluated at the same \(\mathbf{z}\)):

\[\begin{split}\begin{gathered} \big((0,0),(1,0),(0,1),(2,0),(1,1),(0,2)\big) \\ \left(\theta, \frac{\partial\theta}{\partial z_0}, \frac{\partial\theta}{\partial z_1}, \frac{\partial^2\theta}{\partial z_0^2}, \frac{\partial^2\theta}{\partial z_0\partial z_1}, \frac{\partial^2\theta}{\partial z_1^2}\right) \end{gathered}.\end{split}\]

order must be a nonnegative integer. The result is a dictionary mapping each derivative multi-index to its value. Its keys are inserted in graded reverse lexicographic order: total order first, then the earlier coordinates first within each order.

Values are ordinary derivatives, using the same convention as the derivative argument to rtheta(); they are not divided by multi-index factorials. All components are accumulated in one lattice traversal, which is normally much faster than separate calls to rtheta().

Examples

Evaluate a value, gradient and Hessian components together:

>>> from mpmath import almosteq, rtheta, rtheta_jet
>>> tau = [[1.0j, 0.05j], [0.05j, 1.2j]]
>>> z = [0.1, 0.2]
>>> jet = rtheta_jet(z, tau, 2)
>>> list(jet)
[(0, 0), (1, 0), (0, 1), (2, 0), (1, 1), (0, 2)]
>>> almosteq(jet[(1, 1)], rtheta(z, tau, derivative=(1, 1)))
True

Characteristics use the same literal (a, b) convention as rtheta().