blackjax.adaptation.metric_estimators#
Pure metric estimator functions for the low-rank metric adaptation layer.
Each function is a pure transformation: explicit arrays in → metric
representation out. No buffer state, no scheduling logic, no side effects.
All functions are JAX-traceable and safe to use inside jax.lax.scan /
jax.vmap provided that max_rank (where applicable) is a static
Python integer (required to determine output shapes).
Source lineage (estimator extraction history; the private
_compute_low_rank_metric helper shared by the fisher path now resides in
this module, relocated from low_rank_adaptation):
Estimator |
Extracted from |
|---|---|
|
|
|
|
|
|
|
|
branch b197f1e2
|
|
|
Composition note: estimators, data-feeding policy, and schedule are co-adapted package components. The functions here are the estimator component only — callers supply the buffer view. Gating logic (support gates, fraction-window checks, 2·d thresholds) belongs in the caller; it is explicitly not implemented here (docstrings note the relevant gates for each estimator).
Registration: these functions are module-public (importable from
blackjax.adaptation.metric_estimators) but are NOT exported at the
blackjax top-level. Top-level export and consumer re-wiring are
deliberate follow-up work.
Functions#
|
Informativeness of each eigenvalue: how far it deviates from isotropic. |
Select the top- |
|
Fisher-divergence-minimising low-rank inverse mass matrix. |
|
Draws-only SVD low-rank inverse mass matrix (MCLMC Scheme A estimator). |
|
Chan/Welford-accumulated covariance → low-rank metric via |
|
|
Diagonal (per-coordinate) sample variance via Welford's algorithm. |
|
Dense sample covariance via Welford's algorithm. |
|
Core math of the Fisher-diagonal estimator from pre-computed variances. |
|
Fisher-divergence-minimising diagonal inverse mass matrix. |
|
Coordinate-wise population variance from raw draws. |
Module Contents#
- eigenvalue_informativeness(eigenvalues: blackjax.types.Array) blackjax.types.Array[source]#
Informativeness of each eigenvalue: how far it deviates from isotropic.
Computes \(|\lambda - 1|\) for each entry of
eigenvalues. Directions with large informativeness deviate the most from the identity metric and benefit the most from low-rank preconditioning.This idiom is used identically in:
fisher_score_low_rank()(step 9 — top-k selection in projected subspace; see_compute_low_rank_metricin this module).draws_singular_value_low_rank()(top-k SVD eigenvalue selection; seemclmc_lrd_adaptation._extract_lrd_from_samples:271).sample_covariance_eigh_low_rank()(top-k eigh eigenvalue selection; seemeads_adaptation._lrd_from_accumulated_covariance:309).
- Parameters:
eigenvalues – Shape
(q,). Eigenvalues of a correlation (or preconditioned) matrix. An isotropic Gaussian has all eigenvalues equal to 1 and informativeness 0.- Returns:
\(|\lambda_i - 1|\) for each \(\lambda_i\).
- Return type:
Array, shape
(q,)
- select_top_eigenvalues_by_informativeness(eigenvalues: blackjax.types.Array, eigenvectors: blackjax.types.Array, max_rank: int, *, tail_handling: Literal['mask_pad', 'raw'] = 'mask_pad', cutoff: float = 2.0) tuple[blackjax.types.Array, blackjax.types.Array][source]#
Select the top-
max_rankeigenpairs byeigenvalue_informativeness().Two production consumers use this pattern with divergent tail handling:
tail_handling="mask_pad"(default)Used by
fisher_score_low_rank(). Eigenvalues inside the uninformative band \([1/\text{cutoff},\ \text{cutoff}]\) are masked to 1 (they carry no preconditioning benefit), making the corresponding direction a no-op in the metric. When the input subspace has fewer thanmax_rankeigenvectors (q < max_rank, which arises whend < 2 · max_rank), the output is zero-padded to the requested shape — zero columns inUandlam=1entries — so the return shape is always(d, max_rank)/(max_rank,). Matches_compute_low_rank_metric(this module) steps 8–9.tail_handling="raw"Used by
draws_singular_value_low_rank()andsample_covariance_eigh_low_rank(). The top-k pairs are returned as-is: no informativeness masking, no padding. The caller must ensuremax_rank <= q; behaviour is undefined (JAX will silently returnqcolumns) whenmax_rank > q. Matchesmclmc_lrd_adaptation._extract_lrd_from_samples:271–275 andmeads_adaptation._lrd_from_accumulated_covariance:309–312.Tie-break divergence: the two modes differ not only in masking but also in sort stability for exact
|λ−1|ties:"mask_pad"usesargsort(-scores)(ascending original-index among ties; Fisher consumer convention);"raw"usesargsort(scores)[::-1](descending original-index; both raw-source conventions). On realeigh/svdspectra bit-exact ties do not occur — even truly degenerate inputs return ulp-broken eigenvalues — so the difference is behaviorally inert on continuous data; the raw path preserves byte-fidelity to its sources nonetheless.
- Parameters:
eigenvalues – Shape
(q,). Must be the eigenvalues associated with the columns ofeigenvectors.eigenvectors – Shape
(d, q). Columns are the eigenvectors.max_rank – Number of eigenpairs to return. Must be a static Python integer (determines the output shape).
tail_handling – See above. Default
"mask_pad"matches the Fisher-score consumer.cutoff – Only used when
tail_handling="mask_pad". Eigenvalues in \([1/\text{cutoff},\ \text{cutoff}]\) are masked to 1. Default2.0matches nutpie’sc=2.
- Returns:
U_out (Array, shape
(d, max_rank)(mask_pad) or(d, ≤max_rank)(raw)) – Selected (and possibly masked/padded) eigenvectors.lam_out (Array, shape
(max_rank,)or(≤max_rank,)) – Selected (and possibly masked) eigenvalues.
- fisher_score_low_rank(draws: blackjax.types.Array, grads: blackjax.types.Array, max_rank: int, *, gamma: float = 1e-05, cutoff: float = 2.0) blackjax.mcmc.metrics.LowRankInverseMassMatrix[source]#
Fisher-divergence-minimising low-rank inverse mass matrix.
Implements Steps 1–9 of Algorithm 1 of [SCC26], following the nutpie reference implementation (
nuts-rssrc/transform/adapt/low_rank.rsestimate_mass_matrix).The inverse mass matrix has the form
\[M^{-1} = \operatorname{diag}(\sigma) \bigl(I + U(\Lambda - I)U^\top\bigr) \operatorname{diag}(\sigma)\]where \(\sigma\), \(U\), \(\Lambda\) minimise the sample Fisher divergence from \(\{(x_i, \nabla \log p(x_i))\}\).
Extracted from:
blackjax.adaptation.low_rank_adaptation._compute_low_rank_metric+._spd_mean(main @ 532631c1).Key asymmetry vs
draws_singular_value_low_rank(): this estimator uses BOTH draws and score gradients and applies γ-regularisation plus cutoff-masking on the eigenvalues.draws_singular_value_low_rank()uses draws only and applies neither — the docstring notes this divergence explicitly.Diagonal scale
σ = (Var[x] / Var[∇ log p])^{1/4}is the per-coordinate optimal scale (paper §3.1); clipped to[1e-20, 1e20](nutpie range).AIRM geometric mean
Σ = C_x # C_a^{-1}(AIRM = affine-invariant Riemannian metric): Theorem 2.3 / Eq. 9 of [SCC26] — the regularised optimal inverse mass matrix is the geometric mean of the draw covariance with the inverse score covariance.γ-regularisation
C = P P^T / γ + I(nutpie convention: the unnormalised sum-of-outer-products divided byγdirectly, nonscaling). Influence fades asngrows (Theorem 2.4).Cutoff masking: eigenvalues in
[1/cutoff, cutoff]are set to 1 (no preconditioning benefit); defaultcutoff=2matches nutpie.dtype promotion (round-9 schedule-port audit): promotes internally to
float64whenjax_enable_x64is active, regardless of the chain’s working dtype. Returns in the caller’s original dtype.G-layer note: the 2·d support gate (
n ≥ 2·dbefore the LR estimate is trusted) and any fraction-window guard are G-layer concerns and are NOT implemented here.- Parameters:
draws – Shape
(n, d). Chain positions (all rows must be valid samples — no zero-padding).grads – Shape
(n, d). Log-density gradients at the corresponding draws.max_rank – Maximum number of eigenvectors in the low-rank correction. Must be a static Python integer (determines output shape).
gamma – Regularisation scale (nutpie convention). Default
1e-5matches nutpie’sLowRankSettings::default.cutoff – Eigenvalue cutoff for informativeness masking. Default
2.0matches nutpie’sc=2.
- Returns:
(sigma, U, lam)with shapes(d,),(d, max_rank),(max_rank,).- Return type:
Notes
The optimal translation
μ* = x̄ + σ² ⊙ ᾱ(paper §3.2) is an adaptation-layer output, not part of the metric. Compute it separately from the per-draw means if needed by the warmup wiring (a warmup-wiring concern, deliberately outside this estimator).
- draws_singular_value_low_rank(draws: blackjax.types.Array, max_rank: int) blackjax.mcmc.metrics.LowRankInverseMassMatrix[source]#
Draws-only SVD low-rank inverse mass matrix (MCLMC Scheme A estimator).
Estimates the low-rank inverse mass matrix from the SVD of centred, standardised draws. No gradient information is required.
Extracted from:
blackjax.adaptation.mclmc_lrd_adaptation._extract_lrd_from_samples(main @ 532631c1).Key asymmetry vs
fisher_score_low_rank():Draws only — no score gradients.
No regularisation (no γ): the covariance is estimated from raw outer products without a
P P^T / γ + Iregularisation term.No masking: eigenvalues are returned as-is (
tail_handling="raw"inselect_top_eigenvalues_by_informativeness()); non-informative eigenvalues are NOT masked to 1. This is a deliberate design choice in the MCLMC-LRD pilot estimator: the raw eigenspectrum is what drives the effective condition number diagnostics downstream.
This asymmetry is preserved faithfully here; see the module docstring’s source-lineage table for context.
G-layer note: the Geyer-ESS support gate (
k_used = min(k, ⌊n_eff/2⌋),mclmc_lrd_adaptation.py:636) and then ≥ 2·dthreshold are G-layer concerns. Ensuremax_rank ≤ min(n, d)before calling (behaviour is undefined otherwise; JAX will return fewer thanmax_rankcolumns whenmax_rank > min(n, d)).- Parameters:
draws – Shape
(n, d). All rows must be valid samples (no zero-padding).max_rank – Number of eigenpairs to return. Must be a static Python integer and satisfy
max_rank ≤ min(n, d).
- Returns:
(sigma, U, lam)wheresigmais the per-coordinate standard deviation andlamare the raw SVD eigenvalues of the sample correlation matrix (not masked to 1 for near-unity values).- Return type:
Notes
The full eigenspectrum needed for
_kappa_eff_pilot()diagnostics is NOT returned here — that function operates on the full sorted spectrum (lam_all_sorted). If you need the full spectrum, call_extract_lrd_from_samplesdirectly (G-layer concern, not part of the pure estimator).
- sample_covariance_eigh_low_rank(m2: blackjax.types.Array, count: blackjax.types.Array | int, max_rank: int) blackjax.mcmc.metrics.LowRankInverseMassMatrix[source]#
Chan/Welford-accumulated covariance → low-rank metric via
eigh.Extracts a low-rank inverse mass matrix from a Chan-parallel-accumulated sum of squared deviations matrix (MEADS / MCLMC-LRD Scheme-B estimator).
Extracted from:
blackjax.adaptation.meads_adaptation._lrd_from_accumulated_covariance(main @ 532631c1, landed via #954).The input
m2is the accumulated Chan-parallel M2 matrix (shape(d, d)):\[M_2 = \sum_{i=1}^n (x_i - \bar{x}_n)(x_i - \bar{x}_n)^T\]which gives the Bessel-corrected sample covariance as
C = M_2 / (n - 1). The correlation matrix is thenR = D^{-1/2} C D^{-1/2}whereD = diag(C), andeighofRgives the eigenbasis.Tail handling: raw (no cutoff masking, no zero-padding) — matching the MEADS implementation. The G-layer (
meads_adaptation’slow_rank_window_fraction/ 2·d gate) ensures the estimate is only used when the accumulated support suffices.G-layer note: the fraction-window gate (only accumulate draws in the second half of the adaptation window,
low_rank_window_fraction=0.5) and the 2·d support threshold are G-layer concerns; they gate when to call this estimator, not what it computes. Callers must ensurecountreflects enough effective support before calling.- Parameters:
m2 – Shape
(d, d). Accumulated Chan-Welford sum of squared deviations (NOT divided bycount; this function applies the Bessel correction/ max(count - 1, 1)internally).count – Total number of samples accumulated into
m2. May be a traced JAX integer (safe insidejax.lax.scan).max_rank – Number of eigenpairs to return. Must be a static Python integer and satisfy
max_rank ≤ d.
- Returns:
(sigma, U, lam)with shapes(d,),(d, max_rank),(max_rank,).- Return type:
- welford_diagonal(draws: blackjax.types.Array) blackjax.types.Array[source]#
Diagonal (per-coordinate) sample variance via Welford’s algorithm.
Thin scan-wrapper over
blackjax.adaptation.mass_matrix.welford_algorithm()withis_diagonal_matrix=True. Computes the Bessel-corrected sample variance \(s^2_i = \frac{1}{n-1}\sum_{j=1}^n (x_{ji} - \bar{x}_i)^2\) for each coordinatei.Why a wrapper instead of direct ``jnp.var``: having a single estimator import surface ensures future callers can swap to streaming Welford (e.g., for online adaptation) without changing call sites. The algorithm itself is NOT moved or duplicated — only the call site is unified here.
Source:
blackjax.adaptation.mass_matrix.welford_algorithm(is_diagonal_matrix=True).- Parameters:
draws – Shape
(n, d).- Returns:
Bessel-corrected per-coordinate sample variance (= diagonal of the sample covariance matrix).
- Return type:
Array, shape
(d,)
- welford_dense(draws: blackjax.types.Array) blackjax.types.Array[source]#
Dense sample covariance via Welford’s algorithm.
Thin scan-wrapper over
blackjax.adaptation.mass_matrix.welford_algorithm()withis_diagonal_matrix=False. Computes the Bessel-corrected sample covariance matrix.Source:
blackjax.adaptation.mass_matrix.welford_algorithm(is_diagonal_matrix=False).- Parameters:
draws – Shape
(n, d).- Returns:
Bessel-corrected sample covariance matrix.
- Return type:
Array, shape
(d, d)
- fisher_score_diagonal_from_moments(variance: blackjax.types.Array, gradient_variance: blackjax.types.Array) blackjax.types.Array[source]#
Core math of the Fisher-diagonal estimator from pre-computed variances.
Computes \(\sigma^2 = \sqrt{\mathrm{Var}[x] / \mathrm{Var}[\nabla \log p]}\) per coordinate — the same formula as
fisher_score_diagonal()but operating on already-computed per-coordinate variances rather than raw draw arrays.This entry point is intended for callers that accumulate moments online (e.g. via
_FisherMomentBlock) and want to avoid materialising the full draw array. The caller is responsible for supplying Bessel-corrected (or otherwise normalised) variances; the ratio is invariant to a sharednvsn-1factor so either convention is acceptable for the diagonal estimator.Near-zero gradient protection (identical to
fisher_score_low_rank()andfisher_score_diagonal()):gradient_varianceis floored at1e-10before division, and the result is clipped to nutpie’s[1e-20, 1e20]range before squaring.Pairing insensitivity: this function consumes only the marginal variances
Var[x_i]andVar[∇log p_i]per coordinate. The estimator is therefore insensitive to which draw is paired with which gradient within a batch — any per-batch pairing permutation produces the samevarianceandgradient_varianceinputs and hence the same output. If cross-moment information (e.g. draw-grad covariance) is required, a future extension to this signature must add those moments explicitly.Planned extension note: this entry point is intentionally separate so that future updates to the estimator (e.g. adding draw-grad cross moments) can extend the from_moments signature without changing the raw-draws wrapper
fisher_score_diagonal().- Parameters:
variance – Shape
(d,). Per-coordinate position variance \(\mathrm{Var}[x]\). Must be non-negative; typically Bessel-corrected.gradient_variance – Shape
(d,). Per-coordinate log-density-gradient variance \(\mathrm{Var}[\nabla \log p]\). Must be non-negative; floored at1e-10internally.
- Returns:
Diagonal inverse mass matrix \(\sigma^2 = \sigma_{\text{clip}}^2\) where \(\sigma_{\text{clip}} = \operatorname{clip}(\sigma,\, 10^{-20},\, 10^{20})\) and \(\sigma = (\mathrm{Var}[x] / \max(\mathrm{Var}[\nabla \log p],\, 10^{-10}))^{1/4}\).
The clip is on the scale \(\sigma\), not on \(\sigma^2\), so the returned values span \([10^{-40}, 10^{40}]\). Under float32, \(\sigma^2\) overflows to
infwhen \(\sigma \approx 10^{20}\) (float32 max \(\approx 3.4 \times 10^{38}\), so \(10^{40}\) overflows); the caller must handle this if float32 inputs can drive \(\sigma\) to its clip boundary. This matchesfisher_score_low_rank()’ssigmaclip and is not changed here to preserve numerical consistency between the two estimators.- Return type:
Array, shape
(d,)
- fisher_score_diagonal(draws: blackjax.types.Array, grads: blackjax.types.Array) blackjax.types.Array[source]#
Fisher-divergence-minimising diagonal inverse mass matrix.
Computes \(\sigma^2 = \sqrt{\mathrm{Var}[x] / \mathrm{Var}[\nabla \log p]}\), the per-coordinate diagonal estimator of [SCC26].
This is the diagonal-only analogue of
fisher_score_low_rank(): that function’s diagonal scale \(\sigma = (\mathrm{Var}[x] / \mathrm{Var}[\nabla \log p])^{1/4}\) gives the corresponding inverse mass matrix as \(\sigma^2 = \sqrt{\mathrm{Var}[x] / \mathrm{Var}[\nabla \log p]}\) when the low-rank correction(U, lam)is dropped.Extracted from: branch
b197f1e2(feat/window-adaptation-fisher-diag, 2026-07-04),blackjax.adaptation.mass_matrix._fisher_diagonal_inverse_mass.Near-zero gradient protection (same as
fisher_score_low_rank()):Var[∇ log p]is floored at1e-10before division, and the result is clipped to nutpie’s[1e-20, 1e20]range before squaring.Note on variance convention:
Var[x]andVar[∇ log p]are computed with the same normalisation (Bessel-corrected,n-1divisor, viawelford_diagonal()); the ratio is invariant to a sharednvsn-1factor (branchb197f1e2commit message, verified).Implementation: thin wrapper over
fisher_score_diagonal_from_moments()— computes Bessel-corrected per-coordinate variances via twowelford_diagonal()scans, then delegates all arithmetic to the from-moments entry point.- Parameters:
draws – Shape
(n, d). Chain positions.grads – Shape
(n, d). Log-density gradients at the corresponding draws.
- Returns:
Diagonal inverse mass matrix \(\sigma^2\).
- Return type:
Array, shape
(d,)
- sample_variance_diagonal(draws: blackjax.types.Array) blackjax.types.Array[source]#
Coordinate-wise population variance from raw draws.
Computes \(\hat{\sigma}^2_i = \mathbb{E}[x_i^2] - \mathbb{E}[x_i]^2\) (population variance, no Bessel correction) for each coordinate
i.Extracted from (verbatim duplicate):
blackjax.adaptation.mclmc_adaptation,L_step_size_adaptation:341–342:variances = x_squared_average - jnp.square(x_average)wherex_average = E[x]andx_squared_average = E[x^2]are streaming step-size-weighted averages.blackjax.adaptation.adjusted_mclmc_adaptation,adjusted_mclmc_find_L_and_step_size:374–375: identical formula.
Both inline occurrences use the result as
inverse_mass_matrix = variances, i.e., the population variance IS the diagonal IMM. The streaming-average formulation (weighted by step size viaincremental_value_update) is an adaptation-layer scheduling concern; the pure estimator here takes the batch of accumulated draws directly.Population (not Bessel-corrected) to match the MCLMC streaming form: the weighted incremental average
avg ← avg + w·(x − avg) / Σwconverges to \(\mathbb{E}[x]\) under uniform weights, not the unbiased \(n/(n-1)\) form.- Parameters:
draws – Shape
(n, d). The draws accumulated over the estimation window.- Returns:
Per-coordinate population variance \(\mathbb{E}[x^2] - \mathbb{E}[x]^2\).
- Return type:
Array, shape
(d,)