Boltz2 / minimal_featurizer.py
lhallee's picture
Upload minimal_featurizer.py with huggingface_hub
6b492b0 verified
Raw History Blame Contribute Delete
26.3 kB
import math
from typing import Dict, List, Tuple
import numpy as np
import torch
from torch.nn.functional import one_hot
from .minimal_structures import ProteinStructureTemplate
from . import vb_const as const
_ELEMENT_TO_Z = {
"H": 1,
"C": 6,
"N": 7,
"O": 8,
"P": 15,
"S": 16,
}
def _normalize_sequence(sequence: str) -> str:
seq = sequence.strip().upper()
assert len(seq) > 0, "Amino acid sequence must be non-empty."
for aa in seq:
assert aa in const.prot_letter_to_token, f"Unsupported residue code '{aa}'."
return seq
def _atom_name_to_element(atom_name: str) -> str:
name = atom_name.strip().upper()
if len(name) == 0:
return "C"
if name[0].isdigit():
name = name[1:]
if len(name) >= 2 and name[0:2] in ("CL", "BR", "FE", "MG", "ZN", "NA", "CA"):
return name[0]
return name[0]
def _atom_name_to_codes(atom_name: str) -> torch.Tensor:
clipped = atom_name.strip()[:4]
vals = [ord(ch) - 32 for ch in clipped]
while len(vals) < 4:
vals.append(0)
out = torch.tensor(vals, dtype=torch.long)
assert torch.all(out >= 0) and torch.all(out < 64), (
f"Invalid atom-name encoding for '{atom_name}'."
)
return out
# Canonical RDKit conformer positions extracted from official Boltz2 mol files
# (boltz-community/boltz-2 mols.tar, first conformer, centered per residue).
# These match the geometry the model was trained on.
_RDKIT_CONFORMERS: Dict[str, Dict[str, List[float]]] = {
"ALA": {"N": [-0.944785, 0.952743, 0.876326], "CA": [-0.287002, -0.317661, 0.564429], "C": [1.098243, -0.090637, 0.019913], "O": [1.267603, 0.576468, -1.036548], "CB": [-1.13406, -1.120912, -0.42412]},
"ARG": {"N": [3.318334, -1.792721, -0.81714], "CA": [2.256519, -0.785164, -0.782524], "C": [2.865221, 0.579152, -0.9479], "O": [2.530447, 1.305604, -1.921779], "CB": [1.446111, -0.888115, 0.531287], "CG": [0.26845, 0.099716, 0.625405], "CD": [-0.837403, -0.192693, -0.39904], "NE": [-2.021791, 0.62151, -0.123045], "CZ": [-2.953151, 0.387273, 0.951242], "NH1": [-4.01513, 1.322306, 1.16201], "NH2": [-2.857608, -0.656868, 1.721485]},
"ASN": {"N": [-1.76737, -1.671462, 0.274097], "CA": [-0.894725, -0.51088, 0.459045], "C": [-1.437261, 0.658619, -0.316984], "O": [-1.745491, 0.527762, -1.53251], "CB": [0.535815, -0.851978, 0.002785], "CG": [1.496584, 0.259422, 0.3155], "OD1": [2.006304, 0.340489, 1.464867], "ND2": [1.806145, 1.248028, -0.666799]},
"ASP": {"N": [-0.531828, -1.5551, 0.564551], "CA": [-0.798781, -0.116987, 0.471405], "C": [-2.220416, 0.13533, 0.042978], "O": [-2.637844, -0.292258, -1.067311], "CB": [0.18409, 0.554469, -0.501854], "CG": [1.594766, 0.436104, -0.012856], "OD1": [2.333189, -0.495262, -0.431504], "OD2": [2.076824, 1.333703, 0.934591]},
"CYS": {"N": [0.008485, 1.680076, -0.119503], "CA": [0.001723, 0.385667, -0.808584], "C": [-1.206733, -0.441379, -0.446529], "O": [-1.665274, -0.434109, 0.728016], "CB": [1.310427, -0.380146, -0.555459], "SG": [1.551372, -0.810109, 1.202058]},
"GLN": {"N": [-1.932297, -1.099026, -1.887635], "CA": [-1.370158, -0.7752, -0.575112], "C": [-2.304423, 0.155162, 0.147051], "O": [-2.803483, -0.184897, 1.253191], "CB": [0.038652, -0.158687, -0.716379], "CG": [0.736106, 0.008065, 0.640487], "CD": [2.117149, 0.560394, 0.455758], "OE1": [2.309056, 1.803816, 0.522783], "NE2": [3.209399, -0.309626, 0.159856]},
"GLU": {"N": [-1.750645, -1.298566, -1.148627], "CA": [-1.677927, -0.690629, 0.182405], "C": [-2.320926, 0.670409, 0.183131], "O": [-2.310494, 1.385902, -0.855225], "CB": [-0.218639, -0.616385, 0.677413], "CG": [0.704486, 0.181646, -0.256505], "CD": [2.107661, 0.167064, 0.261748], "OE1": [2.495054, 1.060279, 1.061799], "OE2": [2.97143, -0.85972, -0.106138]},
"GLY": {"N": [-1.416855, 0.862616, -0.1801], "CA": [-0.6149, -0.033688, 0.644675], "C": [0.809702, 0.000186, 0.194889], "O": [1.222053, -0.829114, -0.659464]},
"HIS": {"N": [1.26543, -1.579115, 0.588736], "CA": [1.426323, -0.419409, -0.29228], "C": [2.884431, -0.088226, -0.469953], "O": [3.407859, -0.14071, -1.615113], "CB": [0.668818, 0.794698, 0.269345], "CG": [-0.807376, 0.534987, 0.323389], "ND1": [-1.680612, 0.632102, -0.797403], "CD2": [-1.481273, 0.09683, 1.377023], "CE1": [-2.846859, 0.264305, -0.380313], "NE2": [-2.836741, -0.095463, 0.996569]},
"ILE": {"N": [-0.963969, -1.670572, 0.035929], "CA": [-1.004097, -0.255306, 0.424676], "C": [-1.714092, 0.593857, -0.603418], "O": [-1.755267, 0.246984, -1.815103], "CB": [0.416162, 0.290836, 0.741822], "CG1": [1.380111, 0.189293, -0.469113], "CG2": [0.995443, -0.424464, 1.975358], "CD1": [2.645709, 1.029371, -0.290151]},
"LEU": {"N": [1.265833, -0.791579, -1.184426], "CA": [0.933538, 0.306792, -0.27234], "C": [2.051763, 0.509151, 0.713934], "O": [2.543725, -0.475272, 1.329405], "CB": [-0.373342, 0.011933, 0.498041], "CG": [-1.632816, -0.15124, -0.388179], "CD1": [-2.835081, -0.534398, 0.48526], "CD2": [-1.953621, 1.124614, -1.181694]},
"LYS": {"N": [-1.908918, -1.413217, -1.088748], "CA": [-1.724515, -0.558426, 0.087009], "C": [-2.96641, 0.258056, 0.315903], "O": [-3.48391, 0.312278, 1.463711], "CB": [-0.523791, 0.388858, -0.107149], "CG": [0.821393, -0.35366, -0.108072], "CD": [1.993822, 0.633537, -0.161446], "CE": [3.338346, -0.105923, -0.176016], "NZ": [4.453983, 0.838498, -0.225193]},
"MET": {"N": [-1.522666, -0.831762, 1.959218], "CA": [-0.996081, 0.012809, 0.885437], "C": [-2.091048, 0.292064, -0.106017], "O": [-2.445252, 1.479197, -0.337323], "CB": [0.220238, -0.653951, 0.206767], "CG": [0.915991, 0.285443, -0.785597], "SD": [2.357592, -0.540499, -1.548879], "CE": [3.561227, -0.043301, -0.273607]},
"PHE": {"N": [3.046798, -1.689795, -0.208668], "CA": [1.829154, -0.89776, -0.391539], "C": [2.202709, 0.507016, -0.775299], "O": [1.770264, 1.003795, -1.849745], "CB": [0.962876, -0.916233, 0.885596], "CG": [-0.373751, -0.255463, 0.660531], "CD1": [-0.576773, 1.029007, 1.007455], "CD2": [-1.46686, -1.011364, -0.000788], "CE1": [-1.882158, 1.672384, 0.749291], "CE2": [-2.646851, -0.423965, -0.236002], "CZ": [-2.865409, 0.982376, 0.159168]},
"PRO": {"N": [-0.685006, -0.370164, -0.768919], "CA": [0.404323, 0.377887, -0.13805], "C": [1.731651, -0.324892, -0.273548], "O": [1.975993, -1.024105, -1.293633], "CB": [-0.004718, 0.595698, 1.311196], "CG": [-1.517474, 0.663762, 1.260234], "CD": [-1.904769, 0.081814, -0.097281]},
"SER": {"N": [1.015962, -1.698341, -0.119567], "CA": [0.101187, -0.56256, -0.236622], "C": [0.88759, 0.717914, -0.271742], "O": [0.652684, 1.578473, -1.161893], "CB": [-0.87741, -0.552018, 0.948557], "OG": [-1.780012, 0.516532, 0.841267]},
"THR": {"N": [-0.05857, 1.577455, 0.452633], "CA": [0.359545, 0.187419, 0.662184], "C": [1.654837, -0.077979, -0.057809], "O": [1.861664, 0.408605, -1.20258], "CB": [-0.727051, -0.803664, 0.176961], "OG1": [-1.139906, -0.486103, -1.128492], "CG2": [-1.950518, -0.805733, 1.097103]},
"TRP": {"N": [-3.22168, 1.119624, 0.055731], "CA": [-2.641395, 0.081435, -0.801364], "C": [-2.364426, -1.166119, -0.006875], "O": [-2.514206, -2.296758, -0.543038], "CB": [-1.384007, 0.576331, -1.552364], "CG": [-0.24222, 0.940856, -0.643481], "CD1": [-0.016615, 2.14194, -0.107979], "CD2": [0.800451, 0.051232, -0.1373], "NE1": [1.132969, 2.107427, 0.738111], "CE2": [1.575163, 0.761216, 0.659678], "CE3": [1.058378, -1.378818, -0.38353], "CZ2": [2.729299, 0.158745, 1.347069], "CZ3": [2.107441, -1.950409, 0.235009], "CH2": [2.980848, -1.146704, 1.140333]},
"TYR": {"N": [-2.152606, 0.331726, -1.283488], "CA": [-2.201241, -0.428961, -0.031756], "C": [-3.597879, -0.441616, 0.531374], "O": [-4.172602, 0.638246, 0.83694], "CB": [-1.21174, 0.150443, 0.994648], "CG": [0.209673, 0.053123, 0.503276], "CD1": [0.911845, -1.084226, 0.654794], "CD2": [0.82966, 1.201129, -0.203917], "CE1": [2.294408, -1.180811, 0.141538], "CE2": [2.080826, 1.10993, -0.671525], "CZ": [2.85374, -0.136912, -0.490516], "OH": [4.155917, -0.21207, -0.981367]},
"VAL": {"N": [0.631715, -1.334948, 0.719228], "CA": [0.419681, -0.467832, -0.445221], "C": [1.457942, 0.623882, -0.483986], "O": [1.935858, 1.0943, 0.583992], "CB": [-1.010701, 0.140743, -0.455789], "CG1": [-2.075373, -0.919582, -0.774314], "CG2": [-1.359123, 0.863438, 0.85609]},
"UNK": {"N": [0.8287, -1.182096, -0.645721], "CA": [-0.174671, -0.11586, -0.67851], "C": [0.301419, 1.045573, 0.149013], "O": [0.589973, 0.885289, 1.365851], "CB": [-1.545421, -0.632906, -0.190632]},
}
def _get_atom_position(res_name: str, atom_name: str, atom_idx: int) -> np.ndarray:
"""Get the canonical RDKit conformer position for an atom.
Uses pre-extracted positions from official Boltz2 mol files. Falls back
to a simple geometric placement for unknown residue/atom combinations.
"""
if res_name in _RDKIT_CONFORMERS and atom_name in _RDKIT_CONFORMERS[res_name]:
return np.array(_RDKIT_CONFORMERS[res_name][atom_name], dtype=np.float32)
# Fallback for unknown atoms (should not happen for canonical AAs)
angle = (atom_idx + 1) * 0.7
radius = 1.4 + 0.03 * atom_idx
return np.array(
[
radius * math.cos(angle),
radius * math.sin(angle),
0.1 * ((atom_idx % 5) - 2),
],
dtype=np.float32,
)
def _build_template(
sequence: str,
) -> Tuple[
ProteinStructureTemplate,
List[str],
List[int],
List[int],
List[int],
List[np.ndarray],
List[int],
]:
residue_names: List[str] = []
residue_token_ids: List[int] = []
atom_names: List[str] = []
atom_elements: List[str] = []
atom_residue_index: List[int] = []
atom_chain_id: List[str] = []
atom_positions: List[np.ndarray] = []
residue_center_atom_idx: List[int] = []
residue_disto_atom_idx: List[int] = []
residue_frame_atom_idx: List[int] = []
global_atom_idx = 0
for res_idx, aa in enumerate(sequence):
token_name = const.prot_letter_to_token[aa]
residue_names.append(token_name)
residue_token_ids.append(const.token_ids[token_name])
residue_atoms = const.ref_atoms[token_name]
assert len(residue_atoms) > 0, f"No reference atoms for residue {token_name}."
center_atom_name = const.res_to_center_atom[token_name]
disto_atom_name = const.res_to_disto_atom[token_name]
center_idx = -1
disto_idx = -1
n_idx = -1
ca_idx = -1
c_idx = -1
for local_idx, atom_name in enumerate(residue_atoms):
atom_names.append(atom_name)
element = _atom_name_to_element(atom_name)
atom_elements.append(element)
atom_residue_index.append(res_idx)
atom_chain_id.append("A")
atom_pos = _get_atom_position(token_name, atom_name, local_idx)
atom_positions.append(atom_pos)
if atom_name == center_atom_name:
center_idx = global_atom_idx
if atom_name == disto_atom_name:
disto_idx = global_atom_idx
if atom_name == "N":
n_idx = global_atom_idx
if atom_name == "CA":
ca_idx = global_atom_idx
if atom_name == "C":
c_idx = global_atom_idx
global_atom_idx += 1
if center_idx == -1:
center_idx = global_atom_idx - len(residue_atoms)
if disto_idx == -1:
disto_idx = center_idx
if n_idx == -1:
n_idx = center_idx
if ca_idx == -1:
ca_idx = center_idx
if c_idx == -1:
c_idx = center_idx
residue_center_atom_idx.append(center_idx)
residue_disto_atom_idx.append(disto_idx)
residue_frame_atom_idx.extend([n_idx, ca_idx, c_idx])
template = ProteinStructureTemplate(
sequence=sequence,
residue_names=residue_names,
atom_names=atom_names,
atom_elements=atom_elements,
atom_residue_index=atom_residue_index,
atom_chain_id=atom_chain_id,
)
return (
template,
residue_names,
residue_token_ids,
residue_center_atom_idx,
residue_disto_atom_idx,
atom_positions,
residue_frame_atom_idx,
)
def _random_rotation_matrix() -> torch.Tensor:
"""Sample a uniform random 3x3 rotation matrix (Algorithm 19 from AF2/Boltz)."""
q = torch.randn(4)
q = q / q.norm()
# Quaternion to rotation matrix
w, x, y, z = q[0], q[1], q[2], q[3]
return torch.tensor([
[1 - 2*(y*y + z*z), 2*(x*y - w*z), 2*(x*z + w*y)],
[2*(x*y + w*z), 1 - 2*(x*x + z*z), 2*(y*z - w*x)],
[2*(x*z - w*y), 2*(y*z + w*x), 1 - 2*(x*x + y*y)],
], dtype=torch.float32)
def _center_and_augment_atoms_per_residue(
atom_positions: torch.Tensor,
atom_residue_index: List[int],
num_residues: int,
) -> torch.Tensor:
"""Center atoms per residue and apply random rotation per residue.
Matches the official Boltz2 featurizer which applies center_random_augmentation
to each residue's ref_pos independently (featurizerv2.py lines 1495-1500).
"""
result = atom_positions.clone()
residue_index_tensor = torch.tensor(atom_residue_index, dtype=torch.long)
for residue_idx in range(num_residues):
residue_mask = residue_index_tensor == residue_idx
assert torch.any(residue_mask), f"Residue index {residue_idx} has no atoms."
residue_coords = result[residue_mask]
# Center
residue_center = residue_coords.mean(dim=0, keepdim=True)
residue_coords = residue_coords - residue_center
# Random rotation (matching official center_random_augmentation with centering=True)
R = _random_rotation_matrix()
residue_coords = residue_coords @ R.T
result[residue_mask] = residue_coords
return result
def build_boltz2_features(
amino_acid_sequence: str,
num_bins: int = 64,
atoms_per_window_queries: int = 32,
) -> Tuple[Dict[str, torch.Tensor], ProteinStructureTemplate]:
sequence = _normalize_sequence(amino_acid_sequence)
(
template,
residue_names,
residue_token_ids,
residue_center_atom_idx,
residue_disto_atom_idx,
atom_positions_np,
residue_frame_atom_idx_flat,
) = _build_template(sequence)
num_tokens = len(residue_names)
num_atoms = len(atom_positions_np)
assert num_tokens > 0 and num_atoms > 0
atom_positions = torch.tensor(np.asarray(atom_positions_np), dtype=torch.float32)
atom_positions = _center_and_augment_atoms_per_residue(
atom_positions=atom_positions,
atom_residue_index=template.atom_residue_index,
num_residues=num_tokens,
)
token_index = torch.arange(num_tokens, dtype=torch.long).unsqueeze(0)
residue_index = torch.arange(num_tokens, dtype=torch.long).unsqueeze(0)
asym_id = torch.zeros((1, num_tokens), dtype=torch.long)
entity_id = torch.zeros((1, num_tokens), dtype=torch.long)
sym_id = torch.zeros((1, num_tokens), dtype=torch.long)
mol_type = torch.full(
(1, num_tokens),
fill_value=const.chain_type_ids["PROTEIN"],
dtype=torch.long,
)
res_type_ids = torch.tensor(residue_token_ids, dtype=torch.long)
res_type = one_hot(res_type_ids, num_classes=const.num_tokens).float().unsqueeze(0)
# token_bonds encodes explicit covalent cross-links from structure bonds,
# NOT backbone peptide bonds (those are implicit via residue_index + asym_id).
# For a standard single-chain protein without cross-links, this is all zeros.
# This matches the official Boltz2 featurizer (featurizerv2.py lines 696-705).
token_bonds = torch.zeros((num_tokens, num_tokens), dtype=torch.float32)
type_bonds = torch.zeros((num_tokens, num_tokens), dtype=torch.long)
token_bonds = token_bonds.unsqueeze(0).unsqueeze(-1)
type_bonds = type_bonds.unsqueeze(0)
token_pad_mask = torch.ones((1, num_tokens), dtype=torch.float32)
token_resolved_mask = torch.ones((1, num_tokens), dtype=torch.float32)
token_disto_mask = torch.ones((1, num_tokens), dtype=torch.float32)
num_contact_classes = len(const.contact_conditioning_info)
unspecified_id = const.contact_conditioning_info["UNSPECIFIED"]
contact_ids = torch.full(
(num_tokens, num_tokens),
fill_value=unspecified_id,
dtype=torch.long,
)
contact_conditioning = one_hot(
contact_ids,
num_classes=num_contact_classes,
).float().unsqueeze(0)
contact_threshold = torch.zeros((1, num_tokens, num_tokens), dtype=torch.float32)
assert "x-ray diffraction" in const.method_types_ids
method_feature = torch.full(
(1, num_tokens),
fill_value=const.method_types_ids["x-ray diffraction"],
dtype=torch.long,
)
modified = torch.zeros((1, num_tokens), dtype=torch.long)
cyclic_period = torch.zeros((1, num_tokens), dtype=torch.float32)
affinity_token_mask = torch.zeros((1, num_tokens), dtype=torch.float32)
ref_pos = atom_positions.unsqueeze(0)
atom_pad_mask = torch.ones((1, num_atoms), dtype=torch.float32)
atom_resolved_mask = torch.ones((1, num_atoms), dtype=torch.float32)
atom_name_codes = torch.stack(
[_atom_name_to_codes(atom_name) for atom_name in template.atom_names],
dim=0,
)
ref_atom_name_chars = one_hot(atom_name_codes, num_classes=64).float().unsqueeze(0)
atomic_numbers = []
for element in template.atom_elements:
if element in _ELEMENT_TO_Z:
z_value = _ELEMENT_TO_Z[element]
else:
z_value = _ELEMENT_TO_Z["C"]
assert z_value < const.num_elements
atomic_numbers.append(z_value)
ref_element = one_hot(
torch.tensor(atomic_numbers, dtype=torch.long),
num_classes=const.num_elements,
).float().unsqueeze(0)
ref_charge = torch.zeros((1, num_atoms), dtype=torch.float32)
ref_chirality = torch.zeros((1, num_atoms), dtype=torch.long)
ref_space_uid = torch.tensor(template.atom_residue_index, dtype=torch.long).unsqueeze(0)
atom_to_token = one_hot(
torch.tensor(template.atom_residue_index, dtype=torch.long),
num_classes=num_tokens,
).float().unsqueeze(0)
token_to_rep_atom = one_hot(
torch.tensor(residue_disto_atom_idx, dtype=torch.long),
num_classes=num_atoms,
).float().unsqueeze(0)
token_to_center_atom = one_hot(
torch.tensor(residue_center_atom_idx, dtype=torch.long),
num_classes=num_atoms,
).float().unsqueeze(0)
r_set_to_rep_atom = token_to_center_atom.clone()
num_backbone_classes = (
1
+ len(const.protein_backbone_atom_index)
+ len(const.nucleic_backbone_atom_index)
)
backbone_ids = []
for atom_name in template.atom_names:
if atom_name in const.protein_backbone_atom_index:
backbone_ids.append(const.protein_backbone_atom_index[atom_name] + 1)
else:
backbone_ids.append(0)
atom_backbone_feat = one_hot(
torch.tensor(backbone_ids, dtype=torch.long),
num_classes=num_backbone_classes,
).float().unsqueeze(0)
coords = ref_pos.unsqueeze(1).contiguous()
disto_coords = torch.stack(
[atom_positions[idx] for idx in residue_disto_atom_idx],
dim=0,
)
disto_coords_ensemble = disto_coords.unsqueeze(0).unsqueeze(0).contiguous()
bfactor = torch.zeros((1, num_atoms), dtype=torch.float32)
atom_plddt = torch.ones((1, num_atoms), dtype=torch.float32)
assert atoms_per_window_queries > 0
pad_atoms = (
((num_atoms - 1) // atoms_per_window_queries + 1) * atoms_per_window_queries
- num_atoms
)
if pad_atoms > 0:
ref_pos = torch.nn.functional.pad(ref_pos, (0, 0, 0, pad_atoms), value=0.0)
atom_pad_mask = torch.nn.functional.pad(atom_pad_mask, (0, pad_atoms), value=0.0)
atom_resolved_mask = torch.nn.functional.pad(
atom_resolved_mask,
(0, pad_atoms),
value=0.0,
)
ref_atom_name_chars = torch.nn.functional.pad(
ref_atom_name_chars,
(0, 0, 0, 0, 0, pad_atoms),
value=0.0,
)
ref_element = torch.nn.functional.pad(ref_element, (0, 0, 0, pad_atoms), value=0.0)
ref_charge = torch.nn.functional.pad(ref_charge, (0, pad_atoms), value=0.0)
ref_chirality = torch.nn.functional.pad(ref_chirality, (0, pad_atoms), value=0)
atom_backbone_feat = torch.nn.functional.pad(
atom_backbone_feat,
(0, 0, 0, pad_atoms),
value=0.0,
)
ref_space_uid = torch.nn.functional.pad(ref_space_uid, (0, pad_atoms), value=0)
coords = torch.nn.functional.pad(coords, (0, 0, 0, pad_atoms), value=0.0)
atom_to_token = torch.nn.functional.pad(atom_to_token, (0, 0, 0, pad_atoms), value=0.0)
token_to_rep_atom = torch.nn.functional.pad(
token_to_rep_atom,
(0, pad_atoms),
value=0.0,
)
token_to_center_atom = torch.nn.functional.pad(
token_to_center_atom,
(0, pad_atoms),
value=0.0,
)
r_set_to_rep_atom = torch.nn.functional.pad(
r_set_to_rep_atom,
(0, pad_atoms),
value=0.0,
)
bfactor = torch.nn.functional.pad(bfactor, (0, pad_atoms), value=0.0)
atom_plddt = torch.nn.functional.pad(atom_plddt, (0, pad_atoms), value=0.0)
frames_idx = torch.tensor(
residue_frame_atom_idx_flat,
dtype=torch.long,
).reshape(num_tokens, 3)
frames_idx = frames_idx.unsqueeze(0).unsqueeze(1)
frame_resolved_mask = torch.ones((1, 1, num_tokens), dtype=torch.float32)
msa = torch.tensor(residue_token_ids, dtype=torch.long).unsqueeze(0).unsqueeze(0)
msa_paired = torch.ones((1, 1, num_tokens), dtype=torch.float32)
deletion_value = torch.zeros((1, 1, num_tokens), dtype=torch.float32)
has_deletion = torch.zeros((1, 1, num_tokens), dtype=torch.float32)
msa_mask = torch.ones((1, 1, num_tokens), dtype=torch.float32)
deletion_mean = torch.zeros((1, num_tokens), dtype=torch.float32)
profile = one_hot(
torch.tensor(residue_token_ids, dtype=torch.long),
num_classes=const.num_tokens,
).float().unsqueeze(0)
template_restype = one_hot(
torch.zeros((1, 1, num_tokens), dtype=torch.long),
num_classes=const.num_tokens,
).float()
template_frame_rot = torch.zeros((1, 1, num_tokens, 3, 3), dtype=torch.float32)
template_frame_t = torch.zeros((1, 1, num_tokens, 3), dtype=torch.float32)
template_cb = torch.zeros((1, 1, num_tokens, 3), dtype=torch.float32)
template_ca = torch.zeros((1, 1, num_tokens, 3), dtype=torch.float32)
template_mask_cb = torch.zeros((1, 1, num_tokens), dtype=torch.float32)
template_mask_frame = torch.zeros((1, 1, num_tokens), dtype=torch.float32)
template_mask = torch.zeros((1, 1, num_tokens), dtype=torch.float32)
query_to_template = torch.zeros((1, 1, num_tokens), dtype=torch.long)
visibility_ids = torch.zeros((1, 1, num_tokens), dtype=torch.float32)
disto_target = torch.zeros(
(1, num_tokens, num_tokens, 1, num_bins),
dtype=torch.float32,
)
disto_center = torch.stack(
[atom_positions[idx] for idx in residue_disto_atom_idx],
dim=0,
).unsqueeze(0)
features: Dict[str, torch.Tensor] = {
"token_index": token_index,
"residue_index": residue_index,
"asym_id": asym_id,
"entity_id": entity_id,
"sym_id": sym_id,
"mol_type": mol_type,
"res_type": res_type,
"disto_center": disto_center,
"token_bonds": token_bonds,
"type_bonds": type_bonds,
"token_pad_mask": token_pad_mask,
"token_resolved_mask": token_resolved_mask,
"token_disto_mask": token_disto_mask,
"contact_conditioning": contact_conditioning,
"contact_threshold": contact_threshold,
"method_feature": method_feature,
"modified": modified,
"cyclic_period": cyclic_period,
"affinity_token_mask": affinity_token_mask,
"ref_pos": ref_pos,
"atom_resolved_mask": atom_resolved_mask,
"ref_atom_name_chars": ref_atom_name_chars,
"ref_element": ref_element,
"ref_charge": ref_charge,
"ref_chirality": ref_chirality,
"atom_backbone_feat": atom_backbone_feat,
"ref_space_uid": ref_space_uid,
"coords": coords,
"atom_pad_mask": atom_pad_mask,
"atom_to_token": atom_to_token,
"token_to_rep_atom": token_to_rep_atom,
"r_set_to_rep_atom": r_set_to_rep_atom,
"token_to_center_atom": token_to_center_atom,
"disto_target": disto_target,
"disto_coords_ensemble": disto_coords_ensemble,
"bfactor": bfactor,
"plddt": atom_plddt,
"frames_idx": frames_idx,
"frame_resolved_mask": frame_resolved_mask,
"msa": msa,
"msa_paired": msa_paired,
"deletion_value": deletion_value,
"has_deletion": has_deletion,
"deletion_mean": deletion_mean,
"profile": profile,
"msa_mask": msa_mask,
"template_restype": template_restype,
"template_frame_rot": template_frame_rot,
"template_frame_t": template_frame_t,
"template_cb": template_cb,
"template_ca": template_ca,
"template_mask_cb": template_mask_cb,
"template_mask_frame": template_mask_frame,
"template_mask": template_mask,
"query_to_template": query_to_template,
"visibility_ids": visibility_ids,
}
return features, template