MolecularDiffusion.modules.models.pmdm.encoders¶
Encoders used by the PMDM epsilon network.
Ported from PMDM’s models/encoders/{attention,edge,egnn,egnn_pytorch,schnet}.py,
keeping only what MDM_full_pocket_coor_shared reaches:
CrossAttentionBlock– upstreamBasicTransformerBlock, the ligand<->pocket token mixer.SchNetProteinEncoder– upstreamSchNetEncoder_protein, used for both the pocket and the ligand tower.MLPEdgeEncoder– upstreamMLPEdgeEncoder(edge_encoder: mlp).EGNNSparseNetwork– upstreamEGNN_Sparse_Network.
Two deliberate deviations from upstream:
No einops. Upstream’s attention uses
rearrangefor three reshapes; those are plainview/permutehere, so PMDM needs no new dependency.No ``MessagePassing``. Upstream’s
EGNN_Sparseoverridespropagateand reaches into PyG privates (self.inspector.distribute,_collect) that moved in PyG 2.5. The message/aggregate pair is written directly withscatter_add, which is whataggr="add"did anyway. Every graph fed to these layers is symmetric, so the source/target convention cannot flip a result.
Dropped as unreachable under this integration’s scope: the fourier-feature
branch (fourier_features=0), global linear attention
(global_linear_attn_every=0), the node/edge embedding-table branch
(empty dims), and upstream’s unused ligandemb/proteinemb/inner
atten_layer parameters in EGNN_Sparse_Network (the live code path is
h = z).
linker_mask (used by PMDMEpsNet.linker_sample – see
modules/tasks/diffusion_pmdm.py’s PMDMConstrainedGenerator,
mode: linker) zeros the coordinate-update contribution for every atom it
does not cover, biasing the EGNN toward moving only the region being
regenerated
(others/PMDM/models/encoders/egnn.py:268-274). Upstream additionally
concatenates a zero mask for the pocket half before this point, but that
pocket-half masking is a no-op on the coordinates this port returns (the
pocket rows of coors_out never receive +mhat_i regardless of the
mask), so only the ligand-sized mask is threaded through here.
Attributes¶
Classes¶
Upstream |
|
One E(n)-equivariant message-passing layer. |
|
Stack of |
|
Embed an edge length into |
|
Continuous-filter tower over a raw point cloud (its own radius graph). |
Module Contents¶
- class MolecularDiffusion.modules.models.pmdm.encoders.CrossAttentionBlock(dim: int, n_heads: int, d_head: int, dropout: float = 0.0, context_dim: int | None = None)¶
Bases:
torch.nn.ModuleUpstream
BasicTransformerBlock: self-attend both towers, then cross.- forward(x: torch.Tensor, context: torch.Tensor) Tuple[torch.Tensor, torch.Tensor]¶
- attn1¶
- attn2¶
- attn2p¶
- attn_p¶
- ff¶
- ffp¶
- norm1¶
- norm1p¶
- norm2¶
- norm2p¶
- norm3¶
- norm3p¶
- class MolecularDiffusion.modules.models.pmdm.encoders.EGNNSparseLayer(feats_dim: int, pos_dim: int = 3, edge_attr_dim: int = 0, m_dim: int = 16, soft_edge: int = 0, norm_feats: bool = False, norm_coors: bool = False, norm_coors_scale_init: float = 0.01, dropout: float = 0.0)¶
Bases:
torch.nn.ModuleOne E(n)-equivariant message-passing layer.
Only the ligand block of the coordinate update is applied: the pocket is a fixed conditioning cloud and must not move. Ligand nodes are the first
n_ligandrows of every tensor (the caller concatenates ligand-then-pocket), which is exactly upstream’s convention.- forward(x: torch.Tensor, edge_index: torch.Tensor, edge_attr: torch.Tensor | None, batch: torch.Tensor, n_ligand: int, linker_mask: torch.Tensor | None = None) torch.Tensor¶
- coors_mlp¶
- coors_norm¶
- edge_mlp¶
- edge_weight¶
- feats_dim¶
- m_dim = 16¶
- node_mlp¶
- node_norm¶
- pos_dim = 3¶
- soft_edge = 0¶
- class MolecularDiffusion.modules.models.pmdm.encoders.EGNNSparseNetwork(n_layers: int, feats_dim: int, pos_dim: int = 3, edge_attr_dim: int = 0, m_dim: int = 16, soft_edge: int = 0, norm_feats: bool = True, norm_coors: bool = False, norm_coors_scale_init: float = 0.01, dropout: float = 0.0)¶
Bases:
torch.nn.ModuleStack of
EGNNSparseLayerover the joined ligand+pocket cloud.Returns the ligand slice only:
(node_feats, coord_update), where the coordinate output is the displacement from the input positions (the equivariant score), as upstream does.- forward(z: torch.Tensor, pos: torch.Tensor, edge_index: torch.Tensor, edge_attr: torch.Tensor, batch: torch.Tensor, n_ligand: int, linker_mask: torch.Tensor | None = None) Tuple[torch.Tensor, torch.Tensor]¶
- feats_dim¶
- mpnn_layers¶
- pos_dim = 3¶
- class MolecularDiffusion.modules.models.pmdm.encoders.MLPEdgeEncoder(hidden_dim: int = 100, activation: str = 'relu')¶
Bases:
torch.nn.ModuleEmbed an edge length into
hidden_dim.Upstream also multiplies in a learned bond-type embedding, but both call sites in
MDM_full_pocket_coor_shared.netpassedge_type=None(the joined ligand+pocket graphs are purely geometric), leaving that embedding permanently untrained. It is dropped here rather than shipped as dead weight that DDP would then flag as an unused parameter.- forward(edge_length: torch.Tensor) torch.Tensor¶
- mlp¶
- class MolecularDiffusion.modules.models.pmdm.encoders.SchNetProteinEncoder(hidden_channels: int = 128, num_filters: int = 128, num_interactions: int = 6, edge_channels: int = 64, cutoff: float = 10.0, input_dim: int = 31)¶
Bases:
torch.nn.ModuleContinuous-filter tower over a raw point cloud (its own radius graph).
- forward(node_attr: torch.Tensor, pos: torch.Tensor, batch: torch.Tensor) torch.Tensor¶
- cutoff = 10.0¶
- distance_expansion¶
- emblin¶
- interactions¶
- MolecularDiffusion.modules.models.pmdm.encoders.SiLU¶