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.
- cosmograd-0.1.0/LICENSE +21 -0
- cosmograd-0.1.0/PKG-INFO +253 -0
- cosmograd-0.1.0/README.md +224 -0
- cosmograd-0.1.0/pyproject.toml +99 -0
- cosmograd-0.1.0/pyproject.toml.orig +98 -0
- cosmograd-0.1.0/src/cosmograd/__init__.py +31 -0
- cosmograd-0.1.0/src/cosmograd/background/__init__.py +9 -0
- cosmograd-0.1.0/src/cosmograd/background/_base.py +322 -0
- cosmograd-0.1.0/src/cosmograd/background/flat_lambdacdm.py +131 -0
- cosmograd-0.1.0/src/cosmograd/background/flat_w0wa_cdm.py +119 -0
- cosmograd-0.1.0/src/cosmograd/background/lambdacdm.py +109 -0
- cosmograd-0.1.0/src/cosmograd/background/protocol.py +201 -0
- cosmograd-0.1.0/src/cosmograd/background/w0wa_cdm.py +139 -0
- cosmograd-0.1.0/src/cosmograd/constants.py +22 -0
- cosmograd-0.1.0/src/cosmograd/cosmologies.py +62 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/__init__.py +10 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/__init__.py +10 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_cache.py +200 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_cosmology.py +389 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_emulator.py +212 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/baccoemu/_power.py +216 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/cosmopower/__init__.py +360 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/eisenstein_hu.py +318 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/growth.py +167 -0
- cosmograd-0.1.0/src/cosmograd/perturbations/protocol.py +169 -0
- cosmograd-0.1.0/src/cosmograd/py.typed +0 -0
- cosmograd-0.1.0/src/cosmograd/utils.py +63 -0
cosmograd-0.1.0/LICENSE
ADDED
|
@@ -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.
|
cosmograd-0.1.0/PKG-INFO
ADDED
|
@@ -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 }
|