Document the DFT/NUFFT trade-off on the sparse-operator setup path - #479
Merged
Merged
Conversation
Both transformers work with `apply_sparse_operator` and agree to ~3e-13
relative, but which is *faster* was undocumented, and the answer is not the
one the codebase's existing 10,000-visibility guard implies.
Measured on CPU (`use_jax=False`), the crossover is governed by the product
`N_vis * N_pix`, not by visibility count alone — which follows from the DFT
setup being `O(N_vis * N_pix)` against the NUFFT's `O((N_vis + N_pix) log N)`
plus a fixed ~2s overhead. Seven measurements agree on a crossover near 1e7:
N_vis x N_pix DFT/NUFFT time ratio
1.3e6 0.21x (DFT faster)
2.6e6 0.27x
5.2e6 0.70x
1.0e7 1.16x (crossover)
2.1e7 1.50x
2.3e7 1.41x
8.3e7 1.92x (NUFFT faster)
So at a typical 64x64 mask the crossover sits near 5,000 visibilities, but on
a 32x32 mask the DFT still wins at 4,000. Quoting a visibility count alone
would be wrong on half the grids.
Memory is the more decisive axis and is what makes this more than a
micro-optimisation. The DFT's allocation grows with `N_vis * N_pix` (measured
60 -> 239 -> 293 -> 446 MB as the grid grows at fixed N_vis=4000) while the
NUFFT path allocates nothing measurable beyond its working buffers. At 10.7
bytes per element that extrapolates to ~109 GB at 1M visibilities, which
independently corroborates the ~123 GB figure recorded in bd18a76 and
confirms the NUFFT is the only feasible path at ALMA scale.
Adds the guidance as an INFO on the existing setup log, emitted only when a
NUFFT-backed transformer is used below the crossover — i.e. only on an
explicit, expensive, opt-in call, and never when the advice would be moot.
Deliberately NOT a warning on `Interferometer` construction: that class
defaults to `TransformerNUFFT`, so a small-dataset warning would fire on the
default path for every tutorial, test and workspace example. The dangerous
direction is already a hard `DatasetException` above 10,000 visibilities with
an opt-out, so nothing is left unguarded by keeping this advisory quiet.
Verified the advisory fires for small+NUFFT and stays silent for both
small+DFT and large+NUFFT. Suite green: 1179 passed.
This was referenced Aug 22, 2026
Merged
2 tasks
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
Closes the last open item from the pynufft removal (#475): whether
apply_sparse_operatoris still incompatible withTransformerNUFFT.It is not, and has not been since May. The
NotImplementedErrorguard was removed by #329 (bd18a769, 2026-05-22); only a stale workspace comment still claimed otherwise. Verified directly:apply_sparse_operatorruns withTransformerNUFFTat every scale tried, and its dirty image matchesTransformerDFTto ~3e-13 relative.Both paths are supported. What was undocumented is which one you should actually pick — and the answer isn't the visibility count.
The crossover is the product, not the visibility count
The DFT setup costs
O(N_vis × N_pix); the NUFFT costsO((N_vis + N_pix) log N)plus a fixed ~2 s overhead. So the product decides it. Measured on CPU (use_jax=False), seven points agree on a crossover near 1e7:At a typical 64×64 mask that's ~5,000 visibilities — but on a 32×32 mask the DFT still wins at 4,000. Quoting a visibility count alone would be wrong on half the grids.
Memory is the decisive axis
At fixed
N_vis=4000, growing the grid:The DFT allocates with
N_vis × N_pix; the NUFFT allocates nothing measurable beyond its working buffers. At 10.7 bytes/element that extrapolates to ~109 GB at 1M visibilities — independently corroborating the ~123 GB figurebd18a769recorded by a different route, and confirming the NUFFT is the only feasible ALMA-scale path.Why an INFO and not a warning
Interferometerdefaults toTransformerNUFFT(dataset.py:34). A "NUFFT below the crossover" warning would therefore fire on the default path for every small dataset — every tutorial, test and workspace example. That's warning fatigue for a ~2 s cost.So the guidance is an INFO on the existing setup log, emitted only when a NUFFT-backed transformer is used below the crossover: an explicit, expensive, opt-in call, never fired when the advice would be moot. The genuinely dangerous direction is already a hard
DatasetExceptionabove 10,000 visibilities with an opt-out, so nothing is left unguarded.API Changes
None — internal changes only. One added INFO log and docstring text; no signature, symbol or behaviour changes.
Test Plan
pytest test_autoarray— 1179 passedDatasetExceptionblock, and that churn was deliberately kept outGenerated by Claude Code