Skip to content

FNO: run spectral convolutions in float32 under autocast - #2150

Open
GeorgeLiang3 wants to merge 2 commits into
NVIDIA:mainfrom
GeorgeLiang3:fno-amp-fp32
Open

GeorgeLiang3 wants to merge 2 commits into
NVIDIA:mainfrom
GeorgeLiang3:fno-amp-fp32

Conversation

@GeorgeLiang3

Copy link
Copy Markdown

PhysicsNeMo Pull Request

Description

Closes #2149.

FNO is marked amp=False in its MetaData, so validate_amp returns early and the FNO AMP test checks nothing. Under float16 autocast on CUDA, SpectralConv1d-4d fail. With a non-power-of-two grid, cuFFT raises "cuFFT only supports dimensions whose sizes are powers of two when computing in half precision". With a power-of-two grid, the complex-half mode multiplication raises "baddbmm_cuda" not implemented for 'ComplexHalf'. On torch 2.13, the minimum version, CUDA FFTs also reject bfloat16 (Unsupported dtype BFloat16), so bfloat16 autocast fails too.

This PR adds a small decorator on SpectralConv1d-4d.forward. When autocast is active for the input's device, it upcasts float16 and bfloat16 inputs to float32 and runs the FFT, mode multiplication and inverse FFT with autocast disabled. Layers around the spectral convolutions keep running under autocast. Without autocast, forward is unchanged, so full-precision outputs and the existing golden files are unchanged. This is the same approach xDeepONet uses in SpatialBranch._spectral (#1737, #1738), placed in the shared layers as suggested in the design note of #1737.

FNO is now marked AMP-capable. Note that StaticCaptureTraining and StaticCaptureEvaluateNoGrad default to use_amp=True, so they now enable autocast for FNO. The in-repo FNO examples pass use_amp=False and are unaffected.

Two small fixes in the same layers:

  • The output spectrum is built in the input spectrum's complex dtype instead of torch.cfloat, so SpectralConv1d returns float64 for float64 inputs. 2D-4D already did.
  • reset_parameters uses torch.rand_like, so it keeps the weights' device and dtype. Before, calling it after .to("cuda") or .double() moved the weights back to CPU float32. Default initialization is unchanged under a fixed seed.

New tests are in test/nn/module/test_spectral_conv.py. Until now only SpectralConv4d had a direct test, in test_nd_conv_layers.py. The new tests cover:

  • a reference comparison, forward and gradients, against a plain slice-accumulation implementation, for 1D-4D, including 2 * modes == size and corners that overlap on each axis;
  • float16 and bfloat16 reduced-precision inputs under autocast, which must equal the float32 path exactly;
  • an FNO training step under autocast, with GradScaler for float16;
  • torch.compile(fullgraph=True), with and without autocast;
  • meta-device forward, float64 output, and reset_parameters keeping device and dtype.

Against main, 26 of the new tests fail on CUDA: reduced precision, the FNO autocast step, compile with autocast, 1D float64, and reset_parameters.

Validation on torch 2.13.0+cu130 and 2.14.1+cu130:

  • On GPU, test/nn/module, test/models/fno, afno, sfno and experimental/models/xdeeponet pass: 1116 passed, 150 skipped (optional dependencies and ONNX).
  • CPU-only, the new tests and test/models/fno pass.
  • pre-commit run --all-files passes.
  • Inductor fullgraph forward and backward for FNO under float16 and bfloat16 autocast run without graph breaks. CI's compile check uses a no-op backend.

Checklist

Dependencies

None.

@copy-pr-bot

copy-pr-bot Bot commented Oct 21, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown
Contributor

CODEOWNERS review map

Current for commit 4647f6960e95. An approval covers every file listed for that owner; one owner is sufficient for shared files.

⏳ @ktangsali — 1 file(s)
  • physicsnemo/models/fno/fno.py
⏳ @loliverhennigh — 1 file(s)
  • physicsnemo/nn/module/spectral_layers.py

No CODEOWNER

  • CHANGELOG.md
  • test/nn/module/test_spectral_conv.py

Comment /codeowners-info to refresh.

@greptile-apps

greptile-apps Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in Greptile

[Medium impact] The changes affect spectral layers shared by multiple model workflows.

The PR appears safe to merge, with a non-blocking improvement needed in the training test.

Findings

  1. P2 Broken training passes the test ▶

Summary

This PR lets FNO use mixed precision while keeping spectral convolutions in float32.

  • FNO spectral layers run Fourier work in float32 under autocast.
  • Spectral convolutions preserve float64 outputs for float64 inputs.
  • Resetting spectral weights keeps their device and dtype.

GeorgeLiang3 explicitly identifies default autocast in StaticCaptureTraining and StaticCaptureEvaluateNoGrad as intentional and documents use_amp=False as the opt-out.

Reviews (1) · Last reviewed commit: "fix(fno): run spectral convolutions in f..." · Reviewed by Greptile

Comment thread test/nn/module/test_spectral_conv.py Outdated
Comment on lines +206 to +210
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
assert torch.isfinite(loss)
assert all(torch.isfinite(p).all() for p in model.parameters())

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.

P2 Broken training passes the test

test_fno_autocast_training_step can pass without updating any weights. For float16, GradScaler.step skips the update when gradients contain infinities or NaNs, leaving the initial weights finite. Checking only the loss and weights therefore misses a broken backward pass. Check that gradients are finite after unscaling and that at least one weight changes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The test now unscales the gradients before scaler.step and checks that every trainable parameter has a finite gradient. After the step it checks that every trainable parameter changed. It only checks parameters with requires_grad, because Conv2dFCLayer sets requires_grad=False on its bias. I confirmed the updated test fails when a gradient is forced to inf or NaN, and when a parameter receives no gradient or an all-zero gradient; the previous version passed in all of these cases under float16.

@GeorgeLiang3

Copy link
Copy Markdown
Author

Hi @ktangsali @loliverhennigh , could you review this please? thanks

FNO was marked amp=False, so validate_amp skipped it. Under float16
autocast, SpectralConv1d-4d failed: cuFFT accepts half precision only for
power-of-two sizes, and the complex-half mode multiplication is not
implemented. torch 2.13 also rejects bfloat16 FFTs on CUDA.

SpectralConv1d-4d now upcast float16 and bfloat16 inputs to float32 and
run the FFT, mode multiplication and inverse FFT with autocast disabled,
when autocast is active for the input's device. Layers around them keep
running under autocast. Without autocast the forward is unchanged. FNO
is marked AMP-capable.

Also:
- build the output spectrum in the input spectrum's complex dtype, so
  SpectralConv1d returns float64 for float64 inputs
- reset_parameters keeps the weights' device and dtype

Adds test/nn/module/test_spectral_conv.py with direct tests for the
spectral convolution layers.

Signed-off-by: GeorgeLiang3 <liangzhouji@gmail.com>
…g test

GradScaler skips the optimizer step when gradients are not finite, so a
broken backward pass under float16 could leave the weights untouched and
still pass. The test now unscales and checks the gradients of every
trainable parameter before the step, and checks that each one changed
after it.

Signed-off-by: GeorgeLiang3 <liangzhouji@gmail.com>
@GeorgeLiang3

GeorgeLiang3 commented Oct 30, 2026 •

Copy link
Copy Markdown
Author

@melo-gonzo this moves the float32 spectral-convolution guard from xDeepONet (#1738) into spectral_layers.py, as you suggested in that review. Could you take a look or approve CI when you have time?

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FNO fails under torch.autocast: SpectralConv1d-4d run FFTs in half precision

1 participant