Numerical backends¤
Numerical backends evaluate binomial expectations and their transposes. They provide the numerical representation used by transitions and evolution methods.
AbstractTransitionBackend
drifft.AbstractTransitionBackend(equinox.Module)
[source]
¤
Interface for prepared geometry with a linear expectation operation.
For destination gene-copy count \(M\) and values \(v_j\), backends evaluate the binomial expectation
Implementations provide gene_copies and an expect operation linear in
destination values, supporting transposition, JVP, VJP, and batching.
Prepare geometry outside JIT; use Transition for compiled application.
expect(self, probabilities: Array, values: Array) -> Array
¤
Compute binomial expectations at the supplied probabilities.
Arguments:
probabilities: Float64 vector of finite source probabilities in[0, 1].values: Finite float64 destination vector of lengthgene_copies + 1.
Returns:
A float64 vector with the same shape as probabilities, linear in values.
Raises:
ValueError for incompatible vector shapes, including during JIT tracing.
Numerical preconditions
Finiteness and probability ranges are unchecked. Outputs are not clipped or normalized; approximation and derivative limitations depend on the concrete backend. Roundoff can vary across devices.
drifft.FFT(drifft.AbstractTransitionBackend)
[source]
¤
Binomial expectations through local FFT convolutions.
For destination values \(v_j\) and gene-copy count \(M\), approximate \(B_M[v](q)=\sum_{j=0}^M\binom{M}{j}q^j(1-q)^{M-j}v_j\). On each local log-odds grid, the retained binomial kernel factors as
Here \(j_0\) is the reference allele count, \(h\) is the log-odds spacing, and \(a_r,b_j\) are diagonal scaling weights. The sum is a convolution, evaluated by zero-padded FFTs; local polynomial interpolation supplies expectations at arbitrary \(q\). Near zero and one, direct binomial sums replace log-odds interpolation.
For offspring probabilities \(q_i\), Transition uses
\((Pv)_i=B_M[v](q_i)\) for expectations and the transpose \(P^Tp\) for forward
mass. The approximation comes from tail truncation and interpolation;
the Gaussian-shaped convolution above is a factorization of the binomial
kernel, not a normal approximation to it.
__init__(self, gene_copies: int, *, tail_tol: float = 1e-12, interp_order: int = 12, min_mode_count: int = 128, block_spacing: float = 1.0, balance: bool = True, dtype: DTypeLike = float64) -> None
¤
Plan local binomial FFTs over all success probabilities, independent of selection.
Arguments:
gene_copies: Integer gene-copy count, at least two (diploid population size isgene_copies/2).tail_tol: Discarded mass budget in[1e-15, .1), default1e-12.interp_order: Even polynomial stencil size from 2 through 16, default 12.min_mode_count: Boundary cutoff in expected copies, at least 8, default 128.block_spacing: Center separation in local standard deviations,(0, 4].balance: Whether to recenter the diagonal multipliers, default true.dtype: Must be float64, withjax_enable_x64already enabled.
Returns:
None. Initializes host-computed quantile windows and cached kernel FFTs.
Construction is not JIT-compatible. Core intervals partition log-odds;
halos support interpolation. For gene_copies <= 2*min_mode_count all rows use the
direct fallback. Tail tolerance does not bound interpolation or roundoff.
Raises:
ValueError for invalid configuration, disabled x64 or nonfinite factors;
TypeError if balance is not boolean. Results are deterministic for a
fixed environment but need not be bitwise identical across platforms.
drifft.NUFFT(drifft.AbstractTransitionBackend)
[source]
¤
Binomial expectations through Chebyshev approximation and NUFFT evaluation.
For destination values \(v_j\) and gene-copy count \(M\), approximate \(B_M[v](q)=\sum_{j=0}^M\binom{M}{j}q^j(1-q)^{M-j}v_j\) by
Here \(T_k\) is the degree-\(k\) Chebyshev polynomial and \(L\) is the prepared
approximation degree. The coefficients \(c_k\) come from binomial sampling
at fixed Chebyshev–Lobatto nodes followed by a DCT-I. A type-2 NUFFT
evaluates the cosine series at arbitrary offspring probabilities.
encoding="lobatto" interpolates the samples. encoding="filtered"
tapers the modes and adds a linear endpoint correction before evaluation.
Endpoint expectations are set directly to \(v_0\) and \(v_M\) to suppress
evaluation roundoff. degree always means the output polynomial degree;
compressed filtered encoding uses degree + 2 sampling nodes.
For offspring probabilities \(q_i\), Transition uses
\((Pv)_i=B_M[v](q_i)\) for expectations and the transpose \(P^Tp\) for forward
mass. Polynomial truncation, truncated binomial sampling, and NUFFT
evaluation each contribute approximation error.
__init__(self, gene_copies: int, *, accuracy: float = 1e-10, encoding: typing.Literal['lobatto', 'filtered'] = 'lobatto', tail_tol: float | None = None, nufft_tol: float | None = None, batch_size: int = 32, derivative_accuracy: bool = False, dtype: str | type[typing.Any] | numpy.dtype | jax._src.typing.SupportsDType = float64) -> None
¤
Prepare a fixed-degree Bernstein/Chebyshev/NUFFT transition.
Arguments:
gene_copies: Destination gene copies, at least two.accuracy: Nominal value-error budget, in[1e-13, 0.1).encoding:"lobatto"(default) interpolates the samples;"filtered"tapers them and corrects endpoints as a polynomial. Both use the exact degree without tapering when compression cannot meet the spectral budget. Neither guarantees positivity.tail_tol: Optional stricter per-node discarded binomial mass tolerance.nufft_tol: Optional stricter FINUFFT tolerance (at least1e-15).batch_size: Number of sampling nodes processed together.derivative_accuracy: Increase the fixed degree by 50 percent (capped at the exact degree). This is a conservative heuristic, not a derivative bound.dtype: Only float64 is supported; enable JAX x64 before construction.
Returns:
None. Initializes selection-independent geometry. The value budget is
split equally between spectral error, sampling error, requested NUFFT
tolerance and roundoff.
Roundoff and actual NUFFT error must still be measured independently.
Raises:
ValueError for invalid configuration, disabled x64 or unsupported dtype.
FFT-specific settings belong to the FFT backend.
drifft.Jacobi(drifft.AbstractTransitionBackend)
[source]
¤
Jacobi geometry for binomial expectations and reduced Krylov evolution.
For gene-copy count \(M\) and destination values \(v_j\), the binomial expectation is
Write \(z=2q-1\). After separating the linear interpolant between \(v_0\) and
\(v_M\), approximate the remaining function in the basis
\((1-z^2)\phi_k(z)\), \(k=0,\ldots,r-1\). Here \(r\) is rank and \(\phi_k\)
is a normalized Jacobi polynomial \(P_k^{(1,1)}\), orthonormal for weight
\(3(1-z^2)/4\) on \([-1,1]\). The envelope makes every interior basis function
vanish at the count endpoints. Gauss–Jacobi quadrature and binomial
stencils map count-space values to these coefficients.
Let \(E\) denote this coefficient map and \(D\) evaluation at the offspring probabilities \(q_i=f(i/M)\). The expectation operator factors as \(P\approx DE\), so forward mass evolves by its transpose. For \(g\geq1\) generations with a fixed frequency map and no immigration,
The reduced operator has \(r\) interior coordinates, plus two endpoint coordinates for full-state evolution. It is assembled using polynomial recurrences and a padded tridiagonal coordinate matrix, without forming the population-sized transition matrix. A separate epoch method computes the power of \(H^T\); representation rank and padding control different approximations from the method's power tolerance.
Transition and Sequential accept arbitrary valid offspring probabilities
through the common expectation interface. Reduced Krylov assembly supports
neutral and diploid selection, standalone mutation, and one mutation
composed with one supported selection rule in either order. Custom maps and
longer compositions are not supported by reduced assembly.
Recurrent mutation couples the endpoint states to the interior; full-state
evolution retains this coupling, while segregating evolution discards
endpoint mass each generation. Construct outside JIT with JAX x64 enabled;
the prepared geometry is independent of the dynamic process parameters.
Reduced epoch domains are checked at runtime, including for zero generations:
selection must stay within the configured bound, and mutation rates must be
finite and in [0, 1]. In DemographicTransition, size-changing steps use the
same backend by default; an explicit FFT or NUFFT boundary_backend can be
supplied instead.
__init__(self, gene_copies: int, *, rank: int | None = None, max_strength: float = 0.1, one_step_tolerance: float = 1e-13) -> None
¤
Prepare process-independent geometry outside JIT.
Arguments:
gene_copies: Python integer at least four (twice diploid Ne).rank: Interior polynomial count, between one andgene_copies - 1. If omitted, use an ideal one-step polynomial tail bound.max_strength: Static bound in[0, 1)on the absolute values of both diploid fitness reductions, used for reduced Krylov padding. In reduced assembly,UnderdominanceandOverdominanceretain their finite, nonnegative strength domains, including in mutation compositions.one_step_tolerance: Planning tolerance in(0, 1)for polynomial truncation and Jacobi padding separately; not a total error guarantee.
Returns:
None. Runtime geometry consists of JAX arrays; SciPy is used only to
prepare Gauss–Jacobi nodes, for which JAX has no equivalent routine.
Raises:
ValueError for invalid counts or tolerances. Enable JAX x64 before
constructing this backend. Dense reduced storage costs O(rank²).
With RationalKrylov, dense LU factorization costs O(rank³) per power action.
expect(self, probabilities: Array, values: Array) -> Array
¤
Evaluate the binomial expectation at arbitrary offspring probabilities.
Arguments:
probabilities: Float64 probability vector, including endpoints.values: Finite real destination values of lengthgene_copies + 1.
Returns:
One expectation per probability. The operation is linear in values
and supports transposition, batching, and polynomial derivatives.
Frequency-map and max_strength restrictions apply only to reduced
epoch assembly, not this arbitrary-probability operation.
Raises:
ValueError for incompatible input shapes. Probability ranges and
finiteness are caller preconditions, as for other transition backends.
drifft.Dense(drifft.AbstractTransitionBackend)
[source]
¤
Form binomial transition rows for an independent numerical reference.
For destination gene-copy count \(M\) and source probabilities \(q_i\), construct
Each row is built by repeated Bernoulli convolution, starting at count zero. This avoids factorials, logarithms, and divisions by endpoint probabilities. The calculation is polynomial throughout, so ordinary JAX differentiation gives finite endpoint derivatives without custom rules. It shares no numerical kernels with FFT, NUFFT, or Jacobi.
expect explicitly forms the matrix before multiplying by destination
values. With \(N\) source rows, this uses \(O(NM)\) matrix storage and \(O(NM^2)\)
arithmetic. Reverse-mode differentiation can retain intermediate rows.
Use this backend for small-population checks of transitions, demographic
evolution, and gradients; it is not intended for large populations. There
is no truncation or interpolation, but floating-point roundoff remains.
__init__(self, gene_copies: int) -> None
¤
Configure the destination count grid outside JIT.
Arguments:
gene_copies: Python integer at least two, equal to twice diploid Ne.
Returns:
None. Only the static count is stored; matrices are formed on application.
Raises:
ValueError for invalid counts or if JAX x64 is disabled.
expect(self, probabilities: Array, values: Array) -> Array
¤
Form binomial rows and multiply by destination values.
Arguments:
probabilities: Vector of finite source probabilities in[0, 1].values: Finite real destination vector of lengthgene_copies + 1. Both inputs are converted to float64.
Returns:
One expectation per probability, linear in values. Signed values,
transposition, batching, and higher derivatives are supported.
Raises:
ValueError for incompatible vector shapes. Finiteness and probability
ranges are caller preconditions; outputs are not clipped or normalized.