English
OneScience
protein structure generation
anzhi2710gmailcom's picture
Upload folder using huggingface_hub
8e04e6f verified
Raw
History Blame Contribute Delete
4.33 kB
from typing import Dict
import einops
import torch
from onescience.utils.openfold.np.residue_constants import RESTYPE_ATOM37_MASK
from models.nn.feature_factory import FeatureFactory
def get_atom_mask(device: torch.device = None):
return torch.from_numpy(RESTYPE_ATOM37_MASK).to(
dtype=torch.bool, device=device
) # [21, 37]
class ResidualLayer(torch.nn.Module):
"""Residual layer with pre-LN"""
def __init__(self, dim):
super(ResidualLayer, self).__init__()
self.layer_norm = torch.nn.LayerNorm(dim)
self.linear = torch.nn.Linear(dim, dim)
self.softplus = torch.nn.Softplus()
def forward(self, x):
out = self.layer_norm(x)
out = self.linear(out)
out = self.softplus(out)
return x + out
class DecoderFFLocal(torch.nn.Module):
"""
Encoder part of the autoencoder. A transformer with pair-biased attention.
"""
def __init__(self, **kwargs):
"""
Initializes the NN. The seqs and pair representations used are just zero in case
no features are required."""
super(DecoderFFLocal, self).__init__()
nlayers = kwargs["decoder"]["nlayers"]
token_dim = kwargs["decoder"]["token_dim"] # hidden dim
# To form initial representation
self.init_repr_factory = FeatureFactory(
feats=kwargs["decoder"]["feats_seq"],
dim_feats_out=token_dim,
use_ln_out=False,
mode="seq",
**kwargs["decoder"],
)
layers = []
for _ in range(nlayers):
layers.append(ResidualLayer(dim=token_dim))
self.ff_nn = torch.nn.Sequential(*layers)
self.logit_linear = torch.nn.Sequential(
torch.nn.LayerNorm(token_dim),
torch.nn.Linear(token_dim, 20, bias=False),
)
self.struct_linear = torch.nn.Sequential(
torch.nn.LayerNorm(token_dim),
torch.nn.Linear(token_dim, int(37 * 3), bias=False),
)
def forward(self, input: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
"""
Runs the network.
Args:
input: {
"z_latent": torch.Tensor(b, n, latent_dim),
"ca_coors_nm": torch.Tensor(b, n, 3),
"residue_mask": boolean torch.Tensor(b, n)
}
Returns:
Dictionary:
{
"coors_nm": all atom coordinates, shape [b, n, 37, 3]
"seq_logits": logits for the residue types, shape [b, n, 20]
"residue_mask": boolean [b, n]
"aatype_max": residue type by taking the most likely logit, shape [b, n], with integer values {0, ..., 19}
"atom_mask": boolean [b, n, 37], atom37 mask corresponding to aatype_max
}
"""
ca_coors_nm = input["ca_coors_nm"] # [b, n, 3]
mask = input["residue_mask"] # [b, n] boolean
# Iinitial sequence representation from features
seq_f_repr = self.init_repr_factory(input) # [b, n, token_dim]
seqs = seq_f_repr * mask[..., None] # [b, n, token_dim]
seqs = self.ff_nn(seqs) * mask[..., None] # [b, n, token_dim]
# Get logits
logits_out = self.logit_linear(seqs) * mask[..., None] # [b, n, 20]
# Get coordinates
coors_flat_nm = self.struct_linear(seqs) * mask[..., None] # [b, n, 37 * 3]
coors_a37_nm = einops.rearrange(
coors_flat_nm, "b n (a t) -> b n a t", a=37, t=3
) # [b, n, 37, 3]
coors_a37_nm[..., 1, :] = coors_a37_nm[..., 1, :] * 0.0 + ca_coors_nm
# Get sequence
aatype_max = torch.argmax(logits_out, dim=-1) # [b, n]
aatype_max = aatype_max * mask # [b, n]
# Get atom_mask
aa_a37_mask = get_atom_mask(device=logits_out.device) # [21, 37] boolean
atom_mask = aa_a37_mask[aatype_max, :] # [b, n, 37] boolean
atom_mask = atom_mask * mask[..., None] # [b, n, 37] boolean
output = {
"coors_nm": coors_a37_nm, # [b, n, 37, 3]
"seq_logits": logits_out, # [b, n, 20]
"residue_mask": mask, # [b, n]
"aatype_max": aatype_max, # [b, n]
"atom_mask": atom_mask, # [b, n, 37]
}
return output