Source code for cosmicjellyfish.cosmology.jax_theory

r"""Backend-neutral, differentiable theory interface for JAX observables.

CosmicJellyfish separates the *theory* backend (which produces distances, growth and matter
power spectra) from the *observables* (which turn those quantities into angular power spectra
or cluster counts).  The same separation is useful for the JAX/differentiable path: a JAX
observable should not know which theory backend produced its inputs.

This module defines that small contract:

* :class:`TheoryTables` is the bundle of quantities an observable needs, always in
  CosmicJellyfish units (``chi`` in Mpc, ``h_over_c`` in 1/Mpc, ``P`` in Mpc^3);
* :class:`JaxTheory` is the protocol a differentiable backend implements;
* :func:`get_jax_theory` resolves the backend selected in ``options["code"]``.

Only backends whose computations are written with ``jax.numpy`` can provide exact automatic
derivatives.  A NumPy/SciPy backend can still drive the plain CosmicJellyfish observables through
finite differences, but it cannot be used here.

Nothing in this module imports JAX at module level, so it stays importable without the optional
``jaxcosmo`` extra.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Mapping, Protocol, runtime_checkable


[docs] @dataclass class TheoryTables: """Differentiable theory quantities on a redshift/wavenumber grid. Attributes ---------- z : array, shape (nz,) Redshifts the quantities are evaluated at. k_hmpc : array, shape (nk,) Wavenumbers in ``h/Mpc``; the power spectra' second axis follows this order. chi : array, shape (nz,) Comoving distance in Mpc. h_over_c : array, shape (nz,) Hubble rate divided by the speed of light, in ``1/Mpc``. growth : array, shape (nz,) Linear growth factor normalised to ``D(0) = 1``. growth_rate : array, shape (nz,) Logarithmic growth rate ``f = d ln D / d ln a`` (scale independent). omega_m : array, shape (nz,) Matter fraction ``Omega_m(z)``. pk_linear, pk_nonlinear : array, shape (nz, nk) Matter power spectra in ``Mpc^3``. h0_over_c : scalar Present-day ``H0 / c`` in ``1/Mpc``. omega_m0 : scalar Present-day matter fraction. sigma8 : array, shape (nz,), optional Present-day ``sigma8`` times the linear growth factor, i.e. ``sigma8(z)``. """ z: Any k_hmpc: Any chi: Any h_over_c: Any growth: Any growth_rate: Any omega_m: Any pk_linear: Any pk_nonlinear: Any h0_over_c: Any omega_m0: Any sigma8: Any = None
[docs] @runtime_checkable class JaxTheory(Protocol): """A theory backend that can be traced and differentiated by JAX. Implementations must be *pure functions* of the parameters: no NumPy conversion and no SciPy splines on the path between parameters and returned arrays. """ #: Canonical cosmological parameter names expected in the ``params`` mapping. cosmo_names: tuple[str, ...] #: Mapping from CosmicJellyfish parameter names to this backend's canonical names. free_name_map: Mapping[str, str]
[docs] def resolve_cosmology(self, params: Mapping[str, Any]) -> dict[str, float]: """Validate a CosmicJellyfish cosmology mapping and return canonical float values."""
[docs] def tables( self, params: Mapping[str, Any], z: Any, k_hmpc: Any, nonlinear: bool = True, ) -> TheoryTables: """Evaluate :class:`TheoryTables` at ``z`` and ``k_hmpc`` for ``params``."""
[docs] def power( self, params: Mapping[str, Any], z: Any, k_hmpc: Any, nonlinear: bool = True, ) -> Any: """Matter power in ``Mpc^3`` at per-redshift wavenumbers ``k_hmpc`` (``h/Mpc``). ``z`` has shape ``(nz,)``. ``k_hmpc`` may have shape ``(nk,)`` (the same wavenumbers for every redshift) or ``(nz, nk)`` to request a different wavenumber per redshift, which is what a Limber projection needs. The return value has shape ``(nz, nk)``. """
[docs] def get_jax_theory( code: str | None, backend_parameters: Mapping[str, Any] | None = None, cosmo_model: str = "LCDM", ) -> JaxTheory: """Return the differentiable theory implementation selected by ``options["code"]``. Parameters ---------- code : str Value of ``options["code"]``. Currently only ``"jaxcosmo"`` is available. backend_parameters : mapping, optional Parsed ``default_boltzmann_yaml_files/<code>/default.yaml``. cosmo_model : str ``"LCDM"`` or ``"w0waCDM"``; fixed-w models are rejected by the backend. Raises ------ NotImplementedError If the selected backend has no differentiable implementation. """ if code == "jaxcosmo": from cosmicjellyfish.cosmology.jaxcosmo_theory import JaxCosmoTheory return JaxCosmoTheory(backend_parameters, cosmo_model=cosmo_model) raise NotImplementedError( f"The backend '{code}' does not provide a differentiable JAX theory implementation. " "Set options['code'] = 'jaxcosmo' to use the autodiff path." )