MolecularDiffusion.modules.models.tabasco_equiformer.equiformer_backbone¶
EquiformerV2 backbone for TABASCO’s flow-matching model.
Wraps MolecularDiffusion.modules.models.shepherd_arch.equiformer_v2_encoder.
EquiformerV2 (the SO(3) tensor-product transformer encoder, equiformer_v2_s
layer family – the same encoder class EquiformerV2_dynamics,
EquiformerV2Backbone, and shepherd_arch already use, imported here
unmodified) so it can be substituted 1-for-1 for TABASCO’s
TransformerModule (modules/layers/tabasco/transformer_module.py) with
no change to FlowMatchingModel, the interpolants, the Euler-Maruyama
sampler, or the pointcloud<->TensorDict adapters.
Backbone contract (fixed by FlowMatchingModel._call_net,
modules/models/tabasco/flow_model.py:84-97):
forward(coords, atomics, padding_mask, t) -> (coords, atom_logits)
coords returned is the direct endpoint prediction x_1 – both
CenteredMetricInterpolant.compute_loss and SDEMetricInterpolant.step
(modules/models/tabasco/flow/interpolate.py) treat pred["coords"] as
an x_1 estimate, not a velocity/residual.
Mask convention: padding_mask follows TABASCO’s own inverted convention
(1 = padded, see PointCloudToTensorDictAdapter,
modules/tasks/diffusion_tabasco.py:60-113) – the opposite of the
platform’s usual node_mask (1 = real). This class inverts once at the
boundary, the same idiom EGNNBackbone/GVPBackbone already use.
Six-step mechanism (see docs/model_novel/tabasco_equiformer/INTEGRATION_PLAN.md, “Hypothesis” / “Integration Plan”):
Compact the padded
(B, N, ...)batch to its real (unmasked) atoms and build a per-molecule fully-connected, no-self-loop edge index – reusing the exact valid-node-compaction and edge-construction patternEquiformerV2_dynamics._forward_denseimplements for a dense pointcloud batch (modules/models/equiformer_v2_dynamics.py:266-343). Reproduced locally (a two-line body, boilerplate index bookkeeping, not a layer/schedule) rather than importingEquiformerV2_dynamicsitself, which this backbone otherwise never touches.Project one-hot atom type + scalar time
t(+ concat-routed conditioning, if any) into the l=0 (scalar) channel of a freshSO3_Embedding, following_build_so3_input’s pattern (equiformer_v2_dynamics.py:118-142). Adapter-routed conditioning is a separate single additive injection into l=0 after this projection, mirroringEquiformerV2_dynamics’s ownadapter_proj.Run the wrapped
EquiformerV2encoder over the compacted graph.Read a per-atom displacement
doff the l=1 channel via aFeedForwardNetworkSO3 head (same construction asEquiformerV2_dynamics.head_vel_ffn, including its ownSO3_Grid). Deliberate, load-bearing departure from ``EquiformerV2_dynamics``’s own convention: returnscoords_in + das the endpoint prediction, notdalone, and does not callremove_mean_pyG.dis built purely fromedge_distance_vec(translation-invariant relative geometry), so on its own it carries no absolute position information – it is a displacement, not an endpoint.EquiformerV2_dynamicsreturns it bare because EDM’s dynamics contract wants a velocity/noise prediction added externally by the caller; TABASCO’s contract instead wants the endpoint directly. The ground-truthx_1target is already COM-centered bymask_and_zero_combefore the MSE loss is computed, so – exactly as neitherEGNNBackbonenorGVPBackboneneeded a forced centering step – the loss alone is sufficient to teach a centered output.Read the l=0 scalar channel through a fresh
nn.Linear(sphere_channels, atom_dim)into atom-type logits – NOTEquiformerV2_dynamics.head_h, which is sized for EDM’sin_node_nffeature convention.Scatter both outputs back into the padded
(B, N, 3)/(B, N, atom_dim)shape, mirroring_forward_dense’s own scatter-back step.
Classes¶
TABASCO-compatible net: |
Module Contents¶
- class MolecularDiffusion.modules.models.tabasco_equiformer.equiformer_backbone.EquiformerV2TabascoBackbone(atom_dim: int, sphere_channels: int = 128, input_sphere_channels: int = 128, num_layers: int = 8, lmax_list: List[int] | None = None, mmax_list: List[int] | None = None, grid_resolution: int = 18, num_sphere_samples: int = 128, attn_hidden_channels: int = 64, attn_alpha_channels: int = 64, attn_value_channels: int = 16, ffn_hidden_channels: int = 128, num_heads: int = 8, norm_type: str = 'layer_norm_sh', edge_channels: int = 128, use_atom_edge_embedding: bool = True, share_atom_edge_embedding: bool = False, use_m_share_rad: bool = False, distance_function: str = 'gaussian', num_distance_basis: int = 512, attn_activation: str = 'silu', use_s2_act_attn: bool = False, use_attn_renorm: bool = True, ffn_activation: str = 'silu', use_gate_act: bool = False, use_grid_mlp: bool = True, use_sep_s2_act: bool = True, alpha_drop: float = 0.1, drop_path_rate: float = 0.1, proj_drop: float = 0.0, cutoff: float = 9.0, weight_init: str = 'uniform', adapter_indices: List[int] | None = None, concat_indices: List[int] | None = None)¶
Bases:
torch.nn.ModuleTABASCO-compatible net:
(coords, atomics, padding_mask, t) -> (coords, atom_logits).Architecture kwargs below default to the exact values
configs/tasks/diffusion_equiformer.yamluses (this platform’s own proven QM9-scale EquiformerV2 config) – see the ledger’s “Hyperparameter Provenance” table.- forward(coords: torch.Tensor, atomics: torch.Tensor, padding_mask: torch.Tensor, t: torch.Tensor, condition: torch.Tensor | None = None) Tuple[torch.Tensor, torch.Tensor]¶
- Parameters:
coords – (B, N, 3)
atomics – (B, N, atom_dim) one-hot (or soft) atom-type features
padding_mask – (B, N), TABASCO convention – 1 = padded, 0 = real
t – (B,) timestep in [0, 1]
condition – (B, N, n_adapter_context + n_concat_context) or None
- Returns:
(B, N, 3) endpoint prediction atom_logits: (B, N, atom_dim)
- Return type:
- adapter_indices = []¶
- atom_dim¶
- concat_indices = []¶
- equiformer¶
- head_atom¶
- head_disp_ffn¶
- input_proj¶
- lmax_list = None¶
- mmax_list = None¶
- so3_grid¶
- sphere_channels = 128¶