| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import math |
| import sys |
| import torch |
| import torch.nn as nn |
| from typing import Tuple, Sequence, Optional |
| from functools import partial |
| from abc import ABC, abstractmethod |
|
|
| from model.openfold.primitives import Linear, LayerNorm |
| from model.openfold.dropout import DropoutRowwise, DropoutColumnwise |
| from model.openfold.msa import ( |
| MSARowAttentionWithPairBias, |
| MSAColumnAttention, |
| MSAColumnGlobalAttention, |
| ) |
| from model.openfold.outer_product_mean import OuterProductMean |
| from model.openfold.pair_transition import PairTransition |
| from model.openfold.triangular_attention import ( |
| TriangleAttention, |
| TriangleAttentionStartingNode, |
| TriangleAttentionEndingNode, |
| ) |
| from model.openfold.triangular_multiplicative_update import ( |
| TriangleMultiplicationOutgoing, |
| TriangleMultiplicationIncoming, |
| FusedTriangleMultiplicationIncoming, |
| FusedTriangleMultiplicationOutgoing |
| ) |
| from onescience.utils.openfold.checkpointing import checkpoint_blocks, get_checkpoint_fn |
| from onescience.utils.openfold.chunk_utils import chunk_layer, ChunkSizeTuner |
| from onescience.utils.openfold.tensor_utils import add |
|
|
|
|
| class MSATransition(nn.Module): |
| """ |
| Feed-forward network applied to MSA activations after attention. |
| |
| Implements Algorithm 9 |
| """ |
| def __init__(self, c_m, n): |
| """ |
| Args: |
| c_m: |
| MSA channel dimension |
| n: |
| Factor multiplied to c_m to obtain the hidden channel |
| dimension |
| """ |
| super(MSATransition, self).__init__() |
|
|
| self.c_m = c_m |
| self.n = n |
|
|
| self.layer_norm = LayerNorm(self.c_m) |
| self.linear_1 = Linear(self.c_m, self.n * self.c_m, init="relu") |
| self.relu = nn.ReLU() |
| self.linear_2 = Linear(self.n * self.c_m, self.c_m, init="final") |
|
|
| def _transition(self, m, mask): |
| m = self.layer_norm(m) |
| m = self.linear_1(m) |
| m = self.relu(m) |
| m = self.linear_2(m) * mask |
| return m |
|
|
| @torch.jit.ignore |
| def _chunk(self, |
| m: torch.Tensor, |
| mask: torch.Tensor, |
| chunk_size: int, |
| ) -> torch.Tensor: |
| return chunk_layer( |
| self._transition, |
| {"m": m, "mask": mask}, |
| chunk_size=chunk_size, |
| no_batch_dims=len(m.shape[:-2]), |
| ) |
|
|
| def forward( |
| self, |
| m: torch.Tensor, |
| mask: Optional[torch.Tensor] = None, |
| chunk_size: Optional[int] = None, |
| ) -> torch.Tensor: |
| """ |
| Args: |
| m: |
| [*, N_seq, N_res, C_m] MSA activation |
| mask: |
| [*, N_seq, N_res, C_m] MSA mask |
| Returns: |
| m: |
| [*, N_seq, N_res, C_m] MSA activation update |
| """ |
| |
| if mask is None: |
| mask = m.new_ones(m.shape[:-1]) |
|
|
| mask = mask.unsqueeze(-1) |
|
|
| if chunk_size is not None: |
| m = self._chunk(m, mask, chunk_size) |
| else: |
| m = self._transition(m, mask) |
|
|
| return m |
|
|
|
|
| class PairStack(nn.Module): |
| def __init__( |
| self, |
| c_z: int, |
| c_hidden_mul: int, |
| c_hidden_pair_att: int, |
| no_heads_pair: int, |
| transition_n: int, |
| pair_dropout: float, |
| fuse_projection_weights: bool, |
| inf: float, |
| eps: float |
| ): |
| super(PairStack, self).__init__() |
|
|
| if fuse_projection_weights: |
| self.tri_mul_out = FusedTriangleMultiplicationOutgoing( |
| c_z, |
| c_hidden_mul, |
| ) |
| self.tri_mul_in = FusedTriangleMultiplicationIncoming( |
| c_z, |
| c_hidden_mul, |
| ) |
| else: |
| self.tri_mul_out = TriangleMultiplicationOutgoing( |
| c_z, |
| c_hidden_mul, |
| ) |
| self.tri_mul_in = TriangleMultiplicationIncoming( |
| c_z, |
| c_hidden_mul, |
| ) |
|
|
| self.tri_att_start = TriangleAttention( |
| c_z, |
| c_hidden_pair_att, |
| no_heads_pair, |
| inf=inf, |
| ) |
| self.tri_att_end = TriangleAttention( |
| c_z, |
| c_hidden_pair_att, |
| no_heads_pair, |
| inf=inf, |
| ) |
|
|
| self.pair_transition = PairTransition( |
| c_z, |
| transition_n, |
| ) |
|
|
| self.ps_dropout_row_layer = DropoutRowwise(pair_dropout) |
|
|
| def forward(self, |
| z: torch.Tensor, |
| pair_mask: torch.Tensor, |
| chunk_size: Optional[int] = None, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| inplace_safe: bool = False, |
| _mask_trans: bool = True, |
| _attn_chunk_size: Optional[int] = None |
| ) -> torch.Tensor: |
| |
| |
| |
| pair_trans_mask = pair_mask if _mask_trans else None |
|
|
| if (_attn_chunk_size is None): |
| _attn_chunk_size = chunk_size |
|
|
| tmu_update = self.tri_mul_out( |
| z, |
| mask=pair_mask, |
| inplace_safe=inplace_safe, |
| _add_with_inplace=True, |
| ) |
| if (not inplace_safe): |
| z = z + self.ps_dropout_row_layer(tmu_update) |
| else: |
| z = tmu_update |
|
|
| del tmu_update |
|
|
| tmu_update = self.tri_mul_in( |
| z, |
| mask=pair_mask, |
| inplace_safe=inplace_safe, |
| _add_with_inplace=True, |
| ) |
| if (not inplace_safe): |
| z = z + self.ps_dropout_row_layer(tmu_update) |
| else: |
| z = tmu_update |
|
|
| del tmu_update |
|
|
| z = add(z, |
| self.ps_dropout_row_layer( |
| self.tri_att_start( |
| z, |
| mask=pair_mask, |
| chunk_size=_attn_chunk_size, |
| use_memory_efficient_kernel=False, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| inplace_safe=inplace_safe, |
| ) |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| z = z.transpose(-2, -3) |
| if (inplace_safe): |
| z = z.contiguous() |
|
|
| z = add(z, |
| self.ps_dropout_row_layer( |
| self.tri_att_end( |
| z, |
| mask=pair_mask.transpose(-1, -2), |
| chunk_size=_attn_chunk_size, |
| use_memory_efficient_kernel=False, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| inplace_safe=inplace_safe, |
| ) |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| z = z.transpose(-2, -3) |
| if (inplace_safe): |
| z = z.contiguous() |
|
|
| z = add(z, |
| self.pair_transition( |
| z, mask=pair_trans_mask, chunk_size=chunk_size, |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| return z |
|
|
|
|
| class MSABlock(nn.Module, ABC): |
| @abstractmethod |
| def __init__(self, |
| c_m: int, |
| c_z: int, |
| c_hidden_msa_att: int, |
| c_hidden_opm: int, |
| c_hidden_mul: int, |
| c_hidden_pair_att: int, |
| no_heads_msa: int, |
| no_heads_pair: int, |
| transition_n: int, |
| msa_dropout: float, |
| pair_dropout: float, |
| opm_first: bool, |
| fuse_projection_weights: bool, |
| inf: float, |
| eps: float, |
| ): |
| super(MSABlock, self).__init__() |
|
|
| self.opm_first = opm_first |
|
|
| self.msa_att_row = MSARowAttentionWithPairBias( |
| c_m=c_m, |
| c_z=c_z, |
| c_hidden=c_hidden_msa_att, |
| no_heads=no_heads_msa, |
| inf=inf, |
| ) |
|
|
| self.msa_dropout_layer = DropoutRowwise(msa_dropout) |
|
|
| self.msa_transition = MSATransition( |
| c_m=c_m, |
| n=transition_n, |
| ) |
|
|
| self.outer_product_mean = OuterProductMean( |
| c_m, |
| c_z, |
| c_hidden_opm, |
| ) |
|
|
| self.pair_stack = PairStack( |
| c_z=c_z, |
| c_hidden_mul=c_hidden_mul, |
| c_hidden_pair_att=c_hidden_pair_att, |
| no_heads_pair=no_heads_pair, |
| transition_n=transition_n, |
| pair_dropout=pair_dropout, |
| fuse_projection_weights=fuse_projection_weights, |
| inf=inf, |
| eps=eps |
| ) |
|
|
| def _compute_opm(self, |
| input_tensors: Sequence[torch.Tensor], |
| msa_mask: torch.Tensor, |
| chunk_size: Optional[int] = None, |
| inplace_safe: bool = False, |
| _offload_inference: bool = False |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
|
|
| m, z = input_tensors |
|
|
| if (_offload_inference and inplace_safe): |
| |
| del m, z |
| assert (sys.getrefcount(input_tensors[1]) == 2) |
| input_tensors[1] = input_tensors[1].cpu() |
| m, z = input_tensors |
|
|
| opm = self.outer_product_mean( |
| m, mask=msa_mask, chunk_size=chunk_size, inplace_safe=inplace_safe |
| ) |
|
|
| if (_offload_inference and inplace_safe): |
| |
| del m, z |
| assert (sys.getrefcount(input_tensors[0]) == 2) |
| input_tensors[1] = input_tensors[1].to(opm.device) |
| m, z = input_tensors |
|
|
| z = add(z, opm, inplace=inplace_safe) |
| del opm |
|
|
| return m, z |
|
|
| @abstractmethod |
| def forward(self, |
| m: Optional[torch.Tensor], |
| z: Optional[torch.Tensor], |
| msa_mask: torch.Tensor, |
| pair_mask: torch.Tensor, |
| chunk_size: Optional[int] = None, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| use_flash: bool = False, |
| inplace_safe: bool = False, |
| _mask_trans: bool = True, |
| _attn_chunk_size: Optional[int] = None, |
| _offload_inference: bool = False, |
| _offloadable_inputs: Optional[Sequence[torch.Tensor]] = None, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| pass |
|
|
|
|
| class EvoformerBlock(MSABlock): |
| def __init__(self, |
| c_m: int, |
| c_z: int, |
| c_hidden_msa_att: int, |
| c_hidden_opm: int, |
| c_hidden_mul: int, |
| c_hidden_pair_att: int, |
| no_heads_msa: int, |
| no_heads_pair: int, |
| transition_n: int, |
| msa_dropout: float, |
| pair_dropout: float, |
| no_column_attention: bool, |
| opm_first: bool, |
| fuse_projection_weights: bool, |
| inf: float, |
| eps: float, |
| ): |
| super(EvoformerBlock, self).__init__(c_m=c_m, |
| c_z=c_z, |
| c_hidden_msa_att=c_hidden_msa_att, |
| c_hidden_opm=c_hidden_opm, |
| c_hidden_mul=c_hidden_mul, |
| c_hidden_pair_att=c_hidden_pair_att, |
| no_heads_msa=no_heads_msa, |
| no_heads_pair=no_heads_pair, |
| transition_n=transition_n, |
| msa_dropout=msa_dropout, |
| pair_dropout=pair_dropout, |
| opm_first=opm_first, |
| fuse_projection_weights=fuse_projection_weights, |
| inf=inf, |
| eps=eps) |
|
|
| |
| self.no_column_attention = no_column_attention |
|
|
| if not self.no_column_attention: |
| self.msa_att_col = MSAColumnAttention( |
| c_m, |
| c_hidden_msa_att, |
| no_heads_msa, |
| inf=inf, |
| ) |
|
|
| def forward(self, |
| m: Optional[torch.Tensor], |
| z: Optional[torch.Tensor], |
| msa_mask: torch.Tensor, |
| pair_mask: torch.Tensor, |
| chunk_size: Optional[int] = None, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| use_flash: bool = False, |
| inplace_safe: bool = False, |
| _mask_trans: bool = True, |
| _attn_chunk_size: Optional[int] = None, |
| _offload_inference: bool = False, |
| _offloadable_inputs: Optional[Sequence[torch.Tensor]] = None, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
|
|
| msa_trans_mask = msa_mask if _mask_trans else None |
|
|
| if(_attn_chunk_size is None): |
| _attn_chunk_size = chunk_size |
|
|
| if(_offload_inference and inplace_safe): |
| input_tensors = _offloadable_inputs |
| del _offloadable_inputs |
| else: |
| input_tensors = [m, z] |
|
|
| m, z = input_tensors |
|
|
| if self.opm_first: |
| del m, z |
|
|
| m, z = self._compute_opm(input_tensors=input_tensors, |
| msa_mask=msa_mask, |
| chunk_size=chunk_size, |
| inplace_safe=inplace_safe, |
| _offload_inference=_offload_inference) |
|
|
| m = add(m, |
| self.msa_dropout_layer( |
| self.msa_att_row( |
| m, |
| z=z, |
| mask=msa_mask, |
| chunk_size=_attn_chunk_size, |
| use_memory_efficient_kernel=False, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| ) |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| if (_offload_inference and inplace_safe): |
| |
| del m, z |
| assert (sys.getrefcount(input_tensors[1]) == 2) |
| input_tensors[1] = input_tensors[1].cpu() |
| torch.cuda.empty_cache() |
| m, z = input_tensors |
|
|
| |
| if not self.no_column_attention: |
| m = add(m, |
| self.msa_att_col( |
| m, |
| mask=msa_mask, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| use_flash=use_flash, |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| m = add( |
| m, |
| self.msa_transition( |
| m, mask=msa_trans_mask, chunk_size=chunk_size, |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| if not self.opm_first: |
| if (not inplace_safe): |
| input_tensors = [m, z] |
|
|
| del m, z |
|
|
| m, z = self._compute_opm(input_tensors=input_tensors, |
| msa_mask=msa_mask, |
| chunk_size=chunk_size, |
| inplace_safe=inplace_safe, |
| _offload_inference=_offload_inference) |
|
|
| if (_offload_inference and inplace_safe): |
| |
| del m, z |
| assert (sys.getrefcount(input_tensors[0]) == 2) |
| device = input_tensors[0].device |
| input_tensors[0] = input_tensors[0].cpu() |
| input_tensors[1] = input_tensors[1].to(device) |
| m, z = input_tensors |
|
|
| if (not inplace_safe): |
| input_tensors = [m, z] |
|
|
| del m, z |
|
|
| z = self.pair_stack( |
| z=input_tensors[1], |
| pair_mask=pair_mask, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| inplace_safe=inplace_safe, |
| _mask_trans=_mask_trans, |
| _attn_chunk_size=_attn_chunk_size |
| ) |
|
|
| if (_offload_inference and inplace_safe): |
| |
| device = z.device |
| assert (sys.getrefcount(input_tensors[0]) == 2) |
| input_tensors[0] = input_tensors[0].to(device) |
| m, _ = input_tensors |
| else: |
| m = input_tensors[0] |
|
|
| return m, z |
|
|
|
|
| class ExtraMSABlock(MSABlock): |
| """ |
| Almost identical to the standard EvoformerBlock, except in that the |
| ExtraMSABlock uses GlobalAttention for MSA column attention and |
| requires more fine-grained control over checkpointing. Separated from |
| its twin to preserve the TorchScript-ability of the latter. |
| """ |
| def __init__(self, |
| c_m: int, |
| c_z: int, |
| c_hidden_msa_att: int, |
| c_hidden_opm: int, |
| c_hidden_mul: int, |
| c_hidden_pair_att: int, |
| no_heads_msa: int, |
| no_heads_pair: int, |
| transition_n: int, |
| msa_dropout: float, |
| pair_dropout: float, |
| opm_first: bool, |
| fuse_projection_weights: bool, |
| inf: float, |
| eps: float, |
| ckpt: bool, |
| ): |
| super(ExtraMSABlock, self).__init__(c_m=c_m, |
| c_z=c_z, |
| c_hidden_msa_att=c_hidden_msa_att, |
| c_hidden_opm=c_hidden_opm, |
| c_hidden_mul=c_hidden_mul, |
| c_hidden_pair_att=c_hidden_pair_att, |
| no_heads_msa=no_heads_msa, |
| no_heads_pair=no_heads_pair, |
| transition_n=transition_n, |
| msa_dropout=msa_dropout, |
| pair_dropout=pair_dropout, |
| opm_first=opm_first, |
| fuse_projection_weights=fuse_projection_weights, |
| inf=inf, |
| eps=eps) |
|
|
| self.ckpt = ckpt |
|
|
| self.msa_att_col = MSAColumnGlobalAttention( |
| c_in=c_m, |
| c_hidden=c_hidden_msa_att, |
| no_heads=no_heads_msa, |
| inf=inf, |
| eps=eps, |
| ) |
|
|
| def forward(self, |
| m: Optional[torch.Tensor], |
| z: Optional[torch.Tensor], |
| msa_mask: torch.Tensor, |
| pair_mask: torch.Tensor, |
| chunk_size: Optional[int] = None, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| inplace_safe: bool = False, |
| _mask_trans: bool = True, |
| _attn_chunk_size: Optional[int] = None, |
| _offload_inference: bool = False, |
| _offloadable_inputs: Optional[Sequence[torch.Tensor]] = None, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| if(_attn_chunk_size is None): |
| _attn_chunk_size = chunk_size |
| |
| if(_offload_inference and inplace_safe): |
| input_tensors = _offloadable_inputs |
| del _offloadable_inputs |
| else: |
| input_tensors = [m, z] |
|
|
| m, z = input_tensors |
|
|
| if self.opm_first: |
| del m, z |
|
|
| m, z = self._compute_opm(input_tensors=input_tensors, |
| msa_mask=msa_mask, |
| chunk_size=chunk_size, |
| inplace_safe=inplace_safe, |
| _offload_inference=_offload_inference) |
|
|
| m = add(m, |
| self.msa_dropout_layer( |
| self.msa_att_row( |
| m.clone() if torch.is_grad_enabled() else m, |
| z=z.clone() if torch.is_grad_enabled() else z, |
| mask=msa_mask, |
| chunk_size=_attn_chunk_size, |
| use_lma=use_lma, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_memory_efficient_kernel=not (use_lma or use_deepspeed_evo_attention), |
| _checkpoint_chunks= |
| self.ckpt if torch.is_grad_enabled() else False, |
| ) |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| if (not inplace_safe): |
| input_tensors = [m, z] |
|
|
| del m, z |
|
|
| def fn(input_tensors): |
| m, z = input_tensors |
|
|
| if (_offload_inference and inplace_safe): |
| |
| del m, z |
| assert (sys.getrefcount(input_tensors[1]) == 2) |
| input_tensors[1] = input_tensors[1].cpu() |
| torch.cuda.empty_cache() |
| m, z = input_tensors |
|
|
| m = add(m, |
| self.msa_att_col( |
| m, |
| mask=msa_mask, |
| chunk_size=chunk_size, |
| use_lma=use_lma, |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| m = add( |
| m, |
| self.msa_transition( |
| m, mask=msa_mask, chunk_size=chunk_size, |
| ), |
| inplace=inplace_safe, |
| ) |
|
|
| if not self.opm_first: |
| if (not inplace_safe): |
| input_tensors = [m, z] |
|
|
| del m, z |
|
|
| m, z = self._compute_opm(input_tensors=input_tensors, |
| msa_mask=msa_mask, |
| chunk_size=chunk_size, |
| inplace_safe=inplace_safe, |
| _offload_inference=_offload_inference) |
|
|
| if (_offload_inference and inplace_safe): |
| |
| del m, z |
| assert (sys.getrefcount(input_tensors[0]) == 2) |
| device = input_tensors[0].device |
| input_tensors[0] = input_tensors[0].cpu() |
| input_tensors[1] = input_tensors[1].to(device) |
| m, z = input_tensors |
|
|
| if (not inplace_safe): |
| input_tensors = [m, z] |
|
|
| del m, z |
|
|
| z = self.pair_stack( |
| input_tensors[1], |
| pair_mask=pair_mask, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| inplace_safe=inplace_safe, |
| _mask_trans=_mask_trans, |
| _attn_chunk_size=_attn_chunk_size |
| ) |
|
|
| m = input_tensors[0] |
| if (_offload_inference and inplace_safe): |
| |
| device = z.device |
| del m |
| assert (sys.getrefcount(input_tensors[0]) == 2) |
| input_tensors[0] = input_tensors[0].to(device) |
| m, _ = input_tensors |
|
|
| return m, z |
|
|
| if (torch.is_grad_enabled() and self.ckpt): |
| checkpoint_fn = get_checkpoint_fn() |
| m, z = checkpoint_fn(fn, input_tensors) |
| else: |
| m, z = fn(input_tensors) |
|
|
| return m, z |
|
|
|
|
| class EvoformerStack(nn.Module): |
| """ |
| Main Evoformer trunk. |
| |
| Implements Algorithm 6. |
| """ |
|
|
| def __init__( |
| self, |
| c_m: int, |
| c_z: int, |
| c_hidden_msa_att: int, |
| c_hidden_opm: int, |
| c_hidden_mul: int, |
| c_hidden_pair_att: int, |
| c_s: int, |
| no_heads_msa: int, |
| no_heads_pair: int, |
| no_blocks: int, |
| transition_n: int, |
| msa_dropout: float, |
| pair_dropout: float, |
| no_column_attention: bool, |
| opm_first: bool, |
| fuse_projection_weights: bool, |
| blocks_per_ckpt: int, |
| inf: float, |
| eps: float, |
| clear_cache_between_blocks: bool = False, |
| tune_chunk_size: bool = False, |
| **kwargs, |
| ): |
| """ |
| Args: |
| c_m: |
| MSA channel dimension |
| c_z: |
| Pair channel dimension |
| c_hidden_msa_att: |
| Hidden dimension in MSA attention |
| c_hidden_opm: |
| Hidden dimension in outer product mean module |
| c_hidden_mul: |
| Hidden dimension in multiplicative updates |
| c_hidden_pair_att: |
| Hidden dimension in triangular attention |
| c_s: |
| Channel dimension of the output "single" embedding |
| no_heads_msa: |
| Number of heads used for MSA attention |
| no_heads_pair: |
| Number of heads used for pair attention |
| no_blocks: |
| Number of Evoformer blocks in the stack |
| transition_n: |
| Factor by which to multiply c_m to obtain the MSATransition |
| hidden dimension |
| msa_dropout: |
| Dropout rate for MSA activations |
| pair_dropout: |
| Dropout used for pair activations |
| no_column_attention: |
| When True, doesn't use column attention. Required for running |
| sequence embedding mode |
| opm_first: |
| When True, Outer Product Mean is performed at the beginning of |
| the Evoformer block instead of after the MSA Stack. |
| Used in Multimer pipeline. |
| fuse_projection_weights: |
| When True, uses FusedTriangleMultiplicativeUpdate variant in |
| the Pair Stack. Used in Multimer pipeline. |
| blocks_per_ckpt: |
| Number of Evoformer blocks in each activation checkpoint |
| clear_cache_between_blocks: |
| Whether to clear CUDA's GPU memory cache between blocks of the |
| stack. Slows down each block but can reduce fragmentation |
| tune_chunk_size: |
| Whether to dynamically tune the module's chunk size |
| """ |
| super(EvoformerStack, self).__init__() |
|
|
| self.blocks_per_ckpt = blocks_per_ckpt |
| self.clear_cache_between_blocks = clear_cache_between_blocks |
|
|
| self.blocks = nn.ModuleList() |
|
|
| for _ in range(no_blocks): |
| block = EvoformerBlock( |
| c_m=c_m, |
| c_z=c_z, |
| c_hidden_msa_att=c_hidden_msa_att, |
| c_hidden_opm=c_hidden_opm, |
| c_hidden_mul=c_hidden_mul, |
| c_hidden_pair_att=c_hidden_pair_att, |
| no_heads_msa=no_heads_msa, |
| no_heads_pair=no_heads_pair, |
| transition_n=transition_n, |
| msa_dropout=msa_dropout, |
| pair_dropout=pair_dropout, |
| no_column_attention=no_column_attention, |
| opm_first=opm_first, |
| fuse_projection_weights=fuse_projection_weights, |
| inf=inf, |
| eps=eps, |
| ) |
| self.blocks.append(block) |
|
|
| self.linear = Linear(c_m, c_s) |
|
|
| self.tune_chunk_size = tune_chunk_size |
| self.chunk_size_tuner = None |
| if(tune_chunk_size): |
| self.chunk_size_tuner = ChunkSizeTuner() |
|
|
| def _prep_blocks(self, |
| m: torch.Tensor, |
| z: torch.Tensor, |
| chunk_size: int, |
| use_deepspeed_evo_attention: bool, |
| use_lma: bool, |
| use_flash: bool, |
| msa_mask: Optional[torch.Tensor], |
| pair_mask: Optional[torch.Tensor], |
| inplace_safe: bool, |
| _mask_trans: bool, |
| ): |
| blocks = [ |
| partial( |
| b, |
| msa_mask=msa_mask, |
| pair_mask=pair_mask, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| use_flash=use_flash, |
| inplace_safe=inplace_safe, |
| _mask_trans=_mask_trans, |
| ) |
| for b in self.blocks |
| ] |
|
|
| if(self.clear_cache_between_blocks): |
| def block_with_cache_clear(block, *args, **kwargs): |
| torch.cuda.empty_cache() |
| return block(*args, **kwargs) |
|
|
| blocks = [partial(block_with_cache_clear, b) for b in blocks] |
|
|
| if(chunk_size is not None and self.chunk_size_tuner is not None): |
| assert(not self.training) |
| tuned_chunk_size = self.chunk_size_tuner.tune_chunk_size( |
| representative_fn=blocks[0], |
| |
| args=(m.clone(), z.clone(),), |
| min_chunk_size=chunk_size, |
| ) |
| blocks = [ |
| partial(b, |
| chunk_size=tuned_chunk_size, |
| |
| |
| _attn_chunk_size=max(chunk_size, tuned_chunk_size // 4), |
| ) for b in blocks |
| ] |
|
|
| return blocks |
|
|
| def _forward_offload(self, |
| input_tensors: Sequence[torch.Tensor], |
| msa_mask: torch.Tensor, |
| pair_mask: torch.Tensor, |
| chunk_size: int, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| use_flash: bool = False, |
| _mask_trans: bool = True, |
| ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| assert(not (self.training or torch.is_grad_enabled())) |
| blocks = self._prep_blocks( |
| |
| |
| m=input_tensors[0], |
| z=input_tensors[1], |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| use_flash=use_flash, |
| msa_mask=msa_mask, |
| pair_mask=pair_mask, |
| inplace_safe=True, |
| _mask_trans=_mask_trans, |
| ) |
|
|
| for b in blocks: |
| m, z = b( |
| None, |
| None, |
| _offload_inference=True, |
| _offloadable_inputs=input_tensors, |
| ) |
| input_tensors[0] = m |
| input_tensors[1] = z |
| del m, z |
| |
| m, z = input_tensors |
| |
| s = self.linear(m[..., 0, :, :]) |
| |
| return m, z, s |
|
|
| def forward(self, |
| m: torch.Tensor, |
| z: torch.Tensor, |
| msa_mask: torch.Tensor, |
| pair_mask: torch.Tensor, |
| chunk_size: int, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| use_flash: bool = False, |
| inplace_safe: bool = False, |
| _mask_trans: bool = True, |
| ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| """ |
| Args: |
| m: |
| [*, N_seq, N_res, C_m] MSA embedding |
| z: |
| [*, N_res, N_res, C_z] pair embedding |
| msa_mask: |
| [*, N_seq, N_res] MSA mask |
| pair_mask: |
| [*, N_res, N_res] pair mask |
| chunk_size: |
| Inference-time subbatch size. Acts as a minimum if |
| self.tune_chunk_size is True |
| use_deepspeed_evo_attention: |
| Whether to use DeepSpeed memory efficient kernel. |
| Mutually exclusive with use_lma and use_flash. |
| use_lma: |
| Whether to use low-memory attention during inference. |
| Mutually exclusive with use_flash and use_deepspeed_evo_attention. |
| use_flash: |
| Whether to use FlashAttention where possible. Mutually |
| exclusive with use_lma and use_deepspeed_evo_attention. |
| Returns: |
| m: |
| [*, N_seq, N_res, C_m] MSA embedding |
| z: |
| [*, N_res, N_res, C_z] pair embedding |
| s: |
| [*, N_res, C_s] single embedding (or None if extra MSA stack) |
| """ |
| blocks = self._prep_blocks( |
| m=m, |
| z=z, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| use_flash=use_flash, |
| msa_mask=msa_mask, |
| pair_mask=pair_mask, |
| inplace_safe=inplace_safe, |
| _mask_trans=_mask_trans, |
| ) |
|
|
| blocks_per_ckpt = self.blocks_per_ckpt |
| if(not torch.is_grad_enabled()): |
| blocks_per_ckpt = None |
| |
| m, z = checkpoint_blocks( |
| blocks, |
| args=(m, z), |
| blocks_per_ckpt=blocks_per_ckpt, |
| ) |
|
|
| s = self.linear(m[..., 0, :, :]) |
|
|
| return m, z, s |
|
|
|
|
| class ExtraMSAStack(nn.Module): |
| """ |
| Implements Algorithm 18. |
| """ |
| def __init__(self, |
| c_m: int, |
| c_z: int, |
| c_hidden_msa_att: int, |
| c_hidden_opm: int, |
| c_hidden_mul: int, |
| c_hidden_pair_att: int, |
| no_heads_msa: int, |
| no_heads_pair: int, |
| no_blocks: int, |
| transition_n: int, |
| msa_dropout: float, |
| pair_dropout: float, |
| opm_first: bool, |
| fuse_projection_weights: bool, |
| inf: float, |
| eps: float, |
| ckpt: bool, |
| clear_cache_between_blocks: bool = False, |
| tune_chunk_size: bool = False, |
| **kwargs, |
| ): |
| super(ExtraMSAStack, self).__init__() |
| |
| self.ckpt = ckpt |
| self.clear_cache_between_blocks = clear_cache_between_blocks |
| self.blocks = nn.ModuleList() |
| for _ in range(no_blocks): |
| block = ExtraMSABlock( |
| c_m=c_m, |
| c_z=c_z, |
| c_hidden_msa_att=c_hidden_msa_att, |
| c_hidden_opm=c_hidden_opm, |
| c_hidden_mul=c_hidden_mul, |
| c_hidden_pair_att=c_hidden_pair_att, |
| no_heads_msa=no_heads_msa, |
| no_heads_pair=no_heads_pair, |
| transition_n=transition_n, |
| msa_dropout=msa_dropout, |
| pair_dropout=pair_dropout, |
| opm_first=opm_first, |
| fuse_projection_weights=fuse_projection_weights, |
| inf=inf, |
| eps=eps, |
| ckpt=False, |
| ) |
| self.blocks.append(block) |
| |
| self.tune_chunk_size = tune_chunk_size |
| self.chunk_size_tuner = None |
| if(tune_chunk_size): |
| self.chunk_size_tuner = ChunkSizeTuner() |
|
|
| def _prep_blocks(self, |
| m: torch.Tensor, |
| z: torch.Tensor, |
| chunk_size: int, |
| use_deepspeed_evo_attention: bool, |
| use_lma: bool, |
| msa_mask: Optional[torch.Tensor], |
| pair_mask: Optional[torch.Tensor], |
| inplace_safe: bool, |
| _mask_trans: bool, |
| ): |
| blocks = [ |
| partial( |
| b, |
| msa_mask=msa_mask, |
| pair_mask=pair_mask, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| inplace_safe=inplace_safe, |
| _mask_trans=_mask_trans, |
| ) for b in self.blocks |
| ] |
|
|
| def clear_cache(b, *args, **kwargs): |
| torch.cuda.empty_cache() |
| return b(*args, **kwargs) |
|
|
| if(self.clear_cache_between_blocks): |
| blocks = [partial(clear_cache, b) for b in blocks] |
|
|
| if(chunk_size is not None and self.chunk_size_tuner is not None): |
| tuned_chunk_size = self.chunk_size_tuner.tune_chunk_size( |
| representative_fn=blocks[0], |
| |
| |
| |
| args=(m.clone(), z.clone(),), |
| min_chunk_size=chunk_size, |
| ) |
| blocks = [ |
| partial(b, |
| chunk_size=tuned_chunk_size, |
| |
| |
| _attn_chunk_size=max(chunk_size, tuned_chunk_size // 4), |
| ) for b in blocks |
| ] |
|
|
| return blocks |
|
|
| def _forward_offload(self, |
| input_tensors: Sequence[torch.Tensor], |
| chunk_size: int, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| msa_mask: Optional[torch.Tensor] = None, |
| pair_mask: Optional[torch.Tensor] = None, |
| _mask_trans: bool = True, |
| ) -> torch.Tensor: |
| assert(not (self.training or torch.is_grad_enabled())) |
| blocks = self._prep_blocks( |
| |
| |
| m=input_tensors[0], |
| z=input_tensors[1], |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| msa_mask=msa_mask, |
| pair_mask=pair_mask, |
| inplace_safe=True, |
| _mask_trans=_mask_trans, |
| ) |
|
|
| for b in blocks: |
| m, z = b( |
| None, |
| None, |
| _offload_inference=True, |
| _offloadable_inputs=input_tensors, |
| ) |
| input_tensors[0] = m |
| input_tensors[1] = z |
| del m, z |
|
|
| return input_tensors[1] |
|
|
| def forward(self, |
| m: torch.Tensor, |
| z: torch.Tensor, |
| msa_mask: Optional[torch.Tensor], |
| pair_mask: Optional[torch.Tensor], |
| chunk_size: int, |
| use_deepspeed_evo_attention: bool = False, |
| use_lma: bool = False, |
| inplace_safe: bool = False, |
| _mask_trans: bool = True, |
| ) -> torch.Tensor: |
| """ |
| Args: |
| m: |
| [*, N_extra, N_res, C_m] extra MSA embedding |
| z: |
| [*, N_res, N_res, C_z] pair embedding |
| chunk_size: Inference-time subbatch size for Evoformer modules |
| use_deepspeed_evo_attention: Whether to use DeepSpeed memory-efficient kernel |
| use_lma: Whether to use low-memory attention during inference |
| msa_mask: |
| Optional [*, N_extra, N_res] MSA mask |
| pair_mask: |
| Optional [*, N_res, N_res] pair mask |
| Returns: |
| [*, N_res, N_res, C_z] pair update |
| """ |
| checkpoint_fn = get_checkpoint_fn() |
| blocks = self._prep_blocks( |
| m=m, |
| z=z, |
| chunk_size=chunk_size, |
| use_deepspeed_evo_attention=use_deepspeed_evo_attention, |
| use_lma=use_lma, |
| msa_mask=msa_mask, |
| pair_mask=pair_mask, |
| inplace_safe=inplace_safe, |
| _mask_trans=_mask_trans, |
| ) |
|
|
| for b in blocks: |
| if(self.ckpt and torch.is_grad_enabled()): |
| m, z = checkpoint_fn(b, m, z) |
| else: |
| m, z = b(m, z) |
|
|
| return z |
|
|