Neural Network Integrators
In GeometricMachineLearning we can divide most neural network architectures (that are used for applications to physical systems) into two categories: autoencoders and integrators. This is also closely related to the application of reduced order modeling where autoencoders are used in the offline phase and integrators are used in the online phase.
The term integrator in its most general form refers to an approximation of the flow of an ODE by a numerical scheme. Traditionally, for so called one-step methods, these numerical schemes are constructed by defining certain relationships between a known time step $z^{(t)}$ and a future unknown one $z^{(t+1)}$ [1, 63]:
\[ f(z^{(t)}, z^{(t+1)}) = 0.\]
One usually refers to such a relationship as an integration scheme. If this relationship can be reformulated as
\[ z^{(t+1)} = g(z^{(t)}),\]
then we refer to the scheme as explicit, if it cannot be reformulated in such a way then we refer to it as implicit. Implicit schemes are typically more expensive to solve than explicit ones. The Julia library GeometricIntegrators [2] offers a wide variety of integration schemes both implicit and explicit.
The neural network integrators in GeometricMachineLearning (the corresponding type is NeuralNetworkIntegrator) are all explicit integration schemes where the function $g$ above is modeled with a neural network.
Neural networks, as an alternative to traditional methods, are employed because of (i) potentially superior performance and (ii) an ability to learn unknown dynamics from data.
The simplest of such a neural network for modeling an explicit integrator is the ResNet. SympNets can be seen as the symplectic version of the ResNet. There is an example demonstrating the performance of SympNets. This example demonstrates the advantages of symplectic neural networks.
Multi-step methods
Multi-step method [37, 38] refers to schemes that are of the form[1]:
\[ f(z^{(t - \mathtt{sl} + 1)}, z^{(t - \mathtt{sl} + 2)}, \ldots, z^{(t)}, z^{(t + 1)}, \ldots, z^{(\mathtt{pw} + 1)}) = 0,\]
where sl is short for sequence length and pw is short for prediction window. Note that we can recover traditional one-step methods by setting sl and pw equal to 1. We can also formulate explicit mulit-step methods. They are of the form:
\[[z^{(t+1)}, \ldots, z^{(t+\mathtt{pw})}] = g(z^{(t - \mathtt{sl} + 1)}, \ldots, z^{(t)}).\]
In GeometricMachineLearning all multi-step methods, as is the case with one-step methods, are explicit. There are essentially two ways to construct multi-step methods with neural networks: the older one is using recurrent neural networks such as long short-term memory cells (LSTMs) [64] and the newer one is using transformer neural networks [33]. Both of these approaches have been successfully employed to learn multi-step methods (see [41, 42] for the former and [4, 65, 66] for the latter), but because the transformer architecture exhibits superior performance on modern hardware and can be imbued with geometric properties we almost always use a transformer-derived architecture when dealing with time series[2].
Explicit multi-step methods derived from the transformer are always subtypes of the type TransformerIntegrator in GeometricMachineLearning. In GeometricMachineLearning the standard transformer, the volume-preserving transformer and the linear symplectic transformer are implemented.
For standard multi-step methods (that are not neural network-based) sl is generally a number greater than one whereas pw = 1 in most cases. For the TransformerIntegrators in GeometricMachineLearning however we usually have:
\[ \mathtt{pw} = \mathtt{sl},\]
so the number of vectors in the input sequence is equal to the number of vectors in the output sequence. This makes it easier to define structure-preservation for these architectures and improves stability.
Library Functions
GeometricMachineLearning.NeuralNetworkIntegrator — Type
NeuralNetworkIntegrator is a super type of various neural network architectures such as SympNet and ResNet.
The purpose of such neural networks is to approximate the flow of an ordinary differential equation (ODE).
NeuralNetworkIntegrators can be seen as modeling traditional one-step methods with neural networks, i.e. for a fixed time step they perform:
\[ \mathtt{NeuralNetworkIntegrator}: z^{(t)} \mapsto z^{(t+1)},\]
to try to approximate the flow of some ODE:
\[ || \mathtt{Integrator}(z^{(t)}) - \varphi^h(z^{(t)}) || \approx \mathcal{O}(h),\]
where $\varphi^h$ is the flow map of the ODE for a time step $h$.
GeometricMachineLearning.ResNet — Type
ResNet(dim, n_blocks, activation)
ResNet(dim, n_blocks, width, activation)Make an instance of a ResNet.
A ResNet is a neural network that realizes a mapping of the form:
\[ x = \mathcal{NN}(x) + x,\]
so the input is again added to the output (a so-called add connection). In GeometricMachineLearning the specific ResNet that we use consists of a series of simple ResNetLayers, or of WideResNetLayers where width differs from dim: that is the one structural choice the architecture makes, and a width equal to dim keeps the plain ResNetLayer.
Arguments
dim::Integer: the system dimension.n_blocks::Integer: the number of blocks before the linear output layer.width::Integer: the width of the hidden layer of each block, which isdiminResNet(dim, n_blocks, activation)and the third positional argument ofResNet(dim, n_blocks, width, activation)and ofResNet(dl, n_blocks, width), where it defaults todl.input_dim. SeeWideResNetLayer.activation: the third positional argument ofResNet(dim, n_blocks, activation)and the fourth ofResNet(dim, n_blocks, width, activation), whereResNet(dl, n_blocks, width)takes it as the keywordactivation = tanh. It applies to each of then_blocksblocks; the linear output layer applies the identity.
Constructor
ResNet can also be called with the constructor:
ResNet(dl, n_blocks, width)where dl is an instance of DataLoader and width defaults to dl.input_dim.
See integrate for an example of this.
For a ResNet whose blocks also take the parameters of the system, see ParametricResNet.
GeometricMachineLearning.WideResNetLayer — Type
WideResNetLayer(dim, width, activation)A ResNetLayer whose hidden layer is width wide, independent of dim.
ResNetLayer applies a single $\mathrm{dim} \times \mathrm{dim}$ weight, so its capacity is tied to the system dimension. This one upscales to width, applies the activation, and downscales again:
\[ x \mapsto x + \sigma(W_\mathrm{down}\sigma(W_\mathrm{up}x + b_\mathrm{up}) + b),\]
with $W_\mathrm{up}\in\mathbb{R}^{\mathrm{width}\times\mathrm{dim}}$ and $W_\mathrm{down}\in\mathbb{R}^{\mathrm{dim}\times\mathrm{width}}$. ResNet uses it whenever the width it is given differs from the system dimension, and keeps the plain ResNetLayer where the two agree.
The input is a vector, a matrix of samples, a tensor of samples, or a (q, p) pair, and the map is the same in every form.
Also see ParametricResNetLayer, which additionally takes the parameters of the system.
GeometricMachineLearning.ResNetLayer — Type
ResNetLayer(dim)Make an instance of the resnet layer.
The ResNetLayer is a simple feedforward neural network to which we add the input after applying it, i.e. it realizes $x \mapsto x + \sigma(Ax + b)$.
Arguments
The ResNet layer takes the following arguments:
dim::Integer: the system dimension.activation = identity: The activation function.
The following is a keyword argument:
use_bias::Bool = true: This determines whether a bias $b$ is used.
GeometricMachineLearning.ParametricResNet — Type
ParametricResNet(dim; width, n_blocks, activation, parameters)A ResNet whose blocks also take the parameters of the system, built from ParametricResNetLayers.
The architecture without structure preservation that the generalized Hamiltonian neural network of HamiltonianNeuralNetworks.jl is compared against: it maps $(x, \mu) \mapsto x'$ with the same information available to it, but nothing in it makes the map symplectic. Unlike ResNet it uses a ParametricResNetLayer at every width, and the linear output layer applies the identity activation.
Keyword arguments
width = dim: the width of the hidden layer of each block.n_blocks = 1: the number of blocks before the linear one.activation = tanh: the activation of every block.parameters = NullParameters(): aNamedTupleof system parameters, used only for its shape.
Constructor
ParametricResNet can also be called with the constructor
ParametricResNet(dl, n_blocks, width)where dl is an instance of DataLoader and width defaults to dl.input_dim, or an instance of ParametricDataLoader. The latter gives the system dimension, the rows of its data or of q and p together, which is also the default of width, and the system parameters, whose first μ the architecture keeps for its shape; it therefore takes no parameters keyword.
The forward pass is apply_parametric, or the functor nn(x, μ) of the NeuralNetwork built around it.
GeometricMachineLearning.ParametricResNetLayer — Type
ParametricResNetLayer(dim, width, activation; parameters, return_parameters)A WideResNetLayer whose hidden layer also sees the parameters of the system.
The flattened system parameters are appended to the input of the upscaling weight, so the layer computes
\[ x \mapsto x + \sigma(W_\mathrm{down}\sigma(W_\mathrm{up}[x; \mu] + b_\mathrm{up}) + b),\]
where $\mu$ are the system parameters. $W_\mathrm{up}$ is therefore $\mathrm{width}\times(\mathrm{dim} + |\mu|)$ wide, and without system parameters the map is that of a WideResNetLayer with the same width and weights. The input is a vector, a matrix of samples, a tensor of samples, or a (q, p) pair of any of these, with one μ — the forms that WideResNetLayer takes.
Keyword arguments
parameters = NullParameters(): aNamedTupleof system parameters, used only for its shape — the layer stores the resultingNeuralNetworkParameters.ParameterLayoutand the flattened length, and the values are supplied per call.return_parameters = false: whether to pass the system parameters on to the next layer beside the output, which is what lets aChainof these thread them through. AChainof one layer only takes the default.
This is the building block of ParametricResNet.
GeometricMachineLearning.flatten_system_parameters — Function
flatten_system_parameters(μ::NamedTuple)
flatten_system_parameters(::NullParameters)
flatten_system_parameters(::Type{T}, μ::NamedTuple)
flatten_system_parameters(::Type{T}, ::NullParameters)Return the tuple (v, layout) of the flat Vector v of the system parameters μ and the ParameterLayout that puts such a vector back into the shape of μ. It is the flatten of NeuralNetworkParameters of μ, and of the empty NamedTuple() for NullParameters().
With T, the element type of v is T; without it, it is the one element type of the leaves of μ, and Union{} for NullParameters() and (;). NullParameters() flattens to an empty vector. The layout is a value and not a closure, so a layer can store it in a field.
The function throws no error of its own; the errors of flatten propagate. It throws an ArgumentError for a leaf of μ that flatten cannot copy, such as a String, and, without T, for leaves of two element types. With T, it throws an InexactError for a value that T cannot hold, such as 0.5 for T = Int.
GeometricMachineLearning.concatenate_array_with_parameters — Function
concatenate_array_with_parameters(x::AbstractArray, ::NullParameters)
concatenate_array_with_parameters(x::AbstractVector, μ::NamedTuple)
concatenate_array_with_parameters(x::AbstractMatrix, μ::NamedTuple)
concatenate_array_with_parameters(x::AbstractArray{<:Any, 3}, μ::NamedTuple)
concatenate_array_with_parameters(x::AbstractMatrix, μs::AbstractVector)Return the input of a parametric layer: the state x with the flattened system parameters below it, in the element type of x.
- With
NullParameters(), returnxitself. - With one
μ, flatten it withflatten_system_parameters(eltype(x), μ), and put it below a vector, below every column of a matrix, and below every column of every slice of a 3-tensor. The result has as many more rows thanxas the flatμhas entries. - With a vector
μs, putμs[i]below columniof the matrixx, and return the columns side by side.
Throws a DimensionMismatch when the number of columns of x is not length(μs), an ArgumentError for an empty μs with a matrix of no columns, and an InexactError when a parameter has no value in the element type of x, such as 0.5 below an integer state. An error of flatten_system_parameters propagates. An array of more than three dimensions with a NamedTuple, and a vector or an array of three or more dimensions with a vector of parameters, have no method.
GeometricBase.integrate — Method
integrate(nn, ics)This function computes a trajectory for a NeuralNetworkIntegrator that has already been trained for valuation purposes.
It takes as input:
nn: aNeuralNetwork(that has been trained).ics: initial conditions (aNamedTupleof two vectors)
Examples
To demonstrate integrate we use a simple ResNet that does:
\[\mathrm{ResNet}: x \mapsto \begin{pmatrix} 1 & 0 & 0 \\ 0 & 2 & 0 \\ 0 & 0 & 1\end{pmatrix}x + \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix}\]
and we iterate three times with
\[ \mathtt{ics} = \begin{pmatrix} 1 \\ 1 \\ 1 \end{pmatrix}.\]
using GeometricMachineLearning
model = ResNet(3, 0, identity)
weight = [1 0 0; 0 2 0; 0 0 1]
bias = [0, 0, 1]
ps = NetworkParameters((L1 = (weight = weight, bias = bias), ))
nn = NeuralNetwork(model, Chain(model), ps, CPU())
ics = [1, 1, 1]
integrate(nn, ics; n_points = 4)
# output
3×4 Matrix{Int64}:
1 2 4 8
1 3 9 27
1 3 7 15Arguments
The optional keyword argument is
n_points = 100
The number of integration steps that should be performed.
GeometricMachineLearning.TransformerIntegrator — Type
TransformerIntegrator <: ArchitectureEncompasses various transformer architectures, such as the VolumePreservingTransformer and the LinearSymplecticTransformer.
The central idea behind this is to construct an explicit multi-step integrator:
\[ \mathtt{Integrator}: [ z^{(t - \mathtt{sl} + 1)}, z^{(t - \mathtt{sl} + 2)}, \ldots, z^{(t)} ] \mapsto [ z^{(t + 1)}, z^{(t + 2)}, \ldots, z^{(t + \mathtt{pw})} ],\]
where sl stands for sequence length and pw stands for prediction window, so the numbers of input and output vectors respectively.
GeometricBase.integrate — Method
integrate(nn, ics)Iterate the neural network of type TransformerIntegrator for initial conditions ics.
The initial condition is a matrix $\in\mathbb{R}^{n\times\mathtt{seq\_length}}$ or NamedTuple of two matrices).
This function computes a trajectory for a Transformer that has already been trained for valuation purposes.
Parameters
The following are optional keyword arguments:
n_points::Int=100: The number of time steps for which we run the prediction.prediction_window::Int=size(ics.q, 2): The prediction window (i.e. the number of steps we predict into the future) is equal to the sequence length (i.e. the number of input time steps) by default.
References
- [1]
- E. Hairer, C. Lubich and G. Wanner. Geometric Numerical integration: structure-preserving algorithms for ordinary differential equations (Springer, Heidelberg, 2006).
- [63]
- B. Leimkuhler and S. Reich. Simulating hamiltonian dynamics. No. 14 (Cambridge university press, 2004).
- [2]
- [109]
- K. Feng. The step-transition operators for multi-step methods of ODE's. Journal of Computational Mathematics, 193–202 (1998).
- [33]
- A. Vaswani, N. Shazeer, N. Parmar, J. Uszkoreit, L. Jones, A. N. Gomez, L. Kaiser and I. Polosukhin. Attention is all you need. Advances in neural information processing systems 30 (2017).
- [65]
- A. Hemmasian and A. Barati Farimani. Reduced-order modeling of fluid flows with transformers. Physics of Fluids 35 (2023).
- [66]
- A. Solera-Rico, C. S. Vila, M. Gómez, Y. Wang, A. Almashjary, S. Dawson and R. Vinuesa, $\beta$-Variational autoencoders and transformers for reduced-order modelling of fluid flows, arXiv preprint arXiv:2304.03571 (2023).
- [4]
- B. Brantner, M. Kraus, Z. Li and G. de Romemont. Volume-preserving transformers for learning time series data with structure. ESAIM: Proceedings and Surveys 81, 123–144 (2025).