Skip to content

feat: implement ConvolutionalNeuralOperator (CNO) (closes #122) - #134

Open
jitendravjh wants to merge 4 commits into
SciML:mainfrom
jitendravjh:feat/convolutional-neural-operator
Open

jitendravjh wants to merge 4 commits into
SciML:mainfrom
jitendravjh:feat/convolutional-neural-operator

Conversation

@jitendravjh

@jitendravjh jitendravjh commented May 11, 2026 •

Copy link
Copy Markdown
Contributor

Implements CNO from Raonic et al., NeurIPS 2023 (https://arxiv.org/abs/2302.01178).

Each CNOBlock is a 3x3 convolution followed by the paper's activation operator: band limited upsample, activation, then low pass on the way back. Resampling goes through the FFT so inputs are treated as periodic, same as the Fourier layers. The projection uses the same filtered activation.

API matches FourierNeuralOperator(modes, in_ch, out_ch, hidden_ch; ...).

Checklist

  • Appropriate tests were added
  • Any code changes were done in a way that does not break public API
  • All documentation related to code changes were updated
  • The new code follows the contributor guidelines, in particular the SciML Style Guide and COLPRAC
  • Any new documentation only uses public API

@jitendravjh
jitendravjh force-pushed the feat/convolutional-neural-operator branch from d81efeb to e2ded64 Compare May 16, 2026 20:19
@ChrisRackauckas

Copy link
Copy Markdown
Member

Missing tests

@jitendravjh

Copy link
Copy Markdown
Contributor Author

Added test/models/cno_tests.jl with 1D + 2D forward pass, Reactant consistency, and gradient checks (matching the pattern of fno_tests.jl). Doctests and Aqua QA also pass locally.

@jitendravjh
jitendravjh force-pushed the feat/convolutional-neural-operator branch from 8b1a961 to a759fbd Compare September 15, 2026 07:01
@ChrisRackauckas

Copy link
Copy Markdown
Member

🤖 Automated review from an AI agent running as @ChrisRackauckas — not written or reviewed by Chris. It is posted so that you can act on it. Chris or a maintainer may disagree.

Verdict: changes needed before merge (reviewed at a759fbd).

Adds an exported ConvolutionalNeuralOperator, forward/gradient tests, and a separate GridEmbedding device-placement change. Changes are required because the proposed resampling does not implement the cited CNO's filtering guarantees, the new export breaks documentation checks, and API-ownership/versioning requirements remain unmet.

Risk assessment

  • Risk: medium
  • Blast radius: Numerical behavior of the new public model; documentation/QA builds; existing GridEmbedding consumers, including FNO device handling.
  • Evidence: Normal local Core passed 86/86, including 8/8 CNO assertions. Local QA reproduced 20 passed, 1 failed, identifying ConvolutionalNeuralOperator as unrendered. Runic passed on all five changed Julia files. A deterministic frequency probe demonstrated substantial aliasing; details below. CI comparison against default-branch b42958f distinguishes new failures from existing ones.
  • Merge: needs changes (CNO filtering and numerical validation, rendered API documentation, dependency API ownership, and minor-version bump).

Local commands, from the scratch directory, with TMPDIR=$HOME/tmp and Julia 1.12.7:

GROUP=Core timeout 7200 ~/.juliaup/bin/julia +1.12 --project=repo -e 'using Pkg; Pkg.test()'
# Fourier Neural Operator 14/14; DeepONet 10/10; NOMAD 10/10;
# Convolutional Neural Operator 8/8; SpectralConv 12/12;
# SpectralKernel 14/14; Transform Interface 15/15; Precompile workload 3/3.
# Testing NeuralOperators tests passed
GROUP=QA timeout 7200 ~/.juliaup/bin/julia +1.12 --project=repo -e 'using Pkg; Pkg.test()'
# Some tests did not pass: 20 passed, 1 failed, 0 errored, 0 broken.
# Evaluated: isempty([:ConvolutionalNeuralOperator])

Runic command: ~/.juliaup/bin/julia +1.12 --project=/home/crackauc/sandbox/optim-misc-up/runicenv -e 'using Runic; exit(Runic.main(["--check", "--diff", "repo/src/NeuralOperators.jl", "repo/src/layers.jl", "repo/src/models/cno.jl", "repo/test/models/cno_tests.jl", "repo/test/runtests.jl"]))' — exit 0.

An additional Core run with julia_args=["-O0"] was stopped as redundant after the normal run passed; no result from that run is claimed. The full documentation build, CUDA GridEmbedding reproducer, and 32-bit tests were not run locally. No tracked repository files were edited.

CI failures, checked using the actual failed-job logs:

Head check Comparison with default branch
tests / QA (julia 1, ubuntu-latest) / Tests - QA (Julia 1) New failure: missing rendered CNO documentation; default-branch QA passes.
tests / GPU (julia 1, self-hosted Linux X64 gpu-t4) / Tests - GPU (Julia 1) New failure: same missing-docs assertion in QA; default-branch GPU passes.
Documentation / Build and Deploy Documentation New component: :missing_docs for CNO. Both branches also fail :example_block because the Burgers DeepONet data download raises Python: FileURLRetrievalError.
tests / Core (julia 1, ubuntu-latest, x86) / Tests - Core (Julia 1) Pre-existing: both fail initialization with TypeError: in keyword argument priority, expected Int64, got a value of type Int32.

All other reported head checks passed, including formatting and spelling. The default-branch failures above were compared through CI logs, not reproduced locally.

Findings

  1. [P1] src/models/cno.jl:56–60,78–81 — The block lacks the filtering that supports the cited CNO guarantees. Bilinear interpolation followed by average pooling is not the sinc/windowed-sinc resampling specified in the paper's §2, Appendix A.4 and C.1.4. The projection also uses an ordinary pointwise activation. Consequently the stated resolution invariance is unsupported by this implementation. Implement the paper's filtered operators and validate spectral behavior and cross-resolution output values. In a local deterministic check of this actual block, with center-only convolution weight 1, zero bias, activation abs2, and samples of cos(2π·12·j/n), the results were:

    n Output mean Spurious frequency-8 amplitude
    32 0.22104838 0.17942821
    64 0.39687747 0.00230984
    128 0.47052583 0.00334893

    Squaring this cosine yields DC and frequency 24, not frequency 8. The probe uses NeuralOperators.CNOBlock(1,1,(4,),abs2), Lux.setup(Xoshiro(1), block), ps.layer_2.weight .= 0; ps.layer_2.weight[2,1,1] = 1; ps.layer_2.bias .= 0; input shape (n,1,1); and amplitude 2abs(sum(y .* exp.(-2π*im*8*(0:n-1)/n)))/n. Existing shape/gradient assertions pass but do not detect this problem. Separate direct forward checks returned the expected shapes in 1D, 2D and 3D. Paper: https://arxiv.org/html/2302.01178v3.

  2. [P2] src/NeuralOperators.jl:36 / docs/src/api.md:8 — The new exported name has no rendered docs entry. Add ConvolutionalNeuralOperator to a canonical @docs block and document its supported model behavior. This is the exact local QA failure and the new QA/GPU/documentation CI regression, not merely a documentation preference.

  3. [P2] src/layers.jl:349 — The device change accesses get_device through Lux's re-export and lacks a discriminating regression test. Runtime inspection returned parentmodule(Lux.get_device) == MLDataDevices; Lux re-exports that module. Under the required API-ownership rule, use the API through MLDataDevices and declare the direct dependency. Remove this separate GridEmbedding fix from the CNO PR, or substantiate it with the required failing-before/passing-after CUDA regression test; the added CNO tests do not exercise GridEmbedding. The referenced scalar-indexing issue already contains an upstream-fix confirmation: FNO invokes scalar indexing #125 (comment).

  4. [P2] Project.toml:4 — Adding public API leaves the version at 0.7.2. The requested release policy requires a minor bump for new public API; assign an appropriate 0.8.0 version before publishing this feature. No existing public name is removed, and the diff adds no dependencies or weakened/skipped existing tests.

Read the PR description, both conversation comments, all inline comments/reviews (none), and the linked feature issue; Chris requested tests and the author subsequently added them. No other maintainer is reviewing this PR. The additional issue referenced by the GridEmbedding edit was also read.

Push a fix and the PR is reviewed again automatically at the new head.


🤖 Posted by an AI agent — harness: Claude Code · model: claude-opus-5-5[1m] (fleet master); review by Codex CLI 0.157.1 / gpt-6-astra
Conversation: local Claude Code session 3cd6500a-1f81-46b5-ac0b-c466e15b6a53 on Chris's Mac (session ID, no URL)

Implements the Convolutional Neural Operator from:
  Raonic et al., "Convolutional Neural Operators for robust and accurate
  learning of PDEs", NeurIPS 2023. https://arxiv.org/abs/2302.01178

Architecture:
  - Lifting:     Conv(1x...x1): in_channels → hidden_channels
  - CNO blocks:  Upsample(:bilinear) → Conv(3x...x3, act, SamePad) → MeanPool
  - Projection:  Conv(1x...x1, act) → Conv(1x...x1): → out_channels

Each CNOBlock upsamples by upsample_factor, convolves at higher resolution,
then downsamples via MeanPool — ensuring the operator converges to a
continuous limit as spatial resolution increases (resolution-invariant).

New types exported:
  - ConvolutionalNeuralOperator  (the full model)
  - CNOBlock                     (the building block, composable)

Closes SciML#122
@jitendravjh
jitendravjh force-pushed the feat/convolutional-neural-operator branch from a759fbd to 2c5e733 Compare September 26, 2026 19:14
@jitendravjh
jitendravjh force-pushed the feat/convolutional-neural-operator branch from 2c5e733 to a674d40 Compare September 26, 2026 19:50
Replace bilinear upsampling and mean pooling with band-limited FFT resampling around the activation, following the paper's activation operator, and use the same filtered activation in the projection. Add an aliasing check and a spectral resampling test.

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.

2 participants