Skip to content

The registry

The registry is the single front door. Everything is addressed by a key (property, gravity, redshift); there is exactly one artifact per key.

from haloemu import get_registry
reg = get_registry()
reg.predict(prop, gravity, z, theta, **kw)   # the only call you usually need

Keys

The canonical key string is "<property>/<gravity>/z<z:.2f>", e.g. "hmf/LCDM/z0.25". On disk:

haloemu/artifacts/<property>/<gravity>/z<z:.2f>/{emulator.pkl, meta.json}

indexed by haloemu/manifest.json. List everything:

for e in reg.list():
    print(e["key"], e["kind"], e.get("accuracy"))

Redshift snapping

Float redshifts are snapped to the nearest registered key within Z_TOL = 0.005 — there is no exact-float matching. Currently registered redshifts:

  • z = 0.25 — all properties, both gravities.
  • z = 0.00 — the matter sector only (hmf, pk_mm, xi_mm × {LCDM, fRn1}). Clustering (xi_hh, r_ab), bias, and velocity at z = 0 are not yet registered.

A redshift with no registered artifact within tolerance raises FileNotFoundError.

Two kinds of entry

The registry treats two plugin kinds uniformly through predict():

  • trained — a pickled artifact carrying weights. Its depends are train-time data dependencies: if the upstream halocat product changes, you retrain. predict() calls artifact.predict(theta, **kw).
  • derived — a recipe with no weights (b_diff, xi_hh). Its depends_on artifacts are resolved through the registry at predict time and combined by plugin.derive(). Predict-time recipe deps are pinned to the upstream artifact's content hash, so an upstream change forces a re-pin + re-validate.

MGBoostEmulator (the f(R) boosts) is a trained artifact that embeds and pins its ΛCDM base artifact — see Representations.

External dependencies (e.g. colossus for the EH98 baseline of pk_mm/xi_mm) are recorded in the manifest but never resolved by the registry; you supply them in the env. b_diff no longer has any — its comes from the in-suite hmf artifact (the gated freyja→hmf swap).

θ — parameter vectors

The theta argument is a raw parameter vector in a fixed order:

Gravity theta
LCDM [Omega_m, h, n_s, S_8]
fRn1 [Omega_m, h, n_s, S_8, logf_R0]

Pass S_8, not σ_8

S_8 = sigma_8 * sqrt(Omega_m / 0.3). The GR fiducial σ₈ = 0.8159 gives S_8 = 0.827914. Passing σ_8 silently produces a wrong (but plausible) prediction.

logf_R0 is log10(|f_R0|) for the f(R) n = 1 model.

Design (validity) ranges

Predictions inside the design hull are validated; outside it the GP extrapolates and is unvalidated.

Parameter Range
Omega_m [0.15, 0.445]
h [0.596, 0.795]
n_s [0.94, 0.989]
S_8 [0.65, 0.945]
logf_R0 [−7, −4.05]

Known θ vectors

from halocat.cosmology import get_cosmology, theta_keys

keys = theta_keys("fRn1")                              # ordered parameter names
th_f5 = [get_cosmology("fRn1",  0)[k] for k in keys]   # F5n1 fiducial (logf_R0=-5)
th_f6 = [get_cosmology("fRn1", -1)[k] for k in keys]   # F6n1 fiducial (logf_R0=-6)
# LCDM fiducial: get_cosmology("LCDM", 0); design models: imodel 1..64.

The two f(R) fiducials (imodel 0 = F5n1, -1 = F6n1) each have 100 measured boxes and serve as the sharp out-of-sample references.

Artifact introspection

art = reg.load(prop, gravity, z)        # the unpickled trained artifact
art.coord                               # 1-D coordinate grid (log10M / k / r)
art.theta_keys                          # the parameter names this artifact expects
reg.entry(prop, gravity, z)["accuracy"] # the manifest accuracy block
reg.predict(prop, gravity, z, theta, return_var=True)  # (mean, variance)

Surface artifacts carry extra axes: r_ab has art.thresholds (both matrix axes); the vel_* moments have art.pair_keys and art.r.