MolecularDiffusion.modules.models.pmdm.epsnet¶
PMDM’s pocket-conditioned epsilon network.
Port of models/epsnet/MDM_pocket_coor_shared.py::MDM_full_pocket_coor_shared
(the only network PMDM’s get_model can return), re-exported as
PMDMEpsNet.
Shape of the model, unchanged from upstream:
the pocket is embedded once by a SchNet tower and is never noised;
the ligand is the diffused variable – coordinates score-matching style, atom features VP style;
a cross-attention block mixes ligand and pocket tokens, then two EGNN stacks (global @
g_cutoff, local @cutoff) run over the concatenated ligand+pocket point set, with only ligand coordinates updated;two MLP heads produce
grad_{global,local}_node; the position score is the EGNN coordinate output itself.
Differences from upstream, all of them removals of code that this
integration’s scope (see docs/model_integrations/pmdm/INTEGRATION_PLAN.md)
puts out of reach:
the
vae_contextVAE latent branch and thecontextproperty list (both default-off upstream);atom_num_emb(default-off),is_sidechain(alwaysNone), and thegaussianedge encoder (config ismlp);self.model_global/model_local, which wereModuleListaliases of already-registered submodules and only duplicated state_dict keys.
The config object is replaced by explicit keyword arguments so the whole
network is Hydra-instantiable from configs/tasks/diffusion_pmdm.yaml.
Classes¶
Dual-encoder, pocket-conditioned score network. |
Functions¶
|
Upstream's noise schedules ( |
Module Contents¶
- class MolecularDiffusion.modules.models.pmdm.epsnet.PMDMEpsNet(num_atom: int = 10, protein_feature_dim: int = 31, hidden_dim: int = 128, protein_hidden_dim: int = 128, num_convs: int = 3, num_convs_local: int = 3, protein_num_convs: int = 2, cutoff: float = 3.0, g_cutoff: float = 6.0, encoder_cutoff: float = 6.0, edge_order: int = 3, mlp_act: str = 'relu', edge_encoder: str = 'mlp', soft_edge: bool = True, norm_coors: bool = True, beta_schedule: str = 'sigmoid', beta_start: float = 1e-07, beta_end: float = 0.002, num_diffusion_timesteps: int = 1000)¶
Bases:
torch.nn.ModuleDual-encoder, pocket-conditioned score network.
- forward(batch, return_unreduced_loss: bool = False)¶
Score-matching loss for one batch of pocket-ligand complexes.
batchis any attribute-access object carrying the keyspmdm_collateemits (aSimpleNamespacein practice).
- inpainting_sample(ligand_atom_type: torch.Tensor, ligand_pos_init: torch.Tensor, ligand_bond_index: torch.Tensor, ligand_bond_type: torch.Tensor | None, ligand_batch: torch.Tensor, frag_mask: torch.Tensor, protein_atom_feature_full: torch.Tensor, protein_pos: torch.Tensor, protein_batch: torch.Tensor, num_graphs: int, n_steps: int = 100, step_lr: float = 1e-06, clip: float = 1000.0, clip_local: float | None = None, clip_pos: float | None = None, global_start_sigma: float = float('inf'), local_start_sigma: float = float('inf'), w_global_pos: float = 1.0, w_global_node: float = 1.0, w_local_pos: float = 1.0, w_local_node: float = 1.0, sampling_type: str = 'generalized', eta: float = 1.0, keep_traj: bool = False) Tuple[torch.Tensor, List[torch.Tensor], torch.Tensor, List[torch.Tensor]]¶
RePaint-style constrained sampling (“lead optimisation” upstream): keep
frag_maskatoms of a starting ligand fixed, regenerate the rest, inside the same fixed pocket.frag_mask(bool,(n_ligand,)):True= keep exactly as given inligand_pos_init/ligand_atom_type;False= regenerate. Every reverse step re-noises the kept fragment’s atom type to the current timestep before scoring, then force-restores both its position and type afterward – classic RePaint. Position is never re-noised (upstream’s matching line is dead/commented-out code, not ported). Port of upstreaminpainting_sample,MDM_pocket_coor_shared.py:929-1183.
- langevin_dynamics_sample(ligand_atom_type: torch.Tensor, ligand_pos_init: torch.Tensor, ligand_bond_index: torch.Tensor, ligand_bond_type: torch.Tensor | None, ligand_batch: torch.Tensor, protein_atom_feature_full: torch.Tensor, protein_pos: torch.Tensor, protein_batch: torch.Tensor, num_graphs: int, n_steps: int = 100, step_lr: float = 1e-06, clip: float = 1000.0, clip_local: float | None = None, clip_pos: float | None = None, global_start_sigma: float = float('inf'), local_start_sigma: float = float('inf'), w_global_pos: float = 1.0, w_global_node: float = 1.0, w_local_pos: float = 1.0, w_local_node: float = 1.0, sampling_type: str = 'generalized', eta: float = 1.0, keep_traj: bool = False) Tuple[torch.Tensor, List[torch.Tensor], torch.Tensor, List[torch.Tensor]]¶
Reverse process inside a fixed pocket.
Returns
(ligand_pos, pos_traj, ligand_atom_type, atom_traj), flat and concatenated (not padded). Final coordinates are translated back into the input pocket’s frame.
- linker_sample(ligand_atom_type: torch.Tensor, ligand_pos_init: torch.Tensor, ligand_bond_index: torch.Tensor, ligand_bond_type: torch.Tensor | None, ligand_batch: torch.Tensor, frag_mask: torch.Tensor, protein_atom_feature_full: torch.Tensor, protein_pos: torch.Tensor, protein_batch: torch.Tensor, num_graphs: int, n_steps: int = 100, step_lr: float = 1e-06, clip: float = 1000.0, clip_local: float | None = None, clip_pos: float | None = None, global_start_sigma: float = float('inf'), local_start_sigma: float = float('inf'), w_global_pos: float = 1.0, w_global_node: float = 1.0, w_local_pos: float = 1.0, w_local_node: float = 1.0, sampling_type: str = 'generalized', eta: float = 1.0, keep_traj: bool = False) Tuple[torch.Tensor, List[torch.Tensor], torch.Tensor, List[torch.Tensor]]¶
RePaint-style constrained sampling for linker design: keep two (or more) disjoint fragments of a starting ligand fixed (
frag_mask), regenerate the region between them, inside the same fixed pocket.Structurally identical to
inpainting_sample()(same restore-after-every-step loop), with two differences: the kept fragment is fed to the network exactly as given, with no per-step forward-noise on its atom type (upstream computes a matching noise tensor here but never uses it – dead code, not ported); and the network additionally receiveslinker_mask(the region being regenerated), which biases its coordinate update toward that region (seeencoders.py). Port of upstreamlinker_sample,MDM_pocket_coor_shared.py:1186-1425.
- net(ligand_atom_type: torch.Tensor, ligand_pos: torch.Tensor, ligand_bond_index: torch.Tensor, ligand_bond_type: torch.Tensor | None, ligand_batch: torch.Tensor, protein_embeddings: torch.Tensor, protein_pos: torch.Tensor, protein_batch: torch.Tensor, time_step: torch.Tensor, linker_mask: torch.Tensor | None = None) Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]¶
One denoiser pass. Returns the global/local position and node scores plus the ligand edge bookkeeping the loss needs.
linker_mask(bool,(n_ligand,),True= regenerate): used only bylinker_sample(), to bias the EGNN coordinate update toward the region being regenerated (seeencoders.py’s module docstring).None(default) reproduces training/default-sampling behaviour exactly.
- T¶
- alphas¶
- atten_layer¶
- betas¶
- cutoff = 3.0¶
- edge_encoder_global¶
- edge_encoder_local¶
- edge_order = 3¶
- encoder_global¶
- encoder_local¶
- g_cutoff = 6.0¶
- grad_global_node_mlp¶
- grad_local_node_mlp¶
- ligand_encoder¶
- num_atom = 10¶
- num_timesteps¶
- protein_encoder¶
- temb¶
- temb_proj¶