GUIBRUSHR.Retrieval.ExofastMCMC.snooker module

Snooker (DE-MCzs) move primitives for the GUIBRUSHR sampler.

This module is a dependency-free (numpy-only) reference implementation of the snooker update of ter Braak and Vrugt (2008), “Differential Evolution Markov Chain with snooker updater and fewer chains”, Stat. Comput. 18(4), 435-446.

It is deliberately isolated from the rest of the retrieval pipeline so it can be unit-tested in isolation and reused verbatim by the production worker (ModelData.parallel_chain). It implements only the snooker-specific maths:

  • snooker_propose : the line-constrained proposal x_prop = x_i + gamma_s * (zp1 - zp2)

  • snooker_log_jacobian : the radial Jacobian (d-1)*(log||x_prop-z|| - log||x_i-z||)

The 90% “parallel” DE-MCz move (x_i + gamma*(zR1 - zR2) + e) reuses the existing GUIBRUSHR machinery and is not duplicated here; only its archive draw differs.

Conventions (validated at Gate G1 against the project guide snooker_demc.pdf section 3 Eq. 3, ter Braak and Vrugt 2008, mc3 chain.py and the first author’s demc_zs.r reference):

  • The archive Z is a 2D array of shape (M, d): M past+present states, each a parameter vector of length d. Draws are uniform over the M rows.

  • gamma_s is dimension-independent and drawn from Uniform(1.2, 2.2) per step (mc3 parity). It is NOT divided by sqrt(d) (that is the parallel-move convention only).

  • There is no additive noise term e on the snooker move.

  • The Jacobian is always combined in log-space as (d-1) * dlog||.||; the raw power ||.||**(d-1) is never formed (it overflows/underflows for large d).

GUIBRUSHR.Retrieval.ExofastMCMC.snooker.draw_gamma_s(rng: Generator) float[source]

Draw the snooker step scale gamma_s ~ Uniform(1.2, 2.2).

GUIBRUSHR.Retrieval.ExofastMCMC.snooker.sample_distinct_indices(rng: Generator, n: int, k: int) tuple[int, ...][source]

Draw k distinct integer indices uniformly from [0, n).

Parameters:
  • rng – numpy random Generator.

  • n – archive size (number of rows to choose from).

  • k – number of distinct indices required.

Returns:

A tuple of k distinct ints.

Raises:

ValueError – if n < k (not enough states to draw distinct indices).

GUIBRUSHR.Retrieval.ExofastMCMC.snooker.project_onto_line(point: ndarray, anchor: ndarray, unit_dir: ndarray) ndarray[source]

Orthogonal projection of point onto the line through anchor.

The line has unit direction unit_dir. Returns the nearest point on the line, i.e. anchor + <point - anchor, unit_dir> * unit_dir.

GUIBRUSHR.Retrieval.ExofastMCMC.snooker.snooker_propose(x_i: ndarray, archive: ndarray, rng: Generator, gamma_s: float | None = None) tuple[ndarray, ndarray][source]

Generate a snooker proposal for the current state x_i.

Picks an anchor z and two further states z1, z2 uniformly at random (distinct rows) from the archive, projects the difference z1 - z2 onto the line through x_i and z, and steps along that line. No additive noise term is used.

Parameters:
  • x_i – current state, shape (d,).

  • archive – archive Z of past+present states, shape (M, d) with M >= 3.

  • rng – numpy random Generator.

  • gamma_s – optional fixed step scale. If None, drawn from Uniform(1.2, 2.2).

Returns:

(x_prop, z_anchor) where x_prop is the proposed state, shape (d,), and z_anchor is the anchor z (shape (d,)) needed by snooker_log_jacobian for the acceptance correction.

Raises:

ValueError – if the archive has fewer than 3 states.

Note

Mathematically zp1 - zp2 == <z1 - z2, u> * u (the compact form used here), so the explicit orthogonal projections are not materialised.

GUIBRUSHR.Retrieval.ExofastMCMC.snooker.snooker_log_jacobian(x_i: ndarray, x_prop: ndarray, z_anchor: ndarray, d: int) float[source]

Radial-Jacobian term of the snooker log-acceptance ratio.

Returns (d - 1) * (log||x_prop - z|| - log||x_i - z||), computed entirely in log-space so that the raw power ||.||**(d-1) is never formed.

For d == 1 the term is exactly 0 (snooker reduces to plain Metropolis along the line). If the proposal lands on the anchor (or the current state coincides with it) the radius underflows and the move is rejected by returning -inf, matching the fact that the spherical shell has zero volume at z.

Parameters:
  • x_i – current state, shape (d,).

  • x_prop – proposed state, shape (d,).

  • z_anchor – anchor z returned by snooker_propose, shape (d,).

  • d – number of free parameters.

Returns:

The log-Jacobian contribution to log_alpha; -inf to force reject.