emu_pk
Differentiable emulation of the linear matter power spectrum, over an eight-parameter cosmology that includes the summed neutrino mass and CPL dark energy, out to $k = 200\ h,\mathrm{Mpc}^{-1}$ and $z = 5$.
It is written in JAX, so derivatives with respect to cosmological parameters come from automatic differentiation rather than finite differences — and two of them are exact, because the primordial power law is divided out of the training target and restored in closed form.
import numpy as np
from emu_pk import PkEmulator
emu = PkEmulator()
k = np.logspace(-3, 1, 200) # h/Mpc
# omega_b omega_cdm h n_s ln10A_s sum_mnu w0 wa
theta = np.array([0.02237, 0.1200, 0.6736, 0.9649, 3.044, 0.06, -1.0, 0.0])
pk = emu.pk(k, z=0.5, params=theta) # P_m(k, z) in (Mpc/h)^3
pk_cb = emu.pk_cb(k, z=0.5, params=theta) # cdm + baryons, without neutrinos
Accuracy
Against held-out CLASS solves — a Latin hypercube on a different seed from the
training design, so no scored point was trained on. The full record is
emu_pk/data/validation.json, written by
python -m emu_pk.validate and not typed by hand.
Three quantities are scored, and together they describe $P(k)$: its amplitude at $k = 0.05\ h,\mathrm{Mpc}^{-1}$, its shape once both spectra are renormalised there, and the two combined with nothing removed. Medians are over the held-out cosmologies, at $z = 0$.
| at $z = 0$ | median | 90th | max |
|---|---|---|---|
| amplitude at $k = 0.05$ | 0.012 % | 0.035 % | 0.059 % |
| shape, renormalised | 0.111 % | 0.224 % | 0.621 % |
| total, absolute | 0.112 % | 0.221 % | 0.603 % |
0.112 % is the single number for $P(k)$. CosmoPower's released linear-matter model reaches 0.159 % on the shape measure, on a box narrower in four of the five axes the two share and equal on the fifth. Shape error is the largest fractional departure from CLASS over $k \in [10^{-3}, 10]\ h,\mathrm{Mpc}^{-1}$; the amplitude is the factor that renormalisation divides out, and it holds between 0.005 % and 0.012 % across the whole redshift range.
Derivatives are what a Fisher forecast actually consumes, and an emulator can reproduce $P(k)$ to a tenth of a percent and still get $\partial\ln P/\partial\theta$ wrong. Against central differences of CLASS, at $z = 0$:
| parameter | error | parameter | error | |
|---|---|---|---|---|
ln10A_s |
exact | sum_mnu |
0.18 % | |
n_s |
exact | w0 |
0.16 % | |
omega_cdm |
0.06 % | omega_b |
0.20 % | |
h |
0.12 % | wa |
0.41 % |
And with respect to redshift — the derivative $f\sigma_8$ is built from:
| z = 0 | z = 0.5 | z = 1 | z = 2 | |
|---|---|---|---|---|
| $\partial\ln P/\partial z$ | 0.155 % | 0.015 % | 0.012 % | 0.008 % |
| the measurement's own floor | 0.049 % | 0.011 % | 0.006 % | 0.005 % |
Away from $z = 0$ this sits within a factor of about two of what the comparison itself can resolve, so most of what is quoted there is the ruler rather than the network. At $z = 0$ the ratio is 3.1: that node is an endpoint in slope, with nothing on the $z < 0$ side to constrain it.
The box
Deliberately wider than CosmoPower's mpk_lin, and carrying three parameters it
does not have. Outside these bounds the network does not fail, it
extrapolates — returning a number that is finite, smooth and unwarranted — so
PkEmulator checks the box on every call that can afford to look.
| parameter | CosmoPower | emu_pk |
|---|---|---|
omega_b |
0.01875 – 0.02625 | 0.0170 – 0.0280 |
omega_cdm |
0.05 – 0.255 | 0.0500 – 0.3000 |
h |
0.64 – 0.82 | 0.5500 – 0.8500 |
n_s |
0.84 – 1.10 | 0.8400 – 1.1000 |
ln10A_s |
1.61 – 3.91 | 1.6100 – 4.0000 |
sum_mnu [eV] |
— | 0.0000 – 0.6000 |
w0 |
— | −1.5000 – −0.5000 |
wa |
— | −1.0000 – 0.6000 |
| $k_{\max}$ [h/Mpc] | 14.56 | 200 |
| $z$ | 0 – 5 | 0 – 5 |
Points with w0 + wa >= 0 are excluded: CPL dark energy then grows without
bound towards early times and dominates before recombination, which is not a
cosmology anyone means to train on.
Install
pip install emu_pk # inference: numpy + jax, nothing else
pip install 'emu_pk[gen]' # + classy, to generate data or validate
pip install 'emu_pk[train]' # + optax, to train
The split is load-bearing. import emu_pk in an environment with no
classy and no optax must work — that is what lets another package depend
on this one without inheriting a Boltzmann solver or a training stack — and the
test suite asserts it.
[gen] compiles CLASS from source and needs a C compiler.
A dedicated environment
environment.yml is a minimal conda environment — python,
numpy and JAX, and nothing else:
mamba env create -f environment.yml # or: conda env create -f environment.yml
mamba activate emu_pk
pip install -e .
It pins the CPU build of jaxlib: 64 MB against 199 MB for the CUDA one,
and left unpinned the build depends on whether the machine that solved the
environment happened to have a driver. The file says how to swap it for a GPU.
The extras are commented blocks in the same file — uncomment the one you need.
classy is not on conda-forge, so [gen] comes from pip and compiles CLASS
from source.
What is here
| Module | Needs | What it does |
|---|---|---|
emu_pk.model |
numpy, jax | evaluate the network, in pure JAX |
emu_pk.ratio |
numpy, jax | the CLASS-distilled massive-ν and CPL correction |
emu_pk.box, .grid, .cosmo, .interp |
numpy, jax | the hypercube, the grids, the conventions, the interpolation |
emu_pk.generate, .assemble |
[gen] |
run CLASS; shards → table or training set |
emu_pk.train |
[train] |
fit the network |
emu_pk.validate |
[gen] |
shape error and derivative error against CLASS |
emu_pk.ratio is a correction measured from CLASS on a grid in
$(\Sigma m_\nu, w_0, w_a, z, k)$ and applied multiplicatively to a
massless-ΛCDM spectrum. It is exactly 1 at the ΛCDM massless corner, which is
what lets it be applied unconditionally — a Python branch on the neutrino mass
would be a branch on a tracer and would break the gradient. It exists so that an
emulator without neutrinos or dark energy can be given both. emu_pk's own
network is trained on massive-neutrino w0waCDM spectra directly and needs no
correction.
Conventions
$k$ in $h,\mathrm{Mpc}^{-1}$ and $P$ in $(h^{-1}\mathrm{Mpc})^3$ throughout.
CLASS's $1/\mathrm{Mpc}$ is converted once, in generate, so nothing
downstream carries an $h$.
$\Omega_m$ contains the neutrinos, and $\Omega_\nu = \Sigma m_\nu/(93.14 h^2)$.
Validating it yourself
With [gen] installed, the numbers above are one command:
python -m emu_pk.validate --json my_validation.json
It scores shape error and derivative error against CLASS across the redshift range, reports the finite-difference floor of its own comparison, and splits the box edge and the extreme-quintessence corner out from the interior.
Tests
pip install 'emu_pk[dev]'
python -m pytest tests/ -q
230 tests. Every statement in emu_pk is executed by the suite and 99 % of
its branches. [dev] installs everything the suite needs, including optax,
since the tests that exercise the trainer import it. CLASS is not needed: it is
replaced by a stub wherever a test wants a spectrum rather than a correct
one, and the few tests that do want the real solver skip without it. The tests
that compare conventions against the consuming package skip when it is not
importable.
Documentation
Full documentation, including a tutorial with figures, is at
emu-pk.readthedocs.io. Decisions that would
look arbitrary from the source alone are in
docs/design_notes.md.
Citing
If you use emu_pk, please cite it via CITATION.cff, and
also:
- CosmoPower — Spurio Mancini et al. (2022), MNRAS 511, 1771. The network architecture and the learned activation are theirs.
- CLASS — Blas, Lesgourgues & Tram (2011), JCAP 07, 034. Every training spectrum and every validation reference is a CLASS solve.
Licence
BSD 3-Clause. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file emu_pk-1.0.0.tar.gz.
File metadata
- Download URL: emu_pk-1.0.0.tar.gz
- Upload date:
- Size: 17.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a420cfc14659bd67ba319cbdb1b54edd544aade66b335bb2bfee270716e898f
|
|
| MD5 |
ae3082bbc7e29ae66760a11a8d2d4a59
|
|
| BLAKE2b-256 |
0019361d72f7f9107f41228d888a227ac5fcd1c26224480518ec753480a0a6a3
|
Provenance
The following attestation bundles were made for emu_pk-1.0.0.tar.gz:
Publisher:
publish.yml on JohanComparat/emu_pk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
emu_pk-1.0.0.tar.gz -
Subject digest:
0a420cfc14659bd67ba319cbdb1b54edd544aade66b335bb2bfee270716e898f - Sigstore transparency entry: 2666887537
- Sigstore integration time:
-
Permalink:
JohanComparat/emu_pk@de481bf635d517836cde5a34f5781978df30cbc0 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/JohanComparat
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@de481bf635d517836cde5a34f5781978df30cbc0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file emu_pk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: emu_pk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 17.5 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21685dbca8d92e9fc7f9e089341831ad865214e1bfa7dfddc9e83c868e1a3ad0
|
|
| MD5 |
227057ed82dec019d2282b246528b194
|
|
| BLAKE2b-256 |
1cb18310ddbda524f0e693b19ccbff306b612d646b986c26713121082d95dfc8
|
Provenance
The following attestation bundles were made for emu_pk-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on JohanComparat/emu_pk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
emu_pk-1.0.0-py3-none-any.whl -
Subject digest:
21685dbca8d92e9fc7f9e089341831ad865214e1bfa7dfddc9e83c868e1a3ad0 - Sigstore transparency entry: 2666887616
- Sigstore integration time:
-
Permalink:
JohanComparat/emu_pk@de481bf635d517836cde5a34f5781978df30cbc0 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/JohanComparat
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@de481bf635d517836cde5a34f5781978df30cbc0 -
Trigger Event:
push
-
Statement type: