Skip to content

gh-1077: Spherical hard switch using S2FFT - #1232

Open
paddyroddy wants to merge 117 commits into
paddy/issue-1243from
paddy/issue-1077
Open

paddyroddy wants to merge 117 commits into
paddy/issue-1243from
paddy/issue-1077

Conversation

@paddyroddy

@paddyroddy paddyroddy commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

Description

This allows users to use glass.harmonics.transform and glass.harmonics.inverse_transform with either healpy or s2fft inputs. S2FFT depends on JAX necessary so I have created an optional dependency group jax that includes both. I've added some tests to check the values are consistent between healpy and s2fft for NumPy inputs. I've also added a test to check what happens if S2FFT isn't installed.

Currently blocked because S2FFT isn't working on 3.13/3.14, see astro-informatics/s2fft#413.

Closes: #1077

Changelog entry

Added: JAX-native scalar and spin spherical harmonic transforms
Added: A glass[jax] install extra
Changed: nside is now mandatory in glass.harmonics.transform

Checks

  • Is your code passing linting?
  • Is your code passing tests?
  • Have you added additional tests (if required)?
  • Have you modified/extended the documentation (if required)?
  • Have you added a one-liner changelog entry above (if required)?

@paddyroddy paddyroddy self-assigned this Oct 2, 2026
@paddyroddy paddyroddy added enhancement New feature or request infrastructure Project infrastructure: dev tools, packaging, etc. dependencies Pull requests that update a dependency file performance Performance improvements or regressions array-api Work is related to the Array API needs-2-reviewers Could be considered "controversial" so worth a second pair of eyes jax-porting Removing mutation to make the code JAX compatible spherical-transform Changed related to spherical transforms, i.e. HEALPix labels Oct 2, 2026
@paddyroddy
paddyroddy requested a balanced review from Copilot October 2, 2026 17:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The coefficient-order contract, CPU weighting behavior, GPU residency, and benchmark dependencies have unresolved issues.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity

Open (4)
What changed in this PR

Adds JAX-backed spherical harmonic transforms via S2FFT and migrates relevant field-generation and lensing paths.

Changes:

  • Adds NumPy/JAX transform wrappers and S2FFT dependencies.
  • Migrates scalar harmonic transforms in fields and lensing.
  • Adds cross-backend transform tests.
File Description
glass/​harmonics.py Adds forward and inverse SHT wrappers.
glass/​lensing.py Uses the new wrappers for convergence transforms.
glass/​fields.py Uses the inverse wrapper for field generation.
glass/​healpix.py Corrects map2alm return typing.
noxfile.py Installs S2FFT for JAX test sessions.
pyproject.toml Adds S2FFT to development groups.
tests/​core/​test_harmonics.py Tests JAX transforms against NumPy.
tests/​core/​test_healpix.py Clarifies optional-backend detection.
tests/​fixtures/​helper_classes.py Generates valid real-map harmonic coefficients.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread glass/harmonics.py
Comment thread glass/lensing.py
Comment thread glass/lensing.py
Comment thread pyproject.toml Outdated
Comment thread glass/healpix.py Outdated
Comment thread tests/core/test_healpix.py Outdated
Comment thread glass/fields.py
Comment thread glass/harmonics.py
flm = s2fft.forward(
maps,
bandlimit,
iter=3,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 is the default for methods healpix and healpix_jax but not for jax for some reason https://astro-informatics.github.io/s2fft/api/transforms/spin_spherical_transform.html#s2fft.transforms.spherical.forward. Required for good accuracy.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rationale for this is method="healpix_jax" with sampling="healpy" wraps healpy and so matches the default used there while the support for iter in the numpy and jax methods was only added latterly and so the original default behaviour of using no iterative refinement was retained when iter support was added for these methods. It could make sense to make a breaking change and default to iter = 3 for sampling = "healpix" with any method though.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think that might make sense. People using S2FFT for the JAX benefits coming from a HEALPix configuration might get thrown.

Comment thread tests/fixtures/helper_classes.py
Comment thread glass/harmonics.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

JAX transforms still have unsupported bandlimits and CPU fallbacks, while runtime dependency and test expectations are incomplete.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
Resolved since last review (3)
Previously missed (4)

In code that hasn't changed since last review

Medium severity JAX forward transform fails when lmax is below 2*nside

glass/​harmonics.py:146

S2FFT also enforces L >= 2 * nside for the forward HEALPix transform, but this path passes lmax + 1 unchanged. Therefore valid calls such as from_convergence(..., lmax=8) at nside=32 work with NumPy but fail with an assertion for JAX. Mirror the inverse path by transforming at an effective bandlimit of at least 2 * nside, then crop the 2-D coefficients back to the requested bandlimit before packing them.

Medium severity Derive NSIDE from shape instead of converting JAX arrays to NumPy

glass/​lensing.py:297

Before reaching this JAX transform, hp.get_nside(kappa) uses numpy_fallback, which calls np.asarray(kappa). That copies the entire device map to the host in eager execution and raises a tracer-conversion error under jax.jit, so even the potential-only path is not fully GPU-resident or traceable. NSIDE can be derived from the static map shape without converting the array.

Medium severity Use a backend-native spin transform for JAX deflection and shear

glass/​lensing.py:330

The deflection path still reaches hp.alm2map_spin at line 334 (and the shear path at line 357); its numpy_fallback converts these JAX coefficients to NumPy and performs the transform on the CPU. Thus the full lensing workflow requested by #1077 remains GPU-incompatible whenever deflection or shear is requested. A backend-native spin inverse transform is needed before this issue can be considered closed.

Low severity Document transform and inverse_transform in the harmonics API

glass/​harmonics.py:56

This new public wrapper (and transform) is absent from docs/modules/harmonics.rst, whose API reference still lists only multalm. Add both transform functions there so the feature introduced by this PR appears in the generated user documentation.

Comment thread pyproject.toml Outdated
Comment thread tests/core/test_healpix.py
Comment thread glass/healpix.py Outdated
Comment thread glass/healpix.py Outdated
@paddyroddy paddyroddy removed the blocked The issue or pull request is blocked by something label Oct 12, 2026
@paddyroddy
paddyroddy requested a review from connoraird October 12, 2026 09:45

@connoraird connoraird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just one thought

Comment thread tests/core/test_harmonics.py Outdated

from typing import TYPE_CHECKING

import numpy as np

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what I personally prefer but should we be defining np as a fixture since that's what we're doing for jnp?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this wonder too. The question is whether there is much point or benefit? For array_api_strict and jax we're actually doing something. We wouldn't need to configure NumPy at all. Like even pytest.importorskip is unnecessary at the moment as NumPy is always required.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only benefit is np won't be available so we'd know if we accidentally call it an somehow don't notice it in the code.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There would still be the risk that we have the fixture and forget we're using it. I'm happy to do it though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I don't know either way so I'd say leave as is and merge

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on f63a8d2?

@connoraird connoraird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I do prefer it with the np fixture

@paddyroddy paddyroddy mentioned this pull request Oct 15, 2026
3 of 5 tasks
@paddyroddy
paddyroddy added this pull request to stack #1260 October 15, 2026 14:13
@paddyroddy
paddyroddy removed this pull request from stack #1260 October 15, 2026 14:14
@paddyroddy
paddyroddy added this pull request to stack #1262 October 15, 2026 14:54
paddyroddy added a commit that referenced this pull request Oct 15, 2026
@paddyroddy
paddyroddy removed this pull request from stack #1262 October 15, 2026 15:14
@connoraird connoraird mentioned this pull request Oct 15, 2026
3 of 5 tasks

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

array-api Work is related to the Array API dependencies Pull requests that update a dependency file enhancement New feature or request infrastructure Project infrastructure: dev tools, packaging, etc. jax-porting Removing mutation to make the code JAX compatible needs-2-reviewers Could be considered "controversial" so worth a second pair of eyes performance Performance improvements or regressions spherical-transform Changed related to spherical transforms, i.e. HEALPix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Experiment with hard switch for spherical transforms Create wrapper functions for spherical harmonic transforms

5 participants