Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
99c75ad
Add Clenshaw-Curtis quadrature rule implementation
matt-graham Apr 28, 2026
ae28cc1
Change to inverse RFFT based implementation
matt-graham Apr 30, 2026
f18db35
Correct CC weights expression for even L
matt-graham Apr 30, 2026
2e47ee4
Add Fejer-2 weights and refactor CC implementation to reuse
matt-graham May 19, 2026
efa2fb4
Add dimensions for CC and F2 sampling grids
matt-graham May 19, 2026
8f2c2d9
Add CC and F2 sampling methods to numpy quadrature wrapper
matt-graham May 19, 2026
2311a4f
Fix F2 theta positions
matt-graham May 19, 2026
8e7259a
Separate out CC and F2 phi grid sizes
matt-graham May 19, 2026
3462e95
Add tests for quadrature rules on known 1D integrals
matt-graham May 20, 2026
00baa27
Link quadrature weight and ntheta defs for CC and F2 rules
matt-graham May 21, 2026
21ed461
Simplify index to phi computation using nphi def
matt-graham May 21, 2026
9e1f84c
Increase number of theta samples for F2 rule by one
matt-graham May 21, 2026
f5e7278
Factor out m offset 1 and pole singularity sets
matt-graham May 21, 2026
63790b8
Factor out Gl quadrature theta only to match other rules
matt-graham May 21, 2026
a0e0d72
Match signature of DH theta only quadrature weight function to other …
matt-graham May 21, 2026
37bac50
Use nphi_equiang function uniformly in quadrature weight implementations
matt-graham May 21, 2026
a0c2891
Refactor index to theta to use ntheta
matt-graham May 21, 2026
530babe
Double CC and F2 scheme number of theta samples
matt-graham May 21, 2026
1ac5fc3
Include CC and F2 in m offset 1 schemes
matt-graham May 21, 2026
aa09f7c
Correct bug in determining scheme for dealing with pole singularity
matt-graham May 21, 2026
9366a6c
Correct bug in using MW rather than MWSS to compute nphi
matt-graham May 21, 2026
417b374
Generalize additional implicit special casing for handling pole singu…
matt-graham May 21, 2026
0daaeaa
Add CC and F2 to sample scheme lists in docstrings
matt-graham May 21, 2026
7f64fd7
Include CC and F2 schemes in spherical transform tests
matt-graham May 21, 2026
f801d6c
Move array-api-extra to package rather than build dependencies
matt-graham May 21, 2026
ed9dcc5
Remove CC + F2 from tests using SSHT
matt-graham May 21, 2026
176b82f
Further removing of CC + F2 from tests using SSHT
matt-graham May 21, 2026
832476b
Add equiangular sampling scheme set
matt-graham May 22, 2026
304bee5
Adjust tolerances for spherical precompute tests
matt-graham May 22, 2026
3756e22
Handle CC and F2 schemes in precompute kernel construction
matt-graham May 22, 2026
0be15f1
Generalize handling of poles in JAX precompute function
matt-graham May 22, 2026
bff50d3
Account for L=1 case for CC quadrature weights
matt-graham May 22, 2026
e0c447d
Add additional quadrature weights tests
matt-graham May 22, 2026
4fbdded
Remove unneeded special case for CC rule with n_theta = 2
matt-graham May 22, 2026
f6b4145
Give quadrature test using transform more descriptive name
matt-graham May 22, 2026
71de50f
Add further tests for quadrature rule exceptions
matt-graham May 22, 2026
e2aea88
Add tests for HEALPix quadrature weights
matt-graham May 22, 2026
b3efcf7
Add tests for Wigner kernel exceptions and n sample function
matt-graham May 22, 2026
9049377
Relax test condition on number of samples to non-negativity
matt-graham May 22, 2026
53573ed
Add CC and F2 sampling scheme details to docs
matt-graham May 22, 2026
3447e48
Minor fixes to sampling scheme doc page formatting
matt-graham May 22, 2026
87fb759
Merge branch 'main' into mmg/clenshaw-curtis-quadrature
matt-graham Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/api/utility/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ Utility Functions
- Compute MW quadrature weights for :math:`\theta` and :math:`\phi` integration.
* - :func:`~s2fft.utils.quadrature.quad_weights_mwss`
- Compute MWSS quadrature weights for :math:`\theta` and :math:`\phi` integration.
* - :func:`~s2fft.utils.quadrature.quad_weight_dh_theta_only`
- Compute DH quadrature weight for :math:`\theta` integration (only), for given :math:`\theta`.
* - :func:`~s2fft.utils.quadrature.quad_weights_dh_theta_only`
- Compute DH quadrature weight for :math:`\theta` integration (only).
* - :func:`~s2fft.utils.quadrature.quad_weights_mw_theta_only`
- Compute MW quadrature weights for :math:`\theta` integration (only).
* - :func:`~s2fft.utils.quadrature.quad_weights_mwss_theta_only`
Expand Down
90 changes: 76 additions & 14 deletions docs/sampling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ Sampling schemes

The structure of the algorithms implemented in ``S2FFT`` can support a number of sampling schemes, which we give a brief overview of here.
An at-a-glance summary of the differences between the supported sampling schemes is also provided :ref:`in the table below <sampling-comparison-table>`, with further information available in the dedicated section for each scheme.
A more thorough overview of the schemes can be found in section 4.2 of `Price & McEwen (2025) <https://arxiv.org/abs/2311.14670>`_.
A more thorough overview of most of the schemes can be found in section 4.2 of `Price & McEwen (2025) <https://arxiv.org/abs/2311.14670>`_.

We adopt the usual ``S2FFT`` conventions for spherical coordinates; :math:`\theta\in[0, \pi]` (colatitude) and :math:`\varphi\in[0,2\pi)` (longitude), with :math:`\theta_t` and :math:`\varphi_p` being the discretised samples (indexed by $t$ and $p$) drawn by the sampling scheme.
We denote by $L$ the band-limit of the signals we are considering.
We adopt the usual ``S2FFT`` conventions for spherical coordinates; :math:`\theta\in[0, \pi]` (colatitude) and :math:`\varphi\in[0,2\pi)` (longitude), with :math:`\theta_t` and :math:`\varphi_p` being the discretised samples (indexed by :math:`t` and :math:`p`) drawn by the sampling scheme.
We denote by :math:`L` the band-limit of the signals we are considering.

.. _sampling-comparison-table:

.. list-table:: At-a-glance comparison of sampling schemes
:header-rows: 1
:align: center
:width: 95
:widths: 20 10 20 20 15 15
:widths: 20 10 30 15 15 15

* - Scheme
- API string
- Number of sample points [#n-samples-vs-memory-storage]_
- Equi- angular
- Equi-angular
- Equal region area
- Sampling theorem
* - :ref:`mcewen-wiaux-mw`
Expand Down Expand Up @@ -54,10 +54,22 @@ We denote by $L$ the band-limit of the signals we are considering.
- Yes
* - :ref:`healpix`
- ``"healpix"``
- $12 N_{side}^2$
- :math:`12 N_{side}^2`
- No
- Yes
- No
* - :ref:`clenshaw-curtis-cc`
- ``"cc"``
- :math:`(2L - 1) \times 2L`
- Yes
- No
- Yes
* - :ref:`fejer-rule-2-f2`
- ``"f2"``
- :math:`(2L - 1) \times 2L`
- Yes
- No
- Yes

Specifying sampling schemes in ``S2FFT``
----------------------------------------
Expand Down Expand Up @@ -147,11 +159,11 @@ Further information; `McEwen & Wiaux (2012) <https://arxiv.org/abs/1110.6298>`_.

.. _mcewen-wiaux-mwss:

McEwen & Wiaux with Symmetric Sampling (MWSS)
---------------------------------------------
McEwen & Wiaux Symmetric Sampling (MWSS)
----------------------------------------

This sampling scheme uses slightly more samples than MW, requiring an array holding :math:`(L+1)\times 2L` elements (with $2(L^2 - L + 1)$ independent degrees of freedom).
Asymptotically, we still only require :math:`\sim 2L^2` elements in memory as $L$ increases.
This sampling scheme uses slightly more samples than MW, requiring an array holding :math:`(L+1)\times 2L` elements (with :math:`2(L^2 - L + 1)` independent degrees of freedom).
Asymptotically, we still only require :math:`\sim 2L^2` elements in memory as :math:`L` increases.
In exchange for slightly higher memory usage, the sample locations possess antipodal symmetry.

Sample positions are defined by
Expand Down Expand Up @@ -187,7 +199,7 @@ Gauss-Legendre (GL)
The GL sampling theorem also requires an array of :math:`L\times (2L-1) \sim 2L^2` elements to represent the signal.
Like :ref:`DH <driscoll-healy-dh>`, there is no redundancy in samples at the poles, so the same number of independent degrees of freedom are needed.

The :math:`\theta_t` are determined by the roots of the Legendre polynomials of order $L$, whilst the :math:`\varphi_p` are defined by
The :math:`\theta_t` are determined by the roots of the Legendre polynomials of order :math:`L`, whilst the :math:`\varphi_p` are defined by

.. math::

Expand All @@ -207,13 +219,63 @@ However, HEALPix sampling **does not** exhibit a sampling theorem and so round-t
An `iterative refinement <https://en.wikipedia.org/wiki/Iterative_refinement>`_ scheme can be applied to the forward transform to reduce this round-trip error at the cost of additional computation.
This can be applied in ``S2FFT``'s forward transforms by setting the `iter` argument to the number of iterations to perform, with more iterations giving a smaller round-trip error.

A HEALPix grid is defined by a resolution parameter $N_{side}$, requiring $12 N_{side}^2$ elements (and independent degrees of freedom) stored in memory.
Given a resolution parameter, the grid will contain $N_{hp} = 12 N_{side}^2$ regions of the same area :math:`\frac{\pi}{3N_{side}^2}`.
The regions will be laid out on $4N_{side}-1$ iso-latitude rings, and the distribution of regions will be symmetric about the equator.
A HEALPix grid is defined by a resolution parameter :math:`N_{side}`, requiring :math:`12 N_{side}^2` elements (and independent degrees of freedom) stored in memory.
Given a resolution parameter, the grid will contain :math:`N_{hp} = 12 N_{side}^2` regions of the same area :math:`\frac{\pi}{3N_{side}^2}`.
The regions will be laid out on :math:`4N_{side}-1` iso-latitude rings, and the distribution of regions will be symmetric about the equator.
For the equations defining the exact positioning of the regions, their centres, their boundaries, and how they are organised into an array, see section 5 of `Gorski et al. (2005) <https://arxiv.org/abs/astro-ph/0409513>`_.

Further information; `Gorski et al. (2005) <https://arxiv.org/abs/astro-ph/0409513>`_.

.. _clenshaw-curtis-cc:

Clenshaw-Curtis (CC)
--------------------

Clenshaw-Curtis quadrature `(Cleshaw and Curtis, 1960) <https://doi.org/10.1007/BF01386223>`_,
is based on expansion of the integrand in terms of Chebyshev polynomials.
It can be used in a spherical harmonic transform setting to numerically compute integrals over the co-latitude,
:math:`\theta`, dimension.
Clenshaw-Curtis quadrature requires roughly double the number of nodes to exactly integrate a polynomial of a given degree
compared to Gauss-Legendre quadrature,
however the co-latitude nodes are equispaced and grids of different resolutions can be nested.

Sample positions are defined by

.. math::

\theta_t &= \frac{\pi t}{2L-2}, &\quad t\in\lbrace 0,1,...,2L-2 \rbrace, \\
\varphi_p &= \frac{2\pi p}{2L}, &\quad p\in\lbrace 0,1,...,2L-1\rbrace.

Note that here we follow the original definition of the Clenshaw-Curtis quadrature rule
and include samples at both poles.
An analogous scheme which excludes the pole is available as :ref:`F2 <fejer-rule-2-f2>`.
Due to the redundancy in samples at the poles,
the total number of distinct sites on the sphere used by this sampling scheme is :math:`(2L-3)(2L)+2 = 4L^2 -6L + 2`.

For further information see `Hotte and Ujiie (2018) <https://doi.org/10.1002%2Fqj.3282>`_.

.. _fejer-rule-2-f2:

Fejér's second rule (F2)
------------------------

Fejér quadrature `(Fejer, 1933) <http://projecteuclid.org/euclid.bams/1183496842>`_,
is closely related to Clenshaw-Curtis quadrature.
Fejér's second rule can be used to define a sampling scheme analagous to :ref:`CC <clenshaw-curtis-cc>` but with the poles excluded.

Sample positions are defined by

.. math::

\theta_t &= \frac{\pi(t + 1)}{2L}, &\quad t\in\lbrace 0,1,...,2L-2 \rbrace, \\
\varphi_p &= \frac{2\pi p}{2L}, &\quad p\in\lbrace 0,1,...,2L-1\rbrace.

This requires :math:`(2L-1) \times 2L = 4L^2 - 2L` elements to be held in memory,
and since the poles are not sample points,
the same number of independent degrees of freedom to represent the signal.

For further information see `Hotte and Ujiie (2018) <https://doi.org/10.1002%2Fqj.3282>`_.

.. rubric:: Footnotes

.. [#n-samples-vs-memory-storage] The "number of sample points" listed here refers to the size of the array recording sample values in memory.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ description = "Differentiable and accelerated spherical transforms with JAX"
dependencies = [
"numpy>=1.20",
"jax>=0.5.0",
"array-api-extra >= 0.10.1",
]
dynamic = [
"version",
Expand Down
32 changes: 16 additions & 16 deletions s2fft/base_transforms/spherical.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def inverse(
spin (int, optional): Harmonic spin. Defaults to 0.

sampling (str, optional): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "gl", "healpix"}. Defaults to "mw".
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}. Defaults to "mw".

nside (int, optional): HEALPix Nside resolution parameter. Only required
if sampling="healpix". Defaults to None.
Expand Down Expand Up @@ -80,7 +80,7 @@ def _inverse(
spin (int, optional): Harmonic spin. Defaults to 0.

sampling (str, optional): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}. Defaults to "mw".
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}. Defaults to "mw".

method (str, optional): Harmonic transform algorithm. Supported algorithms include
{"direct", "sov", "sov_fft", "sov_fft_vectorized"}. Defaults to
Expand Down Expand Up @@ -154,7 +154,7 @@ def forward(
spin (int, optional): Harmonic spin. Defaults to 0.

sampling (str, optional): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}. Defaults to "mw".
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}. Defaults to "mw".

nside (int, optional): HEALPix Nside resolution parameter. Only required
if sampling="healpix". Defaults to None.
Expand Down Expand Up @@ -217,7 +217,7 @@ def _forward(
spin (int, optional): Harmonic spin. Defaults to 0.

sampling (str, optional): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}. Defaults to "mw".
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}. Defaults to "mw".

method (str, optional): Harmonic transform algorithm. Supported algorithms include
{"direct", "sov", "sov_fft", "sov_fft_vectorized"}. Defaults to
Expand Down Expand Up @@ -304,7 +304,7 @@ def _compute_inverse_direct(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand Down Expand Up @@ -391,7 +391,7 @@ def _compute_inverse_sov(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand Down Expand Up @@ -465,7 +465,7 @@ def _compute_inverse_sov_fft(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand All @@ -486,7 +486,7 @@ def _compute_inverse_sov_fft(
assert L >= 2 * nside

ftm = np.zeros(samples.ftm_shape(L, sampling, nside), dtype=np.complex128)
m_offset = 1 if sampling in ["mwss", "healpix"] else 0
m_offset = 1 if sampling in samples.M_OFFSET_1_SCHEMES else 0

for t, theta in enumerate(thetas):
phi_ring_offset = (
Expand Down Expand Up @@ -558,7 +558,7 @@ def _compute_inverse_sov_fft_vectorized(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand All @@ -576,7 +576,7 @@ def _compute_inverse_sov_fft_vectorized(

"""
ftm = np.zeros(samples.ftm_shape(L, sampling, nside), dtype=np.complex128)
m_offset = 1 if sampling in ["mwss", "healpix"] else 0
m_offset = 1 if sampling in samples.M_OFFSET_1_SCHEMES else 0

for t, theta in enumerate(thetas):
phase_shift = (
Expand Down Expand Up @@ -634,7 +634,7 @@ def _compute_forward_direct(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand Down Expand Up @@ -726,7 +726,7 @@ def _compute_forward_sov(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand Down Expand Up @@ -822,7 +822,7 @@ def _compute_forward_sov_fft(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand All @@ -844,7 +844,7 @@ def _compute_forward_sov_fft(
flm = np.zeros(samples.flm_shape(L), dtype=np.complex128)
ftm = np.zeros_like(f).astype(np.complex128)

m_offset = 1 if sampling in ["mwss", "healpix"] else 0
m_offset = 1 if sampling in samples.M_OFFSET_1_SCHEMES else 0

if sampling.lower() == "healpix":
ftm = hp.healpix_fft(f, L, nside, "numpy", reality)
Expand Down Expand Up @@ -938,7 +938,7 @@ def _compute_forward_sov_fft_vectorized(
spin (int): Harmonic spin.

sampling (str): Sampling scheme. Supported sampling schemes include
{"mw", "mwss", "dh", "healpix"}.
{"mw", "mwss", "dh", "gl", "healpix", "cc", "f2"}.

thetas (np.ndarray): Vector of sample positions in :math:`\theta` on the sphere.

Expand All @@ -960,7 +960,7 @@ def _compute_forward_sov_fft_vectorized(
flm = np.zeros(samples.flm_shape(L), dtype=np.complex128)
ftm = np.zeros_like(f).astype(np.complex128)

m_offset = 1 if sampling in ["mwss", "healpix"] else 0
m_offset = 1 if sampling in samples.M_OFFSET_1_SCHEMES else 0
if reality:
m_conj = (-1) ** (np.arange(1, L) % 2)

Expand Down
Loading
Loading