refactor: vmap-safe transfer + nonlinear; drop _halofit_modulator inline duplication - #15
Open
MinhMPA wants to merge 6 commits into
Open
refactor: vmap-safe transfer + nonlinear; drop _halofit_modulator inline duplication#15MinhMPA wants to merge 6 commits into
MinhMPA wants to merge 6 commits into
Conversation
…nlinear=...) — redo of smsharma#11 with bug fixes Supersedes PR smsharma#11 (which was reverted via PR smsharma#13 due to post-merge test failures). This PR ships the same API consolidation with the three fixes that smsharma#11 was missing folded in from the start. Public API: compute_cl_pp(pt, params, bg, th, l_max, *, nonlinear="none") # nonlinear in {"none", "halofit"}; ValueError for anything else. # "ept" is reserved for a follow-up clax-pt PR. Removed (BREAKING): - compute_cl_pp (Siddharth's original; superseded by source-Limber) - compute_cl_pp_fast (inaccurate at l >= 300 per its own docstring) - compute_cl_pp_vmap (Hermite Bessel-table vmap; superseded) - compute_cl_pp_limber (Poisson-reconstruction; ~20% over at l=2500) Renamed: - compute_cl_pp_source_limber -> compute_cl_pp (sole public entry) - compute_cl_pp_transfer -> _compute_cl_pp_full_bessel (private oracle) Halofit injection (new private _halofit_modulator): - vmap(compute_pk_nonlinear) over a 100-point z-grid (CLASS-aligned density). - Inline `sigma_R(R_min, ...) >= 1` check + jnp.where to force R=1 where Halofit isn't applicable — replicates CLASS fourier.c:1706-1716 in a vmap-safe way (the Python-level check inside compute_pk_nonlinear gets bypassed under vmap-over-z, which is why smsharma#11 over-corrected ~25% at high l). - k_max_extend = 0 default (no power-law extension; matches CLASS's no-extrapolation behavior). Pass a positive value to enable log-log extension for narrow k-grids. - 2D-interpolates R(k, z) onto (pt.k_grid, pt.tau_grid) via bg.loga_of_tau. - Multiplies sqrt(R) directly into S_transfer (CLASS source-multiplication). Tests: - tests/test_cl_pp.py (renamed from test_cl_pp_source_limber.py): contract + linear-accuracy + cross-impl-vs-_compute_cl_pp_full_bessel + Halofit smoke + JIT/AD. - tests/test_clpp_halofit_ratio.py: rewritten for new API; <7% NL/lin ratio agreement at l<=500, <10% at l>=1000 vs CLASS Halofit reference. - tests/test_lensing.py: callers updated; uses local `pipeline` fixture matching upstream-main convention. - Deleted: tests/test_cl_pp_implementations.py, test_cl_pp_limber.py. Verified locally: 32/32 lensing+halofit tests pass, 36/36 EPT tests pass, 104/104 remaining tests pass.
…ute_cl_pp Documents both paths in the new unified API: - nonlinear="none" (linear source-Limber): <1% vs CLASS at all l<=2500 - nonlinear="halofit" (z-aware Halofit injection via source multiplication): <7% at l=500 and <10% at l>=1000 vs CLASS Halofit reference Existing tables (unlensed C_l, lensed C_l with CLASS-generated cl_pp, matter P(k), pipeline, performance, multi-cosmology) are unaffected by PR smsharma#14 — they don't depend on compute_cl_pp. Note on lensed C_l accuracy table: it tests lens_cls in isolation by feeding CLASS-generated C_l^pp; the reported <0.2% is about lens_cls's correlation-function method, not about our compute_cl_pp.
…<=2500
The previous table understated linear accuracy. test_clpp_halofit_ratio.py
::TestClppLinear asserts <1% at l in {100, 500, 1000, 2000, 2500} with
pt_k_max_cl=5.0. Updated the linear column accordingly and added the
prerequisite (pt_k_max_cl >= 5) to the section preamble.
…ratio test thresholds Sweep across modulator settings on default cosmology (l_max=2500): | l | n_z=100, no extension | n_z=100, extend to k=10 | | 100 | 0.01% | 0.01% | | 200 | 0.04% | 0.04% | | 500 | 0.42% | 0.21% | | 1000 | 1.47% | 0.63% | | 1500 | 2.30% | 0.79% | | 2000 | 3.74% | 1.40% | | 2500 | 3.97% | 0.96% | The k-extension restores accuracy at high l. CLASS itself uses a dedicated nonlinear k-grid that extends past the perturbation k-range; our log-log power-law extension provides equivalent coverage with σ(R) bisection well-resolved. Doubling n_z to 200 didn't help (slight degradation at l=1000, l=2500 from interpolation noise), so 100 is the right z-density. Changes: - _halofit_modulator default k_max_extend: 0 → 10 Mpc^-1 - test_ratio_at_low_l threshold: 7% → 1% (measured: 0.21% at l=500) - test_ratio_at_high_l threshold: 10% → 2% (measured: 1.40% worst case) - README accuracy table: replace bounds with measured residuals - Docstrings updated with rationale and measured numbers User asked 'who set the 10% threshold' — it was carried over from the deleted Poisson-Limber test, never measured against the actual source- multiplication recipe. Now it reflects what the code actually achieves.
…=2500); fair absolute-accuracy table k_max_extend sweep on default cosmology, n_z=100 fixed: | l | k=10 | k=20 | k=30 | | 500 | 0.21% | 0.09% | 0.09% | | 1000 | 0.63% | 0.04% | 0.17% | | 1500 | 0.79% | 0.11% | 0.11% | | 2000 | 1.40% | 0.05% | 0.28% | | 2500 | 0.96% | 0.76% | 0.76% | (metric: |R_clax / R_CLASS - 1|, R = NL/lin) k=20 is the sweet spot — far enough for sigma(R) bisection to fully converge, not so far that the local power-law slope drifts from the true asymptote. Going to k=30+ degrades because power-law extrapolation diverges from the true transfer-function shape. Timing benchmark (5-run mean): k=10 110ms, k=20 110ms, k=30 111ms. Accuracy boost is essentially free. Test thresholds: - low_l (l<=500): 1% -> 0.5% - high_l (l>=1000): 2% -> 1% README accuracy table replaced with apples-to-apples absolute residuals for both 'none' and 'halofit' (was mixing absolute-vs-ratio metrics). Both modes share the source-Limber kernel and track each other within ~0.1% on absolute scale; the ratio (third column) isolates the R(k,z) quality from the common linear-kernel systematic.
…ine duplication
clax/transfer.py
- compute_pk_from_perturbations / compute_linear_matter_pk_from_perturbations
gain a `validate=True` kwarg. Default keeps the existing solved-support
check; internal callers that need P(k) past pt.k_grid[-1] (the Halofit
modulator's k-grid extension) pass `validate=False`.
- Replace the Python-level `if z == 0.0:` fast path with always-interpolate
along τ. Clamping τ_z to pt.tau_grid[-1] makes the spline reproduce
pt.delta_m[:, -1] exactly at z=0, so accuracy is unchanged. The path is
now vmap-safe over z.
clax/nonlinear.py
- compute_pk_nonlinear's σ(R)≥1 fallback used a try/except on
ConcretizationTypeError, which silently bypassed the check under jit/vmap
and produced spurious R values when sigma<1. Replace with an inline
`jnp.where(sigma_R(R_min, lnk, pk_lin) >= 1.0, pk_nl, pk_lin)` that is
vmap-safe by construction. The narrow-grid fallback to pk_lin still
holds (test_halofit_sigma_guard).
clax/lensing.py::_halofit_modulator
- Now calls compute_linear_matter_pk_from_perturbations directly (no inline
τ-interp + 2π²/k³·P_R·δ_m² duplication) and trusts compute_pk_nonlinear's
own σ check.
The duplication was a workaround for the two transfer.py obstacles fixed
above. Behavior on the existing tests is unchanged (test_cl_pp,
test_clpp_halofit_ratio, test_lensing, test_halofit_sigma_guard,
test_halofit_z_cutoff, test_nonlinear all pass).
Collaborator
Author
This was referenced May 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Small follow-up on top of #14 that removes the two vmap obstacles in
clax/transfer.py+ the bypassable σ check inclax/nonlinear.py, and uses the cleaned-up helpers to delete an inline duplication in_halofit_modulator.Net diff: 3 files changed, 44 insertions(+), 63 deletions(-).
Why
_halofit_modulator(added in #11/#14) couldn't reusecompute_linear_matter_pk_from_perturbationsbecause that function had two vmap-unfriendly choices, so the modulator inlined the τ-axis δ_m interpolation, the2π²/k³ · P_R · δ_m²formula, and a manual log-log power-law extension. Similarly,compute_pk_nonlinear's σ check used atry/except ConcretizationTypeErrorthat silently bypassed under jit/vmap, so the modulator replicated the σ check inline withjnp.where.This PR fixes the helpers so the modulator stops needing workarounds.
Changes
clax/transfer.pycompute_pk_from_perturbations/compute_linear_matter_pk_from_perturbationsgainvalidate: bool = True. Default keeps the existing solved-support check; internal callers extending the k-grid pastpt.k_grid[-1]for σ(R) convergence passvalidate=False. The validation is still on for all public table-API callers.if z == 0.0:fast path is replaced with always-interpolate along τ, withtau_zclamped topt.tau_grid[-1]. At z=0 the spline reproducespt.delta_m[:, -1]exactly (boundary), so accuracy is unchanged. The path is now vmap-safe over z.clax/nonlinear.pycompute_pk_nonlinear's σ(R) ≥ 1 fallback is rewritten asjnp.where(sigma_R(R_min, lnk, pk_lin) >= 1.0, pk_nl, pk_lin). Vmap-safe by construction. The narrow-grid fallback topk_linis preserved (test_halofit_sigma_guard)._sigma_convergence_checkis kept for direct unit tests, but is no longer in the path ofcompute_pk_nonlinear.clax/lensing.py::_halofit_modulatorcompute_linear_matter_pk_from_perturbationsdirectly (no inline τ-interp + 2π²/k³·P_R·δ_m² duplication).jnp.where; trusts the helper's now-vmap-safe σ guard.pt.k_grid[-1]is non-physical, so we extend P_lin in log-log space rather than passingk_extthrough the helper).Test plan
Verified locally on
refactor/transfer-vmap-safe:pytest tests/test_cl_pp.py -v→ 13 passed in 95spytest tests/test_lensing.py tests/test_clpp_halofit_ratio.py -v→ 15 passed in 204spytest tests/test_halofit_sigma_guard.py tests/test_halofit_z_cutoff.py tests/test_nonlinear.py -v→ 20 passed in 2spytest tests/test_pk_accuracy.py -v --fast→ forthcoming, will paste results hereNo new tests added — the change is internal refactoring with the same external behavior covered by the existing suite.
Migration
None —
validateis a new keyword-only kwarg with the same default behavior. No public API breakage.