Source code for neural_lam.models.latent.base_encoder
"""Abstract base class for latent encoders."""
# Standard library
from typing import Any
# Third-party
import torch
from torch import distributions as tdists
from torch import nn
[docs]
class BaseLatentEncoder(nn.Module):
"""
Abstract encoder mapping an input grid representation to a Gaussian
distribution over a latent variable defined on mesh nodes.
Subclasses implement :meth:`compute_dist_params`, which returns the raw
parameters used to build the output distribution. With
``output_dist="isotropic"`` only the mean is produced (unit variance);
with ``output_dist="diagonal"`` both mean and a positive std are output.
"""
def __init__(self, latent_dim: int, output_dist: str = "isotropic") -> None:
"""
Set up output dimensionality for the chosen distribution type.
Parameters
----------
latent_dim : int
Dimensionality of the latent variable at each mesh node.
output_dist : str
Type of output distribution: ``"isotropic"`` (mean only, unit
variance) or ``"diagonal"`` (mean and per-dimension std).
"""
super().__init__()
self.output_dist = output_dist
if output_dist == "isotropic":
self.output_dim = latent_dim
elif output_dist == "diagonal":
self.output_dim = 2 * latent_dim
# Small floor to prevent the encoder from collapsing to std 0
self.latent_std_eps = 1e-4
else:
raise ValueError(
f"Unknown encoder output distribution: {output_dist}"
)
[docs]
def compute_dist_params(
self, grid_rep: torch.Tensor, *args: Any, **kwargs: Any
) -> torch.Tensor:
"""
Compute raw distribution parameters from the grid representation.
Parameters
----------
grid_rep : torch.Tensor
Shape ``(B, num_grid_nodes, d_h)``. Grid input representation.
**kwargs
Additional inputs used by concrete encoders (e.g. graph
embeddings).
Returns
-------
torch.Tensor
Shape ``(B, num_mesh_nodes, output_dim)``. Raw parameters of
the latent distribution.
"""
raise NotImplementedError("compute_dist_params not implemented")
[docs]
def forward(self, grid_rep: torch.Tensor, **kwargs: Any) -> tdists.Normal:
"""
Compute the Gaussian distribution over the latent variable.
Parameters
----------
grid_rep : torch.Tensor
Shape ``(B, num_grid_nodes, d_h)``. Grid input representation.
**kwargs
Additional inputs forwarded to :meth:`compute_dist_params`.
Returns
-------
torch.distributions.Normal
Distribution over the latent variable, with batch shape
``(B, num_mesh_nodes, latent_dim)``.
"""
latent_dist_params = self.compute_dist_params(grid_rep, **kwargs)
if self.output_dist == "diagonal":
latent_mean, latent_std_raw = latent_dist_params.chunk(2, dim=-1)
latent_std = self.latent_std_eps + nn.functional.softplus(
latent_std_raw
)
else:
latent_mean = latent_dist_params
latent_std = torch.ones_like(latent_mean)
return tdists.Normal(latent_mean, latent_std)