Repository navigation
Let the finite-difference problems solve at a precision of their own, and by FFT - #785
Merged
Merged
Conversation
… and by FFT GenericNDimFinDiff gains `solve_precision`, the precision the implicit solve runs at independently of the state's `dtype`, and `solver_type='FFT'` for periodic grids, where the operator is circulant and a solve is two transforms and a division. With `solve_precision=None`, the default, the existing solve runs unchanged. Set, the solve genuinely works in that precision: the sparse solvers on a copy of the operator held at it, the FFT with transforms at it, and the result is widened back to `dtype`. Sparse matrices stop at single precision, so float16 needs 'FFT'; on a GPU those are cuFFT's half-precision transforms, on a CPU an emulation, documented as such. Complex states solve at complex precision, which is ParaDiag's case. A Krylov solve at reduced precision is not asked for less than 100 eps of its precision: asked for more, CG divides by a dot product that has rounded to zero and returns NaN, as it did on a T4. advectionNd now passes `dtype` and `useGPU` through, which it never did, so the claim that every FD problem runs on GPUs holds for advection too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||
pancetta
added a commit
that referenced
this pull request
Oct 8, 2026
… of it (#786) * Write down the correction-solve contract, and keep the sweeper's side of it The delta form exists so that the implicit solve -- typically the expensive, often external part -- can work in, and return, a much lower precision while the iteration still converges to full precision. What the sweeper and the solver guarantee each other for that to hold is now documented on Problem, replacing the note on the two optional methods. The sweeper's side, on the linear route: it hands the solve its right-hand side at unit size and multiplies the result back, which is exact and keeps any format clear of underflow -- the correction it hands over shrinks with the iteration, below float16's smallest subnormal by 1e-10 -- and it widens a result that comes back in any precision before scaling it, so scaling never happens at the solver's precision. The affine part f(0, t) is removed after the scaling: the stock solve adds it back unscaled, and the two only cancel at the same scale, which an existing test caught. On the nonlinear route the scaled unknown would enter f, so there the solver scales internally, as documented. Tested with a solver that rounds to float16, solves in single precision and returns float16: in delta form it reaches the double-precision floor, and fails at 1.2e-5 with the scaling removed; stock SDC with it stalls. On a T4, delta-form SDC with #785's genuine half-precision FFT solve reaches 4.9e-14 against 6.2e-14 in double. The docstrings write the collocation residual as r, keeping epsilon for a format's precision, as the DeltaSDC README now does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the contract's claim on a GPU, with genuine half-precision arithmetic test_half_precision_solve_reaches_double_precision_in_delta_form ran on the CPU only, where a float16 FFT solve is an emulation around a single-precision transform. It now also runs under the cupy marker, where the solve is cuFFT's half-precision arithmetic -- the case the correction-solve contract is written for: delta-form SDC must still reach the double-precision floor, and stock SDC with the same solve must still stall. One parametrisation, the same assertions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
pancetta
added a commit
that referenced
this pull request
Oct 8, 2026
…lve contract #785 gave the finite-difference problems a solve precision of their own and an FFT solve, and #786 made the delta-form sweeper scale the linear route's right-hand side for any solver. The project's own versions of both go: - heat_delta keeps only what no library solver has, half precision on a sparse solver, which it emulates; every other precision is the library's genuine solve. Its CG and FFT routes and its `normalize` knob are gone. - heat_paradiag goes entirely: ParaDiag runs on the stock heat problem with dtype='complex128' and the library's solve_precision. paradiag.py keeps the transform at reduced precision, and circulant_eigenvalues goes with it. - run_gpu.py uses heatNd_unforced and heatNd_forced directly. - The project's FFT tests, which the library's test_FD_solve_precision now covers, and the GPU fingerprint of the FFT solve go. The heat ladder's "unnormalised fp16 solve" control no longer fails, because the sweeper now scales for that solver -- the contract working. It is removed from the table, and lives on in the library's test with the scaling switched off. The heat figure's control becomes stock SDC with the same half-precision solve (it stalls at 0.25); Allen-Cahn keeps its unscaled solver, since on the nonlinear route the solver scales itself. The README says so. Every iteration count in the heat table is unchanged; the fp32 rows' floors move within round-off (7.2e-14 to 8.8e-14), their solves now genuine rather than emulated. ParaDiag's table is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Part of moving the reduced-precision study (#784) into pySDC proper rather than keeping it in project-level problem twins. The goal it serves: showing that an expensive implicit solver may run, and return its result, at significantly reduced precision while pySDC still converges to full precision. This PR gives the library a solver that genuinely does so, independently of how the state is stored.
What changes
GenericNDimFinDiff, and through itheatNd_unforced,heatNd_forcedandadvectionNd, gains two options:solve_precision: the precision the implicit solve runs at, independently of the state'sdtype.None, the default, runs the existing solve unchanged. When set, the solve genuinely works in that precision:'direct','GMRES'and'CG'on a copy of the operator held at it, and the result is widened back todtype.solver_type='FFT', for periodic grids. The operator is circulant, so the FFT diagonalises it and a solve is two transforms and a division. That is exact, works for non-symmetric operators such as advection, and is the cheapest solve on a GPU.Half precision is only reachable through
'FFT', since SciPy and CuPy sparse matrices stop at single precision:float16is emulated: values are rounded through it around a single-precision transform. The docstring says so; it is optimistic.float16's range. An unscaled 1024² transform reaches 1e6;float16stops at 65504.Complex states solve at complex precision. That is ParaDiag's case: complex128 state, complex64 solve, complex factor.
Two fixes along the way:
advectionNdnow passesdtypeanduseGPUthrough. It never did, so the GPU project's statement that every FD problem became GPU-capable did not hold for advection.Tests
New
pySDC/tests/test_problems/test_FD_solve_precision.py:float16with a sparse solver, and'FFT'on a non-periodic grid.delta_implicit,linear_implicit=True) with a half-precision FFT solve reaches the double-precision residual floor. Stockgeneric_implicitwith the same solve stalls above 1e-6. That second case is the control.cupy): fp32 CG and genuine fp16 FFT on the device.Local: black and ruff over
pySDC, and 1594basetests across problems, sweepers, transfers, tutorials and the problem helper. On 4×T4 throughetc/modal_gpu_tests.py: the 17cupytests oftest_problemspass.🤖 Generated with Claude Code