cosmograd 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. cosmograd-0.1.0/LICENSE +21 -0
  2. cosmograd-0.1.0/PKG-INFO +253 -0
  3. cosmograd-0.1.0/README.md +224 -0
  4. cosmograd-0.1.0/pyproject.toml +99 -0
  5. cosmograd-0.1.0/pyproject.toml.orig +98 -0
  6. cosmograd-0.1.0/src/cosmograd/__init__.py +31 -0
  7. cosmograd-0.1.0/src/cosmograd/background/__init__.py +9 -0
  8. cosmograd-0.1.0/src/cosmograd/background/_base.py +322 -0
  9. cosmograd-0.1.0/src/cosmograd/background/flat_lambdacdm.py +131 -0
  10. cosmograd-0.1.0/src/cosmograd/background/flat_w0wa_cdm.py +119 -0
  11. cosmograd-0.1.0/src/cosmograd/background/lambdacdm.py +109 -0
  12. cosmograd-0.1.0/src/cosmograd/background/protocol.py +201 -0
  13. cosmograd-0.1.0/src/cosmograd/background/w0wa_cdm.py +139 -0
  14. cosmograd-0.1.0/src/cosmograd/constants.py +22 -0
  15. cosmograd-0.1.0/src/cosmograd/cosmologies.py +62 -0
  16. cosmograd-0.1.0/src/cosmograd/perturbations/__init__.py +10 -0
  17. cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/__init__.py +10 -0
  18. cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_cache.py +200 -0
  19. cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_cosmology.py +389 -0
  20. cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_emulator.py +212 -0
  21. cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_power.py +216 -0
  22. cosmograd-0.1.0/src/cosmograd/perturbations/cosmopower/__init__.py +360 -0
  23. cosmograd-0.1.0/src/cosmograd/perturbations/eisenstein_hu.py +318 -0
  24. cosmograd-0.1.0/src/cosmograd/perturbations/growth.py +167 -0
  25. cosmograd-0.1.0/src/cosmograd/perturbations/protocol.py +169 -0
  26. cosmograd-0.1.0/src/cosmograd/py.typed +0 -0
  27. cosmograd-0.1.0/src/cosmograd/utils.py +63 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) binado
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,253 @@
1
+ Metadata-Version: 2.4
2
+ Name: cosmograd
3
+ Version: 0.1.0
4
+ Summary: Differentiable cosmology in JAX behind a small protocol contract.
5
+ Author: binado
6
+ Author-email: binado <bernardopveronese@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Typing :: Typed
15
+ Requires-Dist: diffrax>=0.7.2
16
+ Requires-Dist: jax>=0.10
17
+ Requires-Dist: jaxtyping>=0.3.11
18
+ Requires-Dist: numpy>=2.5.4
19
+ Requires-Dist: baccoemu>=2.3.0 ; extra == 'bacco'
20
+ Requires-Dist: platformdirs>=4.0 ; extra == 'bacco'
21
+ Requires-Dist: cosmopower-jax>=0.5.8 ; extra == 'cosmopower'
22
+ Requires-Python: >=3.12
23
+ Project-URL: Homepage, https://github.com/binado/cosmograd
24
+ Project-URL: Repository, https://github.com/binado/cosmograd.git
25
+ Project-URL: Issues, https://github.com/binado/cosmograd/issues
26
+ Provides-Extra: bacco
27
+ Provides-Extra: cosmopower
28
+ Description-Content-Type: text/markdown
29
+
30
+ # cosmograd
31
+
32
+ Differentiable cosmology in JAX behind a small protocol contract.
33
+
34
+ Consumers depend on two protocols, `Background` (expansion history and
35
+ distances) and `Perturbations` (growth and power spectra), not on any one
36
+ implementation. The core depends on JAX, jaxtyping and Diffrax; emulator
37
+ backends sit behind optional extras.
38
+
39
+ ## Install
40
+
41
+ ```console
42
+ uv add cosmograd # core: protocols + native backgrounds and linear power
43
+ uv add 'cosmograd[cosmopower]' # CosmoPower-JAX backend
44
+ uv add 'cosmograd[bacco]' # bacco emulator backend
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ```python
50
+ import jax
51
+ import jax.numpy as jnp
52
+ from cosmograd import FlatLCDM
53
+
54
+ cosmo = FlatLCDM(H0=67.36, omega_m=0.3153)
55
+ z = jnp.linspace(0.1, 2.0, 20)
56
+
57
+ cosmo.luminosity_distance(z) # Mpc
58
+ jax.jacfwd(lambda c: c.luminosity_distance(z))(cosmo) # FlatLCDM-shaped Jacobian
59
+ ```
60
+
61
+ `FlatW0WaCDM` adds CPL dark energy, $w(a) = w_0 + w_a (1 - a)$, with `w0` and `wa`
62
+ as extra differentiable parameters:
63
+
64
+ ```python
65
+ from cosmograd import FlatW0WaCDM
66
+
67
+ cosmo = FlatW0WaCDM(H0=67.36, omega_m=0.3153, w0=-0.9, wa=0.2)
68
+ jax.grad(lambda c: c.comoving_distance(1.0))(cosmo).w0
69
+ ```
70
+
71
+ `W0WaCDM` is the general background, adding a curvature parameter `omega_k`;
72
+ `LCDM`, `FlatW0WaCDM` and `FlatLCDM` are the same model with `omega_k`, `w0`
73
+ and `wa` fixed, so they are leaves of the pytree only for the parameters they
74
+ leave free. Curvature enters the expansion rate and the transverse distances
75
+ (`luminosity_distance`, `angular_diameter_distance`); the inverse of
76
+ `comoving_distance` is the line-of-sight one.
77
+
78
+ ```python
79
+ from cosmograd import W0WaCDM
80
+
81
+ cosmo = W0WaCDM(H0=67.36, omega_m=0.3153, omega_k=0.02)
82
+ cosmo.luminosity_distance(z)
83
+ ```
84
+
85
+ Every class also takes `omega_b`, `n_s` and `ln10as`, so that one object holds
86
+ the whole cosmology. No background method reads them. They default to the
87
+ Planck 2018 values (0.04897, 0.9665 and 3.047), which the perturbation backends
88
+ then use without warning, so set them whenever you compute spectra. Standard
89
+ cosmologies carry their own:
90
+
91
+ ```python
92
+ from cosmograd.cosmologies import Planck18
93
+
94
+ Planck18.luminosity_distance(z)
95
+ Planck18.with_parameters(omega_m=0.30) # a modified copy
96
+ ```
97
+
98
+ Here `omega_b` is $\Omega_b$ today, with `0 < omega_b < omega_m`, and `ln10as` is
99
+ $\ln(10^{10} A_s)$ at the pivot $0.05\,\mathrm{Mpc}^{-1}$.
100
+
101
+ The perturbation backends below take such a cosmology plus their own settings.
102
+ They assume a flat universe: `EisensteinHu` and `BaccoEmu` accept either flat class;
103
+ `CosmoPower` is trained on $\Lambda\mathrm{CDM}$ and takes a `FlatLCDM` only.
104
+
105
+ The native `EisensteinHu` backend provides linear matter power with EH98 baryon
106
+ acoustic oscillations, normalised by the primordial amplitude:
107
+
108
+ ```python
109
+ from cosmograd import EisensteinHu
110
+
111
+ pert = EisensteinHu(Planck18)
112
+ k = jnp.geomspace(1e-4, 1.0, 100) # 1/Mpc
113
+ pert.background is Planck18 # True
114
+ pert.matter_power_spectrum(k, z[:, None]) # Mpc^3, shape (20, 100)
115
+ pert.growth_function(z) # normalised to 1 at z = 0
116
+ # One FlatLCDM-shaped gradient over H0, omega_m, omega_b, n_s and ln10as.
117
+ jax.grad(lambda c: EisensteinHu(c).matter_power_spectrum(0.1, 0.5))(Planck18)
118
+ ```
119
+
120
+ Growth is scale independent and neglects
121
+ radiation, using a Diffrax ODE initialized with $D = a$ at $a = 0.001$ and checked
122
+ through $z = 10$; background distances retain radiation. Massive neutrinos are
123
+ unsupported, and constructing with `nonlinear=True` raises
124
+ `NotImplementedError`.
125
+
126
+ The reusable growth solver accepts any differentiable `Background`:
127
+
128
+ ```python
129
+ from cosmograd.perturbations import linear_growth
130
+
131
+ # Matter-era initial conditions require negligible radiation at a_initial.
132
+ growth_background = FlatLCDM(H0=67.36, omega_m=0.3153, t_cmb=0.0)
133
+ D, f = linear_growth(growth_background, z) # D(0) = 1, f = d ln D / d ln a
134
+ D_md, f = linear_growth(growth_background, z, normalize=False) # initial D = a
135
+ ```
136
+
137
+ The solver uses the supplied $H(z)$ and $\Omega_m(z)$, supporting curvature and smooth
138
+ dark energy within GR and scale-independent, subhorizon growth. Set
139
+ `a_initial` inside matter domination and check sensitivity to this choice.
140
+ The default of 0.001 ($z = 999$) is appropriate only for radiation-free
141
+ backgrounds (`t_cmb=0`); with radiation, $D$ and $f$ shift by roughly $10^{-4}$ to
142
+ $10^{-3}$ at the default, which `rtol` does not control. Such backgrounds need a larger
143
+ `a_initial`, late enough for radiation to be negligible but early enough that
144
+ dark energy and curvature still are. Redshifts must lie between 0 and
145
+ `1/a_initial - 1`, outside which the result is NaN, and may have any shape or
146
+ order. Solver failures, such as exceeding `max_steps`, raise an error. Both
147
+ forward- and
148
+ reverse-mode parameter derivatives are supported. EH keeps its `FlatLCDM`
149
+ transfer-function inputs and supplies a radiation-free expansion to this solver.
150
+
151
+ With the `bacco` extra, `BaccoEmu` emulates linear and nonlinear matter power
152
+ spectra. Weights are downloaded on first use into
153
+ the user cache directory (`~/.cache/cosmograd/baccoemu` on Linux), checked against
154
+ pinned SHA-256 digests and symlinked into `baccoemu`'s install directory, so
155
+ they survive rebuilding the environment. Select the spectrum with the
156
+ constructor's `nonlinear` option, which defaults to `False` for every backend:
157
+
158
+ ```python
159
+ from cosmograd.perturbations.baccoemu import BaccoEmu
160
+
161
+ pert = BaccoEmu(Planck18, nonlinear=True)
162
+ k = jnp.geomspace(1e-3, 3.0, 100) # 1/Mpc
163
+ z = jnp.linspace(0.0, 1.5, 16)
164
+ pert.check_bounds(k, z) # eager check of the training ranges
165
+ pert.matter_power_spectrum(k, z[:, None]) # Mpc^3, shape (16, 100)
166
+ pert.linear_matter_power_spectrum(k, z[:, None]) # linear, whatever `nonlinear` is
167
+ ```
168
+
169
+ `BaccoEmu` also takes `neutrino_mass` (the cosmology's `omega_m` is then the
170
+ total matter density, $\Omega_{cb} + \Omega_\nu$) and `sigma8_cold`, which sets the amplitude
171
+ in place of the cosmology's `ln10as`. With the `cosmopower` extra,
172
+ `CosmoPower(Planck18, c_min=3.13, eta_0=0.603, nonlinear=True)` works the same
173
+ way, with the HMcode feedback parameters as its own differentiable fields.
174
+
175
+ Units: distances in Mpc, wavenumbers in $\mathrm{Mpc}^{-1}$, $H$ in $\mathrm{Mpc}^{-1}$
176
+ ($c = 1$), power spectra in $\mathrm{Mpc}^3$. Inputs are not validated; each method documents the range it expects.
177
+
178
+ ## Calculation references
179
+
180
+ The `EisensteinHu` calculations follow:
181
+
182
+ - [Eisenstein & Hu (1998), *Baryonic Features in the Matter Transfer Function*](https://arxiv.org/abs/astro-ph/9709112),
183
+ equations (2)–(24): equality and drag scales, CDM and baryon contributions,
184
+ acoustic oscillations and Silk damping.
185
+ - [Heath (1977)](https://doi.org/10.1093/mnras/179.3.351) and
186
+ [Hamilton (2001), *Formulae for growth factors*](https://arxiv.org/abs/astro-ph/0006089),
187
+ equations (1)–(2): the growing-mode integral for matter plus $\Lambda$, used as an
188
+ independent reference in the ODE tests. The Diffrax solver integrates the
189
+ equivalent Sturm–Liouville form $(a^2 E D')' = \tfrac{3}{2} a^2 E \Omega_m D$ in
190
+ $\ln a$, using the variables described in the `linear_growth` notes, so $H$ is
191
+ never differentiated. Matter-era growth starts with $D_\mathrm{md} = a$ and
192
+ $D_\mathrm{md}' = a$; the public growth function is
193
+ $D(z) = D_\mathrm{md}(z) / D_\mathrm{md}(0)$.
194
+ - [Smith & Angulo (2019), *Precision modelling of the matter power spectrum in a Planck-like Universe*](https://arxiv.org/abs/1807.00040),
195
+ equations (8) and (10): the primordial spectrum (with zero running) and its
196
+ conversion to matter power. Their $a g$ corresponds to $D_\mathrm{md}$ here; using
197
+ today-normalised $D$ instead would change the absolute power normalisation.
198
+ - [Planck Collaboration (2020), *Planck 2018 results. VI. Cosmological parameters*](https://arxiv.org/abs/1807.06209),
199
+ section 2.1: the pivot $k = 0.05\,\mathrm{Mpc}^{-1}$ and $\ln(10^{10} A_s)$ parameter
200
+ convention.
201
+
202
+ Equation references also appear beside the corresponding calculations in
203
+ `src/cosmograd/perturbations/eisenstein_hu.py`.
204
+
205
+ ## Setup
206
+
207
+ [Install uv](https://docs.astral.sh/uv/getting-started/installation/) and
208
+ [Just](https://just.systems/man/en/packages.html), then run:
209
+
210
+ ```console
211
+ just setup
212
+ uv run prek install
213
+ ```
214
+
215
+ The underlying command is `uv sync --all-groups` if Just is unavailable.
216
+
217
+ ## Development
218
+
219
+ ### Spectrum comparison notebook
220
+
221
+ Install the notebook dependencies with `uv sync --group notebook`, then open
222
+ [`notebooks/perturbation_benchmark.py`](notebooks/perturbation_benchmark.py) in a
223
+ notebook editor that supports Jupytext `py:percent` scripts (for example, VS Code's
224
+ Python Interactive window). Select the repository's `.venv` kernel and run all
225
+ cells. The first code cell contains imports; the second holds all editable
226
+ settings and enables Retina figures. First use may download emulator weights
227
+ and requires internet access.
228
+ The notebook compares linear and nonlinear spectra against CAMB at a shared
229
+ primordial amplitude; it does not benchmark runtime.
230
+
231
+ Only the percent script is maintained. To execute a temporary notebook from the
232
+ repository root:
233
+
234
+ ```console
235
+ uv run --group notebook jupytext --to ipynb notebooks/perturbation_benchmark.py -o /tmp/perturbation_benchmark.ipynb
236
+ uv run --group notebook jupyter nbconvert --to notebook --execute /tmp/perturbation_benchmark.ipynb --output /tmp/perturbation_benchmark.executed.ipynb --ExecutePreprocessor.timeout=600
237
+ ```
238
+
239
+ ### Repository checks
240
+
241
+ ```console
242
+ just fmt # uv run ruff check --fix . && uv run ruff format .
243
+ just lint # uv run ruff check .
244
+ just typecheck # uv run ty check
245
+ just test # uv run pytest
246
+ just hooks # uv run prek run --all-files
247
+ just check # full validation, tests, and package build
248
+ ```
249
+
250
+ Tests check shape and dtype annotations through jaxtyping’s pytest import hook
251
+ and beartype. Normal library imports do not enable runtime checking or require
252
+ beartype. Shape annotations document conventions; static type checking does not
253
+ verify array shapes.
@@ -0,0 +1,224 @@
1
+ # cosmograd
2
+
3
+ Differentiable cosmology in JAX behind a small protocol contract.
4
+
5
+ Consumers depend on two protocols, `Background` (expansion history and
6
+ distances) and `Perturbations` (growth and power spectra), not on any one
7
+ implementation. The core depends on JAX, jaxtyping and Diffrax; emulator
8
+ backends sit behind optional extras.
9
+
10
+ ## Install
11
+
12
+ ```console
13
+ uv add cosmograd # core: protocols + native backgrounds and linear power
14
+ uv add 'cosmograd[cosmopower]' # CosmoPower-JAX backend
15
+ uv add 'cosmograd[bacco]' # bacco emulator backend
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```python
21
+ import jax
22
+ import jax.numpy as jnp
23
+ from cosmograd import FlatLCDM
24
+
25
+ cosmo = FlatLCDM(H0=67.36, omega_m=0.3153)
26
+ z = jnp.linspace(0.1, 2.0, 20)
27
+
28
+ cosmo.luminosity_distance(z) # Mpc
29
+ jax.jacfwd(lambda c: c.luminosity_distance(z))(cosmo) # FlatLCDM-shaped Jacobian
30
+ ```
31
+
32
+ `FlatW0WaCDM` adds CPL dark energy, $w(a) = w_0 + w_a (1 - a)$, with `w0` and `wa`
33
+ as extra differentiable parameters:
34
+
35
+ ```python
36
+ from cosmograd import FlatW0WaCDM
37
+
38
+ cosmo = FlatW0WaCDM(H0=67.36, omega_m=0.3153, w0=-0.9, wa=0.2)
39
+ jax.grad(lambda c: c.comoving_distance(1.0))(cosmo).w0
40
+ ```
41
+
42
+ `W0WaCDM` is the general background, adding a curvature parameter `omega_k`;
43
+ `LCDM`, `FlatW0WaCDM` and `FlatLCDM` are the same model with `omega_k`, `w0`
44
+ and `wa` fixed, so they are leaves of the pytree only for the parameters they
45
+ leave free. Curvature enters the expansion rate and the transverse distances
46
+ (`luminosity_distance`, `angular_diameter_distance`); the inverse of
47
+ `comoving_distance` is the line-of-sight one.
48
+
49
+ ```python
50
+ from cosmograd import W0WaCDM
51
+
52
+ cosmo = W0WaCDM(H0=67.36, omega_m=0.3153, omega_k=0.02)
53
+ cosmo.luminosity_distance(z)
54
+ ```
55
+
56
+ Every class also takes `omega_b`, `n_s` and `ln10as`, so that one object holds
57
+ the whole cosmology. No background method reads them. They default to the
58
+ Planck 2018 values (0.04897, 0.9665 and 3.047), which the perturbation backends
59
+ then use without warning, so set them whenever you compute spectra. Standard
60
+ cosmologies carry their own:
61
+
62
+ ```python
63
+ from cosmograd.cosmologies import Planck18
64
+
65
+ Planck18.luminosity_distance(z)
66
+ Planck18.with_parameters(omega_m=0.30) # a modified copy
67
+ ```
68
+
69
+ Here `omega_b` is $\Omega_b$ today, with `0 < omega_b < omega_m`, and `ln10as` is
70
+ $\ln(10^{10} A_s)$ at the pivot $0.05\,\mathrm{Mpc}^{-1}$.
71
+
72
+ The perturbation backends below take such a cosmology plus their own settings.
73
+ They assume a flat universe: `EisensteinHu` and `BaccoEmu` accept either flat class;
74
+ `CosmoPower` is trained on $\Lambda\mathrm{CDM}$ and takes a `FlatLCDM` only.
75
+
76
+ The native `EisensteinHu` backend provides linear matter power with EH98 baryon
77
+ acoustic oscillations, normalised by the primordial amplitude:
78
+
79
+ ```python
80
+ from cosmograd import EisensteinHu
81
+
82
+ pert = EisensteinHu(Planck18)
83
+ k = jnp.geomspace(1e-4, 1.0, 100) # 1/Mpc
84
+ pert.background is Planck18 # True
85
+ pert.matter_power_spectrum(k, z[:, None]) # Mpc^3, shape (20, 100)
86
+ pert.growth_function(z) # normalised to 1 at z = 0
87
+ # One FlatLCDM-shaped gradient over H0, omega_m, omega_b, n_s and ln10as.
88
+ jax.grad(lambda c: EisensteinHu(c).matter_power_spectrum(0.1, 0.5))(Planck18)
89
+ ```
90
+
91
+ Growth is scale independent and neglects
92
+ radiation, using a Diffrax ODE initialized with $D = a$ at $a = 0.001$ and checked
93
+ through $z = 10$; background distances retain radiation. Massive neutrinos are
94
+ unsupported, and constructing with `nonlinear=True` raises
95
+ `NotImplementedError`.
96
+
97
+ The reusable growth solver accepts any differentiable `Background`:
98
+
99
+ ```python
100
+ from cosmograd.perturbations import linear_growth
101
+
102
+ # Matter-era initial conditions require negligible radiation at a_initial.
103
+ growth_background = FlatLCDM(H0=67.36, omega_m=0.3153, t_cmb=0.0)
104
+ D, f = linear_growth(growth_background, z) # D(0) = 1, f = d ln D / d ln a
105
+ D_md, f = linear_growth(growth_background, z, normalize=False) # initial D = a
106
+ ```
107
+
108
+ The solver uses the supplied $H(z)$ and $\Omega_m(z)$, supporting curvature and smooth
109
+ dark energy within GR and scale-independent, subhorizon growth. Set
110
+ `a_initial` inside matter domination and check sensitivity to this choice.
111
+ The default of 0.001 ($z = 999$) is appropriate only for radiation-free
112
+ backgrounds (`t_cmb=0`); with radiation, $D$ and $f$ shift by roughly $10^{-4}$ to
113
+ $10^{-3}$ at the default, which `rtol` does not control. Such backgrounds need a larger
114
+ `a_initial`, late enough for radiation to be negligible but early enough that
115
+ dark energy and curvature still are. Redshifts must lie between 0 and
116
+ `1/a_initial - 1`, outside which the result is NaN, and may have any shape or
117
+ order. Solver failures, such as exceeding `max_steps`, raise an error. Both
118
+ forward- and
119
+ reverse-mode parameter derivatives are supported. EH keeps its `FlatLCDM`
120
+ transfer-function inputs and supplies a radiation-free expansion to this solver.
121
+
122
+ With the `bacco` extra, `BaccoEmu` emulates linear and nonlinear matter power
123
+ spectra. Weights are downloaded on first use into
124
+ the user cache directory (`~/.cache/cosmograd/baccoemu` on Linux), checked against
125
+ pinned SHA-256 digests and symlinked into `baccoemu`'s install directory, so
126
+ they survive rebuilding the environment. Select the spectrum with the
127
+ constructor's `nonlinear` option, which defaults to `False` for every backend:
128
+
129
+ ```python
130
+ from cosmograd.perturbations.baccoemu import BaccoEmu
131
+
132
+ pert = BaccoEmu(Planck18, nonlinear=True)
133
+ k = jnp.geomspace(1e-3, 3.0, 100) # 1/Mpc
134
+ z = jnp.linspace(0.0, 1.5, 16)
135
+ pert.check_bounds(k, z) # eager check of the training ranges
136
+ pert.matter_power_spectrum(k, z[:, None]) # Mpc^3, shape (16, 100)
137
+ pert.linear_matter_power_spectrum(k, z[:, None]) # linear, whatever `nonlinear` is
138
+ ```
139
+
140
+ `BaccoEmu` also takes `neutrino_mass` (the cosmology's `omega_m` is then the
141
+ total matter density, $\Omega_{cb} + \Omega_\nu$) and `sigma8_cold`, which sets the amplitude
142
+ in place of the cosmology's `ln10as`. With the `cosmopower` extra,
143
+ `CosmoPower(Planck18, c_min=3.13, eta_0=0.603, nonlinear=True)` works the same
144
+ way, with the HMcode feedback parameters as its own differentiable fields.
145
+
146
+ Units: distances in Mpc, wavenumbers in $\mathrm{Mpc}^{-1}$, $H$ in $\mathrm{Mpc}^{-1}$
147
+ ($c = 1$), power spectra in $\mathrm{Mpc}^3$. Inputs are not validated; each method documents the range it expects.
148
+
149
+ ## Calculation references
150
+
151
+ The `EisensteinHu` calculations follow:
152
+
153
+ - [Eisenstein & Hu (1998), *Baryonic Features in the Matter Transfer Function*](https://arxiv.org/abs/astro-ph/9709112),
154
+ equations (2)–(24): equality and drag scales, CDM and baryon contributions,
155
+ acoustic oscillations and Silk damping.
156
+ - [Heath (1977)](https://doi.org/10.1093/mnras/179.3.351) and
157
+ [Hamilton (2001), *Formulae for growth factors*](https://arxiv.org/abs/astro-ph/0006089),
158
+ equations (1)–(2): the growing-mode integral for matter plus $\Lambda$, used as an
159
+ independent reference in the ODE tests. The Diffrax solver integrates the
160
+ equivalent Sturm–Liouville form $(a^2 E D')' = \tfrac{3}{2} a^2 E \Omega_m D$ in
161
+ $\ln a$, using the variables described in the `linear_growth` notes, so $H$ is
162
+ never differentiated. Matter-era growth starts with $D_\mathrm{md} = a$ and
163
+ $D_\mathrm{md}' = a$; the public growth function is
164
+ $D(z) = D_\mathrm{md}(z) / D_\mathrm{md}(0)$.
165
+ - [Smith & Angulo (2019), *Precision modelling of the matter power spectrum in a Planck-like Universe*](https://arxiv.org/abs/1807.00040),
166
+ equations (8) and (10): the primordial spectrum (with zero running) and its
167
+ conversion to matter power. Their $a g$ corresponds to $D_\mathrm{md}$ here; using
168
+ today-normalised $D$ instead would change the absolute power normalisation.
169
+ - [Planck Collaboration (2020), *Planck 2018 results. VI. Cosmological parameters*](https://arxiv.org/abs/1807.06209),
170
+ section 2.1: the pivot $k = 0.05\,\mathrm{Mpc}^{-1}$ and $\ln(10^{10} A_s)$ parameter
171
+ convention.
172
+
173
+ Equation references also appear beside the corresponding calculations in
174
+ `src/cosmograd/perturbations/eisenstein_hu.py`.
175
+
176
+ ## Setup
177
+
178
+ [Install uv](https://docs.astral.sh/uv/getting-started/installation/) and
179
+ [Just](https://just.systems/man/en/packages.html), then run:
180
+
181
+ ```console
182
+ just setup
183
+ uv run prek install
184
+ ```
185
+
186
+ The underlying command is `uv sync --all-groups` if Just is unavailable.
187
+
188
+ ## Development
189
+
190
+ ### Spectrum comparison notebook
191
+
192
+ Install the notebook dependencies with `uv sync --group notebook`, then open
193
+ [`notebooks/perturbation_benchmark.py`](notebooks/perturbation_benchmark.py) in a
194
+ notebook editor that supports Jupytext `py:percent` scripts (for example, VS Code's
195
+ Python Interactive window). Select the repository's `.venv` kernel and run all
196
+ cells. The first code cell contains imports; the second holds all editable
197
+ settings and enables Retina figures. First use may download emulator weights
198
+ and requires internet access.
199
+ The notebook compares linear and nonlinear spectra against CAMB at a shared
200
+ primordial amplitude; it does not benchmark runtime.
201
+
202
+ Only the percent script is maintained. To execute a temporary notebook from the
203
+ repository root:
204
+
205
+ ```console
206
+ uv run --group notebook jupytext --to ipynb notebooks/perturbation_benchmark.py -o /tmp/perturbation_benchmark.ipynb
207
+ uv run --group notebook jupyter nbconvert --to notebook --execute /tmp/perturbation_benchmark.ipynb --output /tmp/perturbation_benchmark.executed.ipynb --ExecutePreprocessor.timeout=600
208
+ ```
209
+
210
+ ### Repository checks
211
+
212
+ ```console
213
+ just fmt # uv run ruff check --fix . && uv run ruff format .
214
+ just lint # uv run ruff check .
215
+ just typecheck # uv run ty check
216
+ just test # uv run pytest
217
+ just hooks # uv run prek run --all-files
218
+ just check # full validation, tests, and package build
219
+ ```
220
+
221
+ Tests check shape and dtype annotations through jaxtyping’s pytest import hook
222
+ and beartype. Normal library imports do not enable runtime checking or require
223
+ beartype. Shape annotations document conventions; static type checking does not
224
+ verify array shapes.
@@ -0,0 +1,99 @@
1
+ [project]
2
+ name = "cosmograd"
3
+ version = "0.1.0"
4
+ description = "Differentiable cosmology in JAX behind a small protocol contract."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.12"
9
+ dependencies = [
10
+ "diffrax>=0.7.2",
11
+ "jax>=0.10",
12
+ "jaxtyping>=0.3.11",
13
+ "numpy>=2.5.4",
14
+ ]
15
+ classifiers = [
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Typing :: Typed",
22
+ ]
23
+
24
+ [[project.authors]]
25
+ name = "binado"
26
+ email = "bernardopveronese@gmail.com"
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/binado/cosmograd"
30
+ Repository = "https://github.com/binado/cosmograd.git"
31
+ Issues = "https://github.com/binado/cosmograd/issues"
32
+
33
+ [project.optional-dependencies]
34
+ cosmopower = ["cosmopower-jax>=0.5.8"]
35
+ bacco = [
36
+ "baccoemu>=2.3.0",
37
+ "platformdirs>=4.0",
38
+ ]
39
+
40
+ [build-system]
41
+ requires = ["uv_build>=0.12.18,<0.14"]
42
+ build-backend = "uv_build"
43
+
44
+ [dependency-groups]
45
+ notebook = [
46
+ "cosmograd[bacco,cosmopower]",
47
+ "camb>=1.6.5",
48
+ "matplotlib>=3.10",
49
+ "ipykernel>=6.30",
50
+ "jupytext>=1.17",
51
+ "nbconvert>=7.16",
52
+ ]
53
+ ref = [
54
+ "camb>=1.6.5",
55
+ "cloelib>=0.11",
56
+ "jax-cosmo>=0.1.0",
57
+ "setuptools<81",
58
+ ]
59
+ dev = [
60
+ "cosmograd[bacco,cosmopower]",
61
+ "prek>=0.5.3",
62
+ "ruff>=0.16.0",
63
+ "ty>=0.0.83",
64
+ { include-group = "test" },
65
+ { include-group = "ref" },
66
+ ]
67
+ test = [
68
+ "beartype>=0.23.1",
69
+ "pytest>=9.1.1",
70
+ "pytest-xdist>=3.8.0",
71
+ ]
72
+
73
+ [tool.pytest.ini_options]
74
+ addopts = "-ra --jaxtyping-packages=cosmograd,tests.typecheck"
75
+ markers = ["slow: comparisons against Boltzmann codes or emulators, and the Fisher-matrix test (deselect with -m 'not slow')"]
76
+ testpaths = ["tests"]
77
+
78
+ [tool.ruff]
79
+ target-version = "py312"
80
+
81
+ [tool.ruff.lint]
82
+ select = [
83
+ "E4",
84
+ "E7",
85
+ "E9",
86
+ "F",
87
+ "I",
88
+ "UP",
89
+ ]
90
+ ignore = ["F722"]
91
+
92
+ [tool.ty.src]
93
+ include = [
94
+ "src",
95
+ "tests",
96
+ ]
97
+
98
+ [tool.uv.sources.cosmograd]
99
+ workspace = true
@@ -0,0 +1,98 @@
1
+ [project]
2
+ name = "cosmograd"
3
+ version = "0.1.0"
4
+ description = "Differentiable cosmology in JAX behind a small protocol contract."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [
9
+ { name = "binado", email = "bernardopveronese@gmail.com" },
10
+ ]
11
+ requires-python = ">=3.12"
12
+ dependencies = [
13
+ "diffrax>=0.7.2",
14
+ "jax>=0.10",
15
+ "jaxtyping>=0.3.11",
16
+ "numpy>=2.5.4",
17
+ ]
18
+ classifiers = [
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+
22
+
23
+ "Programming Language :: Python :: 3.12",
24
+
25
+
26
+ "Programming Language :: Python :: 3.13",
27
+
28
+ "Programming Language :: Python :: 3.14",
29
+ "Typing :: Typed",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/binado/cosmograd"
34
+ Repository = "https://github.com/binado/cosmograd.git"
35
+ Issues = "https://github.com/binado/cosmograd/issues"
36
+
37
+ [project.optional-dependencies]
38
+ cosmopower = [
39
+ "cosmopower-jax>=0.5.8",
40
+ ]
41
+ bacco = [
42
+ "baccoemu>=2.3.0",
43
+ "platformdirs>=4.0",
44
+ ]
45
+
46
+
47
+ [build-system]
48
+ requires = ["uv_build>=0.12.18,<0.14"]
49
+ build-backend = "uv_build"
50
+
51
+ [dependency-groups]
52
+ notebook = [
53
+ "cosmograd[bacco,cosmopower]",
54
+ "camb>=1.6.5",
55
+ "matplotlib>=3.10",
56
+ "ipykernel>=6.30",
57
+ "jupytext>=1.17",
58
+ "nbconvert>=7.16",
59
+ ]
60
+ ref = [
61
+ "camb>=1.6.5",
62
+ "cloelib>=0.11",
63
+ "jax-cosmo>=0.1.0",
64
+ # jax-cosmo imports pkg_resources, which setuptools 82 removed.
65
+ "setuptools<81",
66
+ ]
67
+ dev = [
68
+ "cosmograd[bacco,cosmopower]",
69
+ "prek>=0.5.3",
70
+ "ruff>=0.16.0",
71
+ "ty>=0.0.83",
72
+ { include-group = "test" },
73
+ { include-group = "ref" },
74
+ ]
75
+ test = [
76
+ "beartype>=0.23.1",
77
+ "pytest>=9.1.1",
78
+ "pytest-xdist>=3.8.0",
79
+ ]
80
+
81
+ [tool.pytest.ini_options]
82
+ addopts = "-ra --jaxtyping-packages=cosmograd,tests.typecheck"
83
+ markers = ["slow: comparisons against Boltzmann codes or emulators, and the Fisher-matrix test (deselect with -m 'not slow')"]
84
+ testpaths = ["tests"]
85
+
86
+ [tool.ruff]
87
+ target-version = "py312"
88
+
89
+ [tool.ruff.lint]
90
+ select = ["E4", "E7", "E9", "F", "I", "UP"]
91
+ # Jaxtyping shape strings are not Python forward annotations.
92
+ ignore = ["F722"]
93
+
94
+ [tool.ty.src]
95
+ include = ["src", "tests"]
96
+
97
+ [tool.uv.sources]
98
+ cosmograd = { workspace = true }