bayesmith.exact.reduced_basis
Source-derived signatures and contracts. See the reference index for coverage and conventions.
Module contract
Reduced-basis selection and orthonormalisation. A reduced basis is a dictionary of directions in the DATA space such that a model's prediction is well approximated by a combination of them: ``mu(theta) ~ sum_k c_k(theta) s_k``. The scheme is Field, Galley, Hesthaven, Kaye & Tiglio (2014, PRX 4, 031006); the special case ``s_k = d mu / d theta_k`` at a fiducial point is MOPED (Heavens, Jimenez & Lahav 2000) and score compression (Alsing & Wandelt 2018). **What is here is the array-level linear algebra and nothing else.** Choosing candidate directions from a bank, and turning candidates into a basis. What is NOT here, deliberately: * the **containers** -- a basis object carrying its raw and whitened rows, and a fidelity report. The migration ledger's D12 keeps those upstream, because their constructor-time refusals are part of a published exception identity; * the **declaration layer** that produces a bank from a parameter space and a pipeline. That is a question about a model's own vocabulary, and this package has no opinion about pipelines. **Selection and basis are different things, and that separation is load-bearing.** :func:`select_svd` and :func:`select_greedy` choose CANDIDATES; :func:`orthonormalise` turns candidates into a basis. Storing raw candidates gives a Gram matrix that no float64 quadratic form survives -- ``c^T G c`` then returns a finite and occasionally negative number rather than raising. That is also why :func:`numerical_rank` cuts at ``sqrt(eps)`` and not at ``eps``: the quadratic form squares the conditioning, so a set that is merely invertible is not usable. **The metric is the caller's, applied before anything here runs.** Every routine takes rows already whitened -- multiplied by the likelihood's own ``N^-1/2`` -- so "orthonormal in the metric the likelihood uses" is just "orthonormal", the Gram matrix is the identity and the projector is a matrix product. With any other convention the projector is not self-adjoint in the likelihood's inner product, the truncation residual stops being orthogonal to the span, and the score at the truth acquires a term that does not vanish: a BIAS, not a loss of sensitivity. Ported from ``rheplicant.inference.reduced_basis``.
numerical_rank
Defined in bayesmith.exact.reduced_basis, line 192.
numerical_rank(whitened_bank: jax.Array) -> intLargest ``k`` with ``s_k / s_0 > sqrt(eps)``. **Not a tuning knob.** Beyond this cut the Gram matrix of the retained set is numerically singular in float64, and ``c^T G c`` returns a finite, occasionally negative number rather than raising. ``sqrt(eps)`` rather than ``eps`` because the quadratic form SQUARES the conditioning: a set that is merely invertible is not usable in one. The two answers differ on real inputs -- a direction at ``1e-10`` of the leading one is above ``eps`` and below this cut -- and ``test_the_cut_is_sqrt_eps_and_not_eps`` is constructed to separate them, because every other test in that class passes under either. An empty bank, or one whose leading singular value is zero, is rank zero: answered before the ratio rather than by it, since neither has a ratio to take.
orthonormal_transform
Defined in bayesmith.exact.reduced_basis, line 121.
orthonormal_transform(candidates: jax.Array, rtol: float | None=None) -> tuple[jax.Array, tuple[int, ...]]``(M, kept)`` with ``M @ candidates`` orthonormal in the rows' metric.
Modified Gram-Schmidt with **one reorthogonalisation pass**, in numpy,
because the TRANSFORM is what a caller needs and a QR only hands back the
result. ``M`` is what lets the same combination be applied to the RAW
rows: a basis has to be usable by an epoch whose flag pattern differs from
the reference's, and the whitened copy is infinite at exactly the samples
the reference could not see -- there is no inverse back to unwhitened data
there, and that is what a zero weight MEANS rather than a limitation.
The second pass is not defensive. On a near-dependent set one pass loses
orthogonality and two recover it, measured in
``tests/exact/test_reduced_basis.py`` against a single-pass reference
computed in that file.
**Order is preserved**, so ``span(row_1..row_j)`` is nested. That is the
property seeding depends on: a direction placed first survives whatever
the later candidates do, which is how a science direction three orders
below the foregrounds is retained by construction rather than by choosing
the basis size large enough.
Args:
candidates: ``(k, n_data)`` rows, **already whitened**.
rtol: drop a direction whose residual norm falls below
``rtol * max_row_norm``. Defaults to ``sqrt(eps)``, for the reason
:func:`numerical_rank` gives.
Returns:
``(M, kept)``. ``M`` is ``(r, k)`` with ``r <= k``, and ``kept`` names
which candidate indices survived. ``r < k`` means the candidate set
was rank-deficient -- a fact about the model, which the caller decides
what to do about, so it is reported rather than raised.
Raises:
StructureError: if ``candidates`` is not two-dimensional.orthonormalise
Defined in bayesmith.exact.reduced_basis, line 186.
orthonormalise(candidates: jax.Array, rtol: float | None=None) -> jax.ArrayThe orthonormal rows themselves, when the transform is not needed.
select_greedy
Defined in bayesmith.exact.reduced_basis, line 239.
select_greedy(whitened_bank: jax.Array, count: int) -> jax.ArrayGreedy EIM-style selection: the worst-represented row, repeatedly. Returns rows OF THE BANK, in the order chosen -- the selection step of Field/Galley/Puerrer and nothing more. Orthonormalising them is a separate call, because storing the raw picks is what makes the Gram matrix unusable. Not the same as sorting by norm, and the difference is the point: after the first pick, a second row nearly parallel to it is almost entirely explained, so greedy takes a smaller ORTHOGONAL row instead. Pinned in ``test_it_picks_a_SPANNING_set_rather_than_the_largest_rows``. Stops early when the residual is exhausted -- a rank-``r`` bank yields ``r`` picks however many are asked for, rather than repeating one.
select_svd
Defined in bayesmith.exact.reduced_basis, line 220.
select_svd(whitened_bank: jax.Array, count: int) -> jax.ArrayThe ``count`` leading right singular directions of the bank. **Candidates, not a basis.** They happen to be orthonormal here, which is a property of the SVD rather than of the pipeline; :func:`orthonormalise` is still what makes that a claim the code depends on. Singular values of a bank of prior draws order modes by prior-induced AMPLITUDE, which is why a caller with a small signal of interest seeds the candidate set with its score direction first and relies on the nesting :func:`orthonormal_transform` guarantees.