Let SymplecticEuler say how it meets the layerwise seam - #251
Open
michakraus wants to merge 1 commit into
Open
Conversation
`SymbolicNeuralNetworks` composes the pullback of a chain layer by layer, putting fresh symbolic variables between two layers instead of inlining one layer's expression into the next. Its seam was a plain vector, so it assumed every layer maps an array to an array. A `SymplecticEuler` built with `return_parameters = true` threads the parameters of the system on to the next layer and therefore returns a `Tuple`, which could not be seeded at all -- and because `composes_layerwise` said the chain decomposes, `layerwise = :auto` committed to that path and then raised (SNN #54). SNN 0.7 widened the seam. `SymplecticEuler` now carries `SymbolicEnergy`'s `parameter_length` and `parameter_layout` -- as a trailing type parameter, so every existing partial signature goes on matching with the layout left free -- and implements the four methods that say how it meets the seam: `carried_variables`, `seam_value`, `state_expressions` and `seam_arguments`. What it carries is data and never a differentiation target: the sensitivity pairs with the state, and the flattened system parameters become one more argument of each generated kernel. So SymbolicNeuralNetworks.SymbolicPullback(SymbolicNeuralNetwork(arch), FeedForwardLoss()) builds for any `n_integrators` -- four steps for two integrators in about 1.5 s, against a monolithic expression of 10⁹ terms that never finished -- and agrees with `Zygote` to machine precision. For an architecture built with `parameters` it is the *only* construction that builds. The monolithic one traces the chain from a plain vector, so every layer defaults its system parameters to `NullParameters` and the energy network's first `Dense` is then short of the components it reads; the build dies inside `Symbolics`. The layerwise construction is handed the pair and differentiates the map that was asked for. `ParametricLoss` declares its `loss_expression` so that the sweep can seed it -- the guess applies a loss in its four-argument form and this loss takes five, so without the declaration `loss_seed` would decline and fall back to the construction that cannot build. `_check_symbolic_pullback_is_tractable` keeps its `n_integrators > 1` refusal, because the two constructors in that file still build one expression for the whole network by hand -- and it now says where to go instead. It also guards `SymbolicPullback(arch)`, which had no guard and failed with an obscure `Symbolics` error. That one cannot move onto the layerwise construction at all: `HNNLoss` evaluates the pre-built Hamiltonian vector field rather than the chain, so there is no prediction for a layerwise seed to start from. `SymbolicPullback(nn, ::ParametricLoss, system_params)` can, and doing so would retire its concatenation of the system parameters onto the network input; that is #245's remaining half. One limitation stays, and it is the layer's own: the sweep runs the forward pass by *calling* each layer, so it inherits `SymplecticEuler`'s single-sample restriction -- the `@view(…)[:, 1]` here and the `@assert size(qp, 2) == 1` in `concatenate_array_with_parameters`. A batch has to be taken sample by sample, as `apply_parametric` already does. The monolithic construction never calls the layer at run time and so does not have this restriction. Needs SymbolicNeuralNetworks 0.7, which is not yet registered. Co-Authored-By: Claude Opus 5 (1M context) <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.
Resolves #245, given SymbolicNeuralNetworks #56.
What this does
SymbolicNeuralNetworkscomposes the pullback of a chain layer by layer, putting fresh symbolic variables between two layers instead of inlining one layer's expression into the next — which is what keeps the symbolic material a sum over layers rather than a product. Its seam was a plain vector, so it assumed every layer maps an array to an array.A
SymplecticEulerbuilt withreturn_parameters = truethreads the parameters of the system on to the next layer and therefore returns aTuple, which could not be seeded at all. Worse,composes_layerwisesaid the chain decomposes, solayerwise = :autocommitted to that path and then raised — SNN #54.SNN 0.7 widens the seam.
SymplecticEulernow carriesSymbolicEnergy'sparameter_lengthandparameter_layout— as a trailing type parameter, so every existing partial signature goes on matching with the layout left free — and implements the four methods that say how it meets the seam:carried_variables,seam_value,state_expressionsandseam_arguments. What it carries is data and never a differentiation target: the sensitivity pairs with the state, and the flattened system parameters become one more argument of each generated kernel.ParametricLossdeclares itsloss_expression, so the sweep can seed it. The construction guesses one by applying the loss in its four-argument form, and this loss takes five — without the declarationloss_seedwould decline and fall back to the construction that cannot build. The declaration restates_compute_loss, which is what the functor itself uses.Results
n_integrators = 1, no system parametersMethodErrorunder:autoZygoten_integrators = 2ZygoteParametricLoss, 2 integratorsZygoteThe parametrized row is the one worth dwelling on. The monolithic construction traces the chain from a plain vector, so every layer defaults its system parameters to
NullParametersand the energy network's firstDenseis then short of the components it reads; the build dies insideSymbolics. The layerwise construction is handed the pair(state, system parameters)and differentiates the map that was actually asked for. It is not the faster construction there — it is the only one.The tractability limit stays, and now says where to go
_check_symbolic_pullback_is_tractablekeeps itsn_integrators > 1refusal, because the two constructors insrc/pullbacks/symbolic_hnn_pullback.jlstill build one expression for the whole network by hand. It also guardsSymbolicPullback(arch), which had no guard at all and failed with an obscureSymbolicserror.That one cannot move onto the layerwise construction:
HNNLossevaluates the pre-built Hamiltonian vector field rather than the chain, so there is no prediction for a layerwise seed to start from.SymbolicPullback(nn, ::ParametricLoss, system_params)can, and doing so would also retire its concatenation of the system parameters onto the network input — that is #245's remaining half, left for a follow-up.Known limitation
The sweep runs the forward pass by calling each layer, so it inherits
SymplecticEuler's own single-sample restriction — the@view(…)[:, 1]in the layer and the@assert size(qp, 2) == 1inconcatenate_array_with_parameters. A batch has to be taken sample by sample, asapply_parametricalready does. The monolithic construction never calls the layer at run time and so does not have this restriction. Lifting it is layer work, independent of this PR.Testing
New
test/generalized_hamiltonian_neural_networks/layerwise_symbolic_pullback_test.jl, registered inruntests.jl. All five GHNN test files pass, including the four that existed before — the struct gained fields and a type parameter, so those are the regression check.Blocked on
SymbolicNeuralNetworks 0.7 is not yet registered. Verified locally with that tree
Pkg.developed in.🤖 Generated with Claude Code