Source code for neural_lam.utils.networks
"""Constructors for neural-network building blocks."""
# Third-party
import torch
import torch_geometric as pyg
from torch import nn
[docs]
def make_mlp(blueprint: list[int], layer_norm: bool = True) -> nn.Sequential:
"""
Construct a multilayer perceptron from a blueprint of layer widths.
Parameters
----------
blueprint : list[int]
Sequence of layer dimensions where ``blueprint[0]`` is the input size,
``blueprint[-1]`` is the output size, the intermediate entries specify
the hidden layer widths, and ``len(blueprint) - 2`` is the number of
hidden layers.
layer_norm : bool, optional
If ``True``, append a ``LayerNorm`` to the output as in GraphCast.
Returns
-------
torch.nn.Sequential
Sequential module implementing the specified MLP.
"""
hidden_layers = len(blueprint) - 2
assert hidden_layers >= 0, "Invalid MLP blueprint"
layers = []
for layer_i, (dim1, dim2) in enumerate(zip(blueprint[:-1], blueprint[1:])):
layers.append(nn.Linear(dim1, dim2))
if layer_i != hidden_layers:
layers.append(nn.SiLU()) # Swish activation
# Optionally add layer norm to output
if layer_norm:
layers.append(nn.LayerNorm(blueprint[-1]))
return nn.Sequential(*layers)
[docs]
def make_gnn_seq(
edge_index: torch.Tensor,
num_gnn_layers: int,
hidden_layers: int,
hidden_dim: int,
gnn_type: str = "InteractionNet",
) -> nn.Module:
"""
Build a sequential stack of GNN layers that propagates both node and
edge representations.
All layer types share the ``(send, rec, edge) -> (rec, edge)``
interface, so the stack can be applied as a single module.
Parameters
----------
edge_index : torch.Tensor
Shape ``(2, M)``. Edge index of the edges that the GNN layers
operate on.
num_gnn_layers : int
Number of stacked GNN layers; must be at least 1. Callers that
want a no-op stage (e.g. zero intra-level layers) should skip
building and applying the stack rather than calling this with 0.
hidden_layers : int
Number of hidden layers in the MLPs of each GNN layer.
hidden_dim : int
Dimensionality of node and edge representations.
gnn_type : str
GNN layer type, any key in ``gnn_layers.GNN_TYPES``.
Returns
-------
pyg.nn.Sequential
Sequential module mapping ``(mesh_rep, edge_rep)`` to updated
``(mesh_rep, edge_rep)``.
Raises
------
ValueError
If ``num_gnn_layers`` is less than 1.
"""
# First-party
from neural_lam.gnn_layers import get_gnn_class
if num_gnn_layers < 1:
raise ValueError(
"make_gnn_seq requires num_gnn_layers >= 1 "
f"(got {num_gnn_layers}); skip the stage for a no-op."
)
gnn_class = get_gnn_class(gnn_type)
return pyg.nn.Sequential(
"mesh_rep, edge_rep",
[
(
gnn_class(
edge_index,
hidden_dim,
hidden_layers=hidden_layers,
),
"mesh_rep, mesh_rep, edge_rep -> mesh_rep, edge_rep",
)
for _ in range(num_gnn_layers)
],
)