Feature Extraction
Transformers
PyTorch
Safetensors
boltz2_automodel
protein-language-model
fastplms
custom_code
Instructions to use Synthyra/Boltz2 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Synthyra/Boltz2 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="Synthyra/Boltz2", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Synthyra/Boltz2", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload cif_writer.py with huggingface_hub
Browse files- cif_writer.py +126 -126
cif_writer.py
CHANGED
|
@@ -1,126 +1,126 @@
|
|
| 1 |
-
from pathlib import Path
|
| 2 |
-
from typing import List, Optional
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
import torch
|
| 6 |
-
|
| 7 |
-
from .minimal_structures import ProteinStructureTemplate
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def _confidence_per_atom(
|
| 11 |
-
plddt: Optional[torch.Tensor],
|
| 12 |
-
atom_to_residue: List[int],
|
| 13 |
-
num_atoms: int,
|
| 14 |
-
sample_index: int,
|
| 15 |
-
) -> np.ndarray:
|
| 16 |
-
if plddt is None:
|
| 17 |
-
return np.ones((num_atoms,), dtype=np.float32) * 100.0
|
| 18 |
-
|
| 19 |
-
values = plddt.detach().cpu()
|
| 20 |
-
if values.ndim == 1:
|
| 21 |
-
values = values.unsqueeze(0)
|
| 22 |
-
assert values.ndim == 2, "Expected pLDDT with shape [samples, tokens/atoms]."
|
| 23 |
-
assert sample_index < values.shape[0], "sample_index out of range for pLDDT."
|
| 24 |
-
|
| 25 |
-
selected = values[sample_index]
|
| 26 |
-
if selected.shape[0] == num_atoms:
|
| 27 |
-
return (selected.numpy() * 100.0).astype(np.float32)
|
| 28 |
-
|
| 29 |
-
num_residues = max(atom_to_residue) + 1
|
| 30 |
-
if selected.shape[0] == num_residues:
|
| 31 |
-
expanded = np.zeros((num_atoms,), dtype=np.float32)
|
| 32 |
-
selected_np = selected.numpy()
|
| 33 |
-
for atom_idx, residue_idx in enumerate(atom_to_residue):
|
| 34 |
-
expanded[atom_idx] = selected_np[residue_idx] * 100.0
|
| 35 |
-
return expanded
|
| 36 |
-
|
| 37 |
-
return np.ones((num_atoms,), dtype=np.float32) * 100.0
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
def write_cif(
|
| 41 |
-
structure_template: ProteinStructureTemplate,
|
| 42 |
-
atom_coords: torch.Tensor,
|
| 43 |
-
atom_mask: torch.Tensor,
|
| 44 |
-
output_path: str,
|
| 45 |
-
plddt: Optional[torch.Tensor] = None,
|
| 46 |
-
sample_index: int = 0,
|
| 47 |
-
) -> str:
|
| 48 |
-
coords = atom_coords.detach().cpu()
|
| 49 |
-
if coords.ndim == 2:
|
| 50 |
-
coords = coords.unsqueeze(0)
|
| 51 |
-
assert coords.ndim == 3, "Expected coordinates with shape [samples, atoms, 3]."
|
| 52 |
-
assert sample_index < coords.shape[0], "sample_index out of range."
|
| 53 |
-
selected_coords_tensor = coords[sample_index]
|
| 54 |
-
all_non_finite = torch.logical_not(torch.isfinite(selected_coords_tensor))
|
| 55 |
-
assert not torch.any(all_non_finite), (
|
| 56 |
-
"CIF export received non-finite coordinates. "
|
| 57 |
-
f"Non-finite count: {int(all_non_finite.sum().item())}"
|
| 58 |
-
)
|
| 59 |
-
selected_coords = selected_coords_tensor.numpy()
|
| 60 |
-
|
| 61 |
-
mask = atom_mask.detach().cpu()
|
| 62 |
-
if mask.ndim == 2:
|
| 63 |
-
mask = mask[0]
|
| 64 |
-
assert mask.ndim == 1, "Expected atom mask with shape [atoms]."
|
| 65 |
-
assert mask.shape[0] == selected_coords.shape[0], "Atom mask/coord size mismatch."
|
| 66 |
-
assert torch.any(mask > 0), "Atom mask has no valid atoms for CIF export."
|
| 67 |
-
valid_non_finite = torch.logical_not(torch.isfinite(selected_coords_tensor[mask > 0]))
|
| 68 |
-
assert not torch.any(valid_non_finite), (
|
| 69 |
-
"CIF export has non-finite coordinates in unmasked atoms. "
|
| 70 |
-
f"Non-finite count: {int(valid_non_finite.sum().item())}"
|
| 71 |
-
)
|
| 72 |
-
|
| 73 |
-
b_iso = _confidence_per_atom(
|
| 74 |
-
plddt=plddt,
|
| 75 |
-
atom_to_residue=structure_template.atom_residue_index,
|
| 76 |
-
num_atoms=structure_template.num_atoms,
|
| 77 |
-
sample_index=sample_index,
|
| 78 |
-
)
|
| 79 |
-
assert b_iso.shape[0] == structure_template.num_atoms
|
| 80 |
-
|
| 81 |
-
lines = [
|
| 82 |
-
"data_boltz2_prediction",
|
| 83 |
-
"#",
|
| 84 |
-
"loop_",
|
| 85 |
-
"_atom_site.group_PDB",
|
| 86 |
-
"_atom_site.id",
|
| 87 |
-
"_atom_site.type_symbol",
|
| 88 |
-
"_atom_site.label_atom_id",
|
| 89 |
-
"_atom_site.label_comp_id",
|
| 90 |
-
"_atom_site.label_asym_id",
|
| 91 |
-
"_atom_site.label_seq_id",
|
| 92 |
-
"_atom_site.Cartn_x",
|
| 93 |
-
"_atom_site.Cartn_y",
|
| 94 |
-
"_atom_site.Cartn_z",
|
| 95 |
-
"_atom_site.occupancy",
|
| 96 |
-
"_atom_site.B_iso_or_equiv",
|
| 97 |
-
"_atom_site.pdbx_PDB_model_num",
|
| 98 |
-
]
|
| 99 |
-
|
| 100 |
-
atom_id = 1
|
| 101 |
-
for idx in range(structure_template.num_atoms):
|
| 102 |
-
if mask[idx] <= 0:
|
| 103 |
-
continue
|
| 104 |
-
|
| 105 |
-
residue_idx = structure_template.atom_residue_index[idx]
|
| 106 |
-
residue_name = structure_template.residue_names[residue_idx]
|
| 107 |
-
atom_name = structure_template.atom_names[idx]
|
| 108 |
-
element = structure_template.atom_elements[idx]
|
| 109 |
-
chain_id = structure_template.atom_chain_id[idx]
|
| 110 |
-
x_val, y_val, z_val = selected_coords[idx].tolist()
|
| 111 |
-
b_factor = float(b_iso[idx])
|
| 112 |
-
|
| 113 |
-
line = (
|
| 114 |
-
f"ATOM {atom_id} {element} {atom_name} {residue_name} {chain_id} "
|
| 115 |
-
f"{residue_idx + 1} {x_val:.3f} {y_val:.3f} {z_val:.3f} 1.00 {b_factor:.2f} 1"
|
| 116 |
-
)
|
| 117 |
-
lines.append(line)
|
| 118 |
-
atom_id += 1
|
| 119 |
-
|
| 120 |
-
lines.append("#")
|
| 121 |
-
text = "\n".join(lines) + "\n"
|
| 122 |
-
|
| 123 |
-
out_path = Path(output_path)
|
| 124 |
-
out_path.parent.mkdir(parents=True, exist_ok=True)
|
| 125 |
-
out_path.write_text(text, encoding="utf-8")
|
| 126 |
-
return str(out_path)
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
from typing import List, Optional
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
from .minimal_structures import ProteinStructureTemplate
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _confidence_per_atom(
|
| 11 |
+
plddt: Optional[torch.Tensor],
|
| 12 |
+
atom_to_residue: List[int],
|
| 13 |
+
num_atoms: int,
|
| 14 |
+
sample_index: int,
|
| 15 |
+
) -> np.ndarray:
|
| 16 |
+
if plddt is None:
|
| 17 |
+
return np.ones((num_atoms,), dtype=np.float32) * 100.0
|
| 18 |
+
|
| 19 |
+
values = plddt.detach().cpu()
|
| 20 |
+
if values.ndim == 1:
|
| 21 |
+
values = values.unsqueeze(0)
|
| 22 |
+
assert values.ndim == 2, "Expected pLDDT with shape [samples, tokens/atoms]."
|
| 23 |
+
assert sample_index < values.shape[0], "sample_index out of range for pLDDT."
|
| 24 |
+
|
| 25 |
+
selected = values[sample_index]
|
| 26 |
+
if selected.shape[0] == num_atoms:
|
| 27 |
+
return (selected.numpy() * 100.0).astype(np.float32)
|
| 28 |
+
|
| 29 |
+
num_residues = max(atom_to_residue) + 1
|
| 30 |
+
if selected.shape[0] == num_residues:
|
| 31 |
+
expanded = np.zeros((num_atoms,), dtype=np.float32)
|
| 32 |
+
selected_np = selected.numpy()
|
| 33 |
+
for atom_idx, residue_idx in enumerate(atom_to_residue):
|
| 34 |
+
expanded[atom_idx] = selected_np[residue_idx] * 100.0
|
| 35 |
+
return expanded
|
| 36 |
+
|
| 37 |
+
return np.ones((num_atoms,), dtype=np.float32) * 100.0
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def write_cif(
|
| 41 |
+
structure_template: ProteinStructureTemplate,
|
| 42 |
+
atom_coords: torch.Tensor,
|
| 43 |
+
atom_mask: torch.Tensor,
|
| 44 |
+
output_path: str,
|
| 45 |
+
plddt: Optional[torch.Tensor] = None,
|
| 46 |
+
sample_index: int = 0,
|
| 47 |
+
) -> str:
|
| 48 |
+
coords = atom_coords.detach().cpu()
|
| 49 |
+
if coords.ndim == 2:
|
| 50 |
+
coords = coords.unsqueeze(0)
|
| 51 |
+
assert coords.ndim == 3, "Expected coordinates with shape [samples, atoms, 3]."
|
| 52 |
+
assert sample_index < coords.shape[0], "sample_index out of range."
|
| 53 |
+
selected_coords_tensor = coords[sample_index]
|
| 54 |
+
all_non_finite = torch.logical_not(torch.isfinite(selected_coords_tensor))
|
| 55 |
+
assert not torch.any(all_non_finite), (
|
| 56 |
+
"CIF export received non-finite coordinates. "
|
| 57 |
+
f"Non-finite count: {int(all_non_finite.sum().item())}"
|
| 58 |
+
)
|
| 59 |
+
selected_coords = selected_coords_tensor.numpy()
|
| 60 |
+
|
| 61 |
+
mask = atom_mask.detach().cpu()
|
| 62 |
+
if mask.ndim == 2:
|
| 63 |
+
mask = mask[0]
|
| 64 |
+
assert mask.ndim == 1, "Expected atom mask with shape [atoms]."
|
| 65 |
+
assert mask.shape[0] == selected_coords.shape[0], "Atom mask/coord size mismatch."
|
| 66 |
+
assert torch.any(mask > 0), "Atom mask has no valid atoms for CIF export."
|
| 67 |
+
valid_non_finite = torch.logical_not(torch.isfinite(selected_coords_tensor[mask > 0]))
|
| 68 |
+
assert not torch.any(valid_non_finite), (
|
| 69 |
+
"CIF export has non-finite coordinates in unmasked atoms. "
|
| 70 |
+
f"Non-finite count: {int(valid_non_finite.sum().item())}"
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
b_iso = _confidence_per_atom(
|
| 74 |
+
plddt=plddt,
|
| 75 |
+
atom_to_residue=structure_template.atom_residue_index,
|
| 76 |
+
num_atoms=structure_template.num_atoms,
|
| 77 |
+
sample_index=sample_index,
|
| 78 |
+
)
|
| 79 |
+
assert b_iso.shape[0] == structure_template.num_atoms
|
| 80 |
+
|
| 81 |
+
lines = [
|
| 82 |
+
"data_boltz2_prediction",
|
| 83 |
+
"#",
|
| 84 |
+
"loop_",
|
| 85 |
+
"_atom_site.group_PDB",
|
| 86 |
+
"_atom_site.id",
|
| 87 |
+
"_atom_site.type_symbol",
|
| 88 |
+
"_atom_site.label_atom_id",
|
| 89 |
+
"_atom_site.label_comp_id",
|
| 90 |
+
"_atom_site.label_asym_id",
|
| 91 |
+
"_atom_site.label_seq_id",
|
| 92 |
+
"_atom_site.Cartn_x",
|
| 93 |
+
"_atom_site.Cartn_y",
|
| 94 |
+
"_atom_site.Cartn_z",
|
| 95 |
+
"_atom_site.occupancy",
|
| 96 |
+
"_atom_site.B_iso_or_equiv",
|
| 97 |
+
"_atom_site.pdbx_PDB_model_num",
|
| 98 |
+
]
|
| 99 |
+
|
| 100 |
+
atom_id = 1
|
| 101 |
+
for idx in range(structure_template.num_atoms):
|
| 102 |
+
if mask[idx] <= 0:
|
| 103 |
+
continue
|
| 104 |
+
|
| 105 |
+
residue_idx = structure_template.atom_residue_index[idx]
|
| 106 |
+
residue_name = structure_template.residue_names[residue_idx]
|
| 107 |
+
atom_name = structure_template.atom_names[idx]
|
| 108 |
+
element = structure_template.atom_elements[idx]
|
| 109 |
+
chain_id = structure_template.atom_chain_id[idx]
|
| 110 |
+
x_val, y_val, z_val = selected_coords[idx].tolist()
|
| 111 |
+
b_factor = float(b_iso[idx])
|
| 112 |
+
|
| 113 |
+
line = (
|
| 114 |
+
f"ATOM {atom_id} {element} {atom_name} {residue_name} {chain_id} "
|
| 115 |
+
f"{residue_idx + 1} {x_val:.3f} {y_val:.3f} {z_val:.3f} 1.00 {b_factor:.2f} 1"
|
| 116 |
+
)
|
| 117 |
+
lines.append(line)
|
| 118 |
+
atom_id += 1
|
| 119 |
+
|
| 120 |
+
lines.append("#")
|
| 121 |
+
text = "\n".join(lines) + "\n"
|
| 122 |
+
|
| 123 |
+
out_path = Path(output_path)
|
| 124 |
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
| 125 |
+
out_path.write_text(text, encoding="utf-8")
|
| 126 |
+
return str(out_path)
|