Gene-level aggregation¤
Aggregations return one gene-level p-value and diagnostics. ACAT combines real variants' p-values; BetaPermutation evaluates the selected lead statistic against a reference built from permutation maxima.
SPA is strongly recommended when ACAT aggregates score-test p-values. Use SpaTest with ACAT
in Python, or --spa --acat in the CLI. ACAT's sensitivity to inaccurate tail probabilities makes variant-level
calibration important. See Tests and gene-level calibration
for the distinction from Beta permutation, which calibrates statistics against their permutation distribution.
jaxqtl.hypothesis.AbstractAggregateTest
jaxqtl.hypothesis.AbstractAggregateTest(equinox.Module, typing.Generic)
[source]
¤
Numerical contract for reducing SNP blocks to one gene-level p-value.
statistic selects values from a TestResult; init and update accumulate
blocks; finalize returns a scalar p-value and diagnostics. The generic types
describe the reduction state, finalization reference, and output diagnostics.
Scheduling, hypothesis fitting, and lead selection belong to the mapper.
statistic(self, result: TestResult) -> jax.Array
¤
Select the per-variant values used by this aggregation inside the compiled kernel.
init(self, dtype, *, num_variants: ArrayLike) -> ~ReductionStateT
¤
Initialize fixed-size state in the statistic dtype.
num_variants counts real variants across the entire window, excluding padding.
It determines ACAT weights and is unused by maximum-statistic reduction.
update(self, state: ~ReductionStateT, values: jax.Array, valid: jax.Array) -> ~ReductionStateT
¤
Accumulate one block of values with a same-shape Boolean validity mask.
valid=False excludes padding. The state structure, leaf shapes, and dtypes
must remain unchanged across updates.
finalize(self, state: ~ReductionStateT, reference: ~ReferenceT) -> tuple[jax.Array, ~Aux]
¤
Return one scalar gene-level p-value and diagnostics after all blocks are accumulated.
For permutation calibration, state is the observed statistic to evaluate. Permutation reductions supply the reference without individually being finalized.
Aggregation methods¤
jaxqtl.hypothesis.BetaPermutation(jaxqtl.hypothesis.AbstractAggregateTest)
[source]
¤
Permutation-based gene-level p-values via a Beta approximation.
This method reduces permutation results to statistics \(T_1, \dots, T_B\) (the maximum absolute z statistic across variants), converts them to permutation p-values \(p_b\), then fits a Beta approximation \(p_b \sim \mathrm{Beta}(k, n)\). Observed variant statistics are mapped through the same calibration and fitted Beta CDF. Cis mapping reports the adjusted value corresponding to the selected lead variant.
The mapper generates permutations; this class reduces and calibrates their statistics. Reusing the same PRNG key with the same inputs in the mapper produces the same permutations. Floating-point results can still vary across JAX backends.
Attributes:
max_perm_direct: Number of direct permutations. Defaults to 1000.max_iter_beta: Maximum iterations for fitting the Beta approximation. Defaults to 1000.use_tdist: Estimate a Student's t degrees-of-freedom adjustment when true; otherwise estimate noncentrality for a chi-squared reference distribution.
__init__(self, max_perm_direct: int = 1000, max_iter_beta: int = 1000, use_tdist: bool = False) -> None
¤
Initialize self. See help(type(self)) for accurate signature.
statistic(self, result: TestResult) -> jax.Array
¤
Return the per-variant z statistics.
init(self, dtype, *, num_variants: ArrayLike) -> jax.Array
¤
Initialize a scalar NaN maximum in dtype; num_variants is unused.
NaN is retained if every real statistic is NaN.
update(self, state: jax.Array, values: jax.Array, valid: jax.Array) -> jax.Array
¤
Accumulate absolute z statistics, ignoring NaNs and padded variants.
finalize(self, state: jax.Array, reference: PermutationReference) -> tuple[jax.Array, jaxqtl.hypothesis._aggregate.BetaCalibration]
¤
Calibrate a scalar lead z statistic against complete permutation maxima.
Returns (gene_pvalue, BetaCalibration). Vector input raises ValueError;
use adjust to apply an existing calibration to additional SNPs.
adjust(self, z: jax.Array, calibration: BetaCalibration) -> jax.Array
¤
Apply an existing calibration to scalar or array-valued z statistics.
Returns adjusted p-values with the same shape as z, without refitting.
They use the gene's permutation-maximum reference for multiple testing
adjustment; this is distinct from marginal p-value calibration by SPA.
jaxqtl.hypothesis.ACAT(jaxqtl.hypothesis.AbstractAggregateTest)
[source]
¤
Aggregate p-values using ACAT.
Given per-variant p-values \(p_1, \dots, p_m\) and weights \(w_i = 1/m\), the Cauchy combination statistic is \(T = \sum_i w_i \tan\left(\left(\frac{1}{2} - p_i\right)\pi\right)\), with p-value \(p = 1 - F_{\mathrm{Cauchy}(0,1)}(T)\).
Failure Modes:
A mixture containing both exact zero and exact one p-values cannot be ordered
consistently in the Cauchy transform. The method reports this condition through
equinox.error_if; behavior follows Equinox's transformed-runtime error policy.
__init__(self) -> None
¤
Initialize self. See help(type(self)) for accurate signature.
statistic(self, result: TestResult) -> jax.Array
¤
Return the per-variant p-values.
init(self, dtype, *, num_variants: ArrayLike) -> CauchyState
¤
Initialize a Cauchy accumulator in dtype with weight 1 / num_variants.
num_variants is the positive number of real SNPs in the whole window,
including those processed in later blocks.
update(self, state: CauchyState, values: jax.Array, valid: jax.Array) -> CauchyState
¤
Accumulate masked p-value contributions without transferring values to the host.
finalize(self, state: CauchyState, reference: None = None) -> tuple[jax.Array, None]
¤
Return (gene_pvalue, None) from the complete Cauchy accumulator.
reference is unused. A mixture of exact zero and exact one p-values
raises through Equinox's transformed-runtime error handling.
Reduce blocks and finalize¤
AbstractAggregateTest[ReductionStateT, ReferenceT, Aux] defines a shared numerical lifecycle:
state = method.init(dtype, num_variants=number_of_real_variants)
values = method.statistic(block_result)
state = method.update(state, values, valid_mask) # Repeat for each block.
pvalue, diagnostics = method.finalize(state, reference)
statistic selects p-values for ACAT or z statistics for Beta permutation inside the compiled kernel.
This lets permutation kernels discard unused SPA tail calculations. valid_mask excludes padding.
State shapes do not depend on window width; ACAT weights use the full count of real variants.
| Method | Reduction state | Reference | Finalization |
|---|---|---|---|
| ACAT | CauchyState: weighted sum, endpoint flags, and weight |
None |
Convert the complete Cauchy statistic to a p-value |
| Beta permutation | Scalar array: maximum absolute z statistic | PermutationReference: permutation maxima and residual degrees of freedom |
Fit calibration and evaluate an observed statistic |
Permutation reductions produce one maximum per shuffle and are not individually finalized. Cis orchestration
selects the lead once, passes its index to the formatter, and calls finalize(lead_z, reference).
Selection uses the nominal or SPA p-value, which need not identify the largest absolute z statistic.
Result type¤
PermutationResult is the public type alias for the (pvalue, auxiliary_diagnostics) tuple returned by aggregation
methods. Both blocked scans and jaxqtl.map.cis.map_cis_single return one scalar gene-level
p-value for either aggregation. map_cis_single orchestrates compiled full-window kernels and lead selection
on the host; its wrapper is not JIT-transformable.
Beta permutation's finalize accepts one scalar lead statistic. Applying an existing calibration to
additional SNPs is a separate operation:
method = BetaPermutation()
result, (gene_pvalue, calibration) = map_cis_single(
X, G, y, offset, snp_test=test, gene_test=method, key=key
)
snp_adjusted_pvalues = method.adjust(result.z, calibration)
These SNP values use the gene's permutation-maximum reference for within-gene multiple testing adjustment. This operation does not provide marginal p-value calibration like SPA and does not refit the calibration.
BetaCalibration names the auxiliary fields beta_params, reference_estimate, and reference_converged.
The diagnostics distinguish the fitted Beta parameters from convergence of the reference-distribution estimate.
Execution and extension¤
AssociationScan owns fitting, block transfers, and permutation batching. Cis orchestration owns lead
selection and finalization; aggregators own statistical calculations.
Custom observed-only aggregators implement statistic, init, update, finalize, and name.
Their preferred block_size selects blocked execution; None selects a full window.
A different resampling workflow requires extending the executor. Cis output currently supports ACAT and
BetaPermutation; adding another method also requires extending the formatter.
Calibration and failure behavior¤
Beta permutation records the maximum absolute score or Wald statistic over the entire cis window for each permutation. SPA uses the underlying score statistic in this procedure; its tail-corrected p-values enter ACAT. The phenotype and a vector offset are shuffled together, while covariates and genotypes remain fixed.
ACAT weights each real variant by the inverse of the number of variants in the window. Padding has zero contribution. Nonfinite input p-values propagate through the aggregate; inputs containing both exact zero and exact one trigger an error. Values close to one can dominate the negative side of the Cauchy sum.
A finite lead-variant p-value does not guarantee a finite adjusted p-value or successful Beta calibration. Check the convergence fields and the adjusted p-value as described in Troubleshooting.
jaxqtl.hypothesis.BetaCalibration(builtins.tuple)
[source]
¤
Reusable gene calibration and its fit diagnostics.
reference_estimate is Student's t degrees of freedom when use_tdist=True,
otherwise a chi-squared noncentrality parameter. beta_params carries its own
convergence flag; reference_converged describes the reference-distribution fit.
jaxqtl.hypothesis.CauchyState(builtins.tuple)
[source]
¤
Weighted Cauchy sum, exact-zero/one flags, and the whole-window SNP weight.
Endpoint flags persist across blocks so incompatible exact p-values are detected even when they occur in different blocks.
jaxqtl.hypothesis.PermutationReference(builtins.tuple)
[source]
¤
Finalization inputs: one maximum per permutation and residual degrees of freedom.
maxima has shape (num_permutations,). dof initializes the optional
Student's t reference fit.
Beta approximation for permutation p-values¤
For cis mapping, jaxqtl can fit a Beta approximation to the distribution of permutation p-values:
jaxqtl.infer.infer_beta_params(p_perm: jax.Array, init: jax.Array, step_size=0.1, tol=0.001, max_iter=500) -> BetaParams
¤
Fit a Beta approximation to a collection of permutation p-values.
Given permutation p-values \(p_1, \dots, p_R\), this estimates parameters k and n such that
\(p \sim \mathrm{Beta}(k, n)\). The implementation uses a natural-gradient/Newton-style iteration on the
positive parameter manifold.
Arguments:
p_perm: Permutation p-values with shape(R,).init: Initial parameter vector(k, n).step_size: Update step size.tol: Convergence tolerance on the change in objective value.max_iter: Maximum number of iterations.
Returns:
A jaxqtl.infer.BetaParams with fitted parameters and a convergence indicator.