bayesmithDocumentation0.10.0 source

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) -> int
Largest ``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.Array
The 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.Array
Greedy 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.Array
The ``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.

Type a concept or API name.