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)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Synthyra/Boltz2", trust_remote_code=True, dtype="auto") - Notebooks
- Google Colab
- Kaggle
Upload vb_loss_diffusionv2.py with huggingface_hub
Browse files- vb_loss_diffusionv2.py +138 -138
vb_loss_diffusionv2.py
CHANGED
|
@@ -1,138 +1,138 @@
|
|
| 1 |
-
# started from code from https://github.com/lucidrains/alphafold3-pytorch, MIT License, Copyright (c) 2024 Phil Wang
|
| 2 |
-
|
| 3 |
-
import torch
|
| 4 |
-
import torch.nn.functional as F
|
| 5 |
-
from einops import einsum, rearrange
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
def weighted_rigid_align(
|
| 9 |
-
true_coords, # Float['b n 3'], # true coordinates
|
| 10 |
-
pred_coords, # Float['b n 3'], # predicted coordinates
|
| 11 |
-
weights, # Float['b n'], # weights for each atom
|
| 12 |
-
mask, # Bool['b n'] | None = None # mask for variable lengths
|
| 13 |
-
): # -> Float['b n 3']:
|
| 14 |
-
"""Algorithm 28 : note there is a problem with the pseudocode in the paper where predicted and
|
| 15 |
-
GT are swapped in algorithm 28, but correct in equation (2)."""
|
| 16 |
-
|
| 17 |
-
out_shape = torch.broadcast_shapes(true_coords.shape, pred_coords.shape)
|
| 18 |
-
*batch_size, num_points, dim = out_shape
|
| 19 |
-
weights = (mask * weights).unsqueeze(-1)
|
| 20 |
-
|
| 21 |
-
# Compute weighted centroids
|
| 22 |
-
true_centroid = (true_coords * weights).sum(dim=-2, keepdim=True) / weights.sum(
|
| 23 |
-
dim=-2, keepdim=True
|
| 24 |
-
)
|
| 25 |
-
pred_centroid = (pred_coords * weights).sum(dim=-2, keepdim=True) / weights.sum(
|
| 26 |
-
dim=-2, keepdim=True
|
| 27 |
-
)
|
| 28 |
-
|
| 29 |
-
# Center the coordinates
|
| 30 |
-
true_coords_centered = true_coords - true_centroid
|
| 31 |
-
pred_coords_centered = pred_coords - pred_centroid
|
| 32 |
-
|
| 33 |
-
if torch.any(mask.sum(dim=-1) < (dim + 1)):
|
| 34 |
-
print(
|
| 35 |
-
"Warning: The size of one of the point clouds is <= dim+1. "
|
| 36 |
-
+ "`WeightedRigidAlign` cannot return a unique rotation."
|
| 37 |
-
)
|
| 38 |
-
|
| 39 |
-
# Compute the weighted covariance matrix
|
| 40 |
-
cov_matrix = einsum(
|
| 41 |
-
weights * pred_coords_centered,
|
| 42 |
-
true_coords_centered,
|
| 43 |
-
"... n i, ... n j -> ... i j",
|
| 44 |
-
)
|
| 45 |
-
|
| 46 |
-
# Compute the SVD of the covariance matrix, required float32 for svd and determinant
|
| 47 |
-
original_dtype = cov_matrix.dtype
|
| 48 |
-
cov_matrix_32 = cov_matrix.to(dtype=torch.float32)
|
| 49 |
-
|
| 50 |
-
U, S, V = torch.linalg.svd(
|
| 51 |
-
cov_matrix_32, driver="gesvd" if cov_matrix_32.is_cuda else None
|
| 52 |
-
)
|
| 53 |
-
V = V.mH
|
| 54 |
-
|
| 55 |
-
# Catch ambiguous rotation by checking the magnitude of singular values
|
| 56 |
-
if (S.abs() <= 1e-15).any() and not (num_points < (dim + 1)):
|
| 57 |
-
print(
|
| 58 |
-
"Warning: Excessively low rank of "
|
| 59 |
-
+ "cross-correlation between aligned point clouds. "
|
| 60 |
-
+ "`WeightedRigidAlign` cannot return a unique rotation."
|
| 61 |
-
)
|
| 62 |
-
|
| 63 |
-
# Compute the rotation matrix
|
| 64 |
-
rot_matrix = torch.einsum("... i j, ... k j -> ... i k", U, V).to(
|
| 65 |
-
dtype=torch.float32
|
| 66 |
-
)
|
| 67 |
-
|
| 68 |
-
# Ensure proper rotation matrix with determinant 1
|
| 69 |
-
F = torch.eye(dim, dtype=cov_matrix_32.dtype, device=cov_matrix.device)[
|
| 70 |
-
None
|
| 71 |
-
].repeat(*batch_size, 1, 1)
|
| 72 |
-
F[..., -1, -1] = torch.det(rot_matrix)
|
| 73 |
-
rot_matrix = einsum(U, F, V, "... i j, ... j k, ... l k -> ... i l")
|
| 74 |
-
rot_matrix = rot_matrix.to(dtype=original_dtype)
|
| 75 |
-
|
| 76 |
-
# Apply the rotation and translation
|
| 77 |
-
aligned_coords = (
|
| 78 |
-
einsum(true_coords_centered, rot_matrix, "... n i, ... j i -> ... n j")
|
| 79 |
-
+ pred_centroid
|
| 80 |
-
)
|
| 81 |
-
aligned_coords.detach_()
|
| 82 |
-
|
| 83 |
-
return aligned_coords
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
def smooth_lddt_loss(
|
| 87 |
-
pred_coords, # Float['b n 3'],
|
| 88 |
-
true_coords, # Float['b n 3'],
|
| 89 |
-
is_nucleotide, # Bool['b n'],
|
| 90 |
-
coords_mask, # Bool['b n'] | None = None,
|
| 91 |
-
nucleic_acid_cutoff: float = 30.0,
|
| 92 |
-
other_cutoff: float = 15.0,
|
| 93 |
-
multiplicity: int = 1,
|
| 94 |
-
): # -> Float['']:
|
| 95 |
-
"""Algorithm 27
|
| 96 |
-
pred_coords: predicted coordinates
|
| 97 |
-
true_coords: true coordinates
|
| 98 |
-
Note: for efficiency pred_coords is the only one with the multiplicity expanded
|
| 99 |
-
TODO: add weighing which overweight the smooth lddt contribution close to t=0 (not present in the paper)
|
| 100 |
-
"""
|
| 101 |
-
lddt = []
|
| 102 |
-
for i in range(true_coords.shape[0]):
|
| 103 |
-
true_dists = torch.cdist(true_coords[i], true_coords[i])
|
| 104 |
-
|
| 105 |
-
is_nucleotide_i = is_nucleotide[i // multiplicity]
|
| 106 |
-
coords_mask_i = coords_mask[i // multiplicity]
|
| 107 |
-
|
| 108 |
-
is_nucleotide_pair = is_nucleotide_i.unsqueeze(-1).expand(
|
| 109 |
-
-1, is_nucleotide_i.shape[-1]
|
| 110 |
-
)
|
| 111 |
-
|
| 112 |
-
mask = is_nucleotide_pair * (true_dists < nucleic_acid_cutoff).float()
|
| 113 |
-
mask += (1 - is_nucleotide_pair) * (true_dists < other_cutoff).float()
|
| 114 |
-
mask *= 1 - torch.eye(pred_coords.shape[1], device=pred_coords.device)
|
| 115 |
-
mask *= coords_mask_i.unsqueeze(-1)
|
| 116 |
-
mask *= coords_mask_i.unsqueeze(-2)
|
| 117 |
-
|
| 118 |
-
valid_pairs = mask.nonzero()
|
| 119 |
-
true_dists_i = true_dists[valid_pairs[:, 0], valid_pairs[:, 1]]
|
| 120 |
-
|
| 121 |
-
pred_coords_i1 = pred_coords[i, valid_pairs[:, 0]]
|
| 122 |
-
pred_coords_i2 = pred_coords[i, valid_pairs[:, 1]]
|
| 123 |
-
pred_dists_i = F.pairwise_distance(pred_coords_i1, pred_coords_i2)
|
| 124 |
-
|
| 125 |
-
dist_diff_i = torch.abs(true_dists_i - pred_dists_i)
|
| 126 |
-
|
| 127 |
-
eps_i = (
|
| 128 |
-
F.sigmoid(0.5 - dist_diff_i)
|
| 129 |
-
+ F.sigmoid(1.0 - dist_diff_i)
|
| 130 |
-
+ F.sigmoid(2.0 - dist_diff_i)
|
| 131 |
-
+ F.sigmoid(4.0 - dist_diff_i)
|
| 132 |
-
) / 4.0
|
| 133 |
-
|
| 134 |
-
lddt_i = eps_i.sum() / (valid_pairs.shape[0] + 1e-5)
|
| 135 |
-
lddt.append(lddt_i)
|
| 136 |
-
|
| 137 |
-
# average over batch & multiplicity
|
| 138 |
-
return 1.0 - torch.stack(lddt, dim=0).mean(dim=0)
|
|
|
|
| 1 |
+
# started from code from https://github.com/lucidrains/alphafold3-pytorch, MIT License, Copyright (c) 2024 Phil Wang
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from einops import einsum, rearrange
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def weighted_rigid_align(
|
| 9 |
+
true_coords, # Float['b n 3'], # true coordinates
|
| 10 |
+
pred_coords, # Float['b n 3'], # predicted coordinates
|
| 11 |
+
weights, # Float['b n'], # weights for each atom
|
| 12 |
+
mask, # Bool['b n'] | None = None # mask for variable lengths
|
| 13 |
+
): # -> Float['b n 3']:
|
| 14 |
+
"""Algorithm 28 : note there is a problem with the pseudocode in the paper where predicted and
|
| 15 |
+
GT are swapped in algorithm 28, but correct in equation (2)."""
|
| 16 |
+
|
| 17 |
+
out_shape = torch.broadcast_shapes(true_coords.shape, pred_coords.shape)
|
| 18 |
+
*batch_size, num_points, dim = out_shape
|
| 19 |
+
weights = (mask * weights).unsqueeze(-1)
|
| 20 |
+
|
| 21 |
+
# Compute weighted centroids
|
| 22 |
+
true_centroid = (true_coords * weights).sum(dim=-2, keepdim=True) / weights.sum(
|
| 23 |
+
dim=-2, keepdim=True
|
| 24 |
+
)
|
| 25 |
+
pred_centroid = (pred_coords * weights).sum(dim=-2, keepdim=True) / weights.sum(
|
| 26 |
+
dim=-2, keepdim=True
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
# Center the coordinates
|
| 30 |
+
true_coords_centered = true_coords - true_centroid
|
| 31 |
+
pred_coords_centered = pred_coords - pred_centroid
|
| 32 |
+
|
| 33 |
+
if torch.any(mask.sum(dim=-1) < (dim + 1)):
|
| 34 |
+
print(
|
| 35 |
+
"Warning: The size of one of the point clouds is <= dim+1. "
|
| 36 |
+
+ "`WeightedRigidAlign` cannot return a unique rotation."
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
# Compute the weighted covariance matrix
|
| 40 |
+
cov_matrix = einsum(
|
| 41 |
+
weights * pred_coords_centered,
|
| 42 |
+
true_coords_centered,
|
| 43 |
+
"... n i, ... n j -> ... i j",
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
# Compute the SVD of the covariance matrix, required float32 for svd and determinant
|
| 47 |
+
original_dtype = cov_matrix.dtype
|
| 48 |
+
cov_matrix_32 = cov_matrix.to(dtype=torch.float32)
|
| 49 |
+
|
| 50 |
+
U, S, V = torch.linalg.svd(
|
| 51 |
+
cov_matrix_32, driver="gesvd" if cov_matrix_32.is_cuda else None
|
| 52 |
+
)
|
| 53 |
+
V = V.mH
|
| 54 |
+
|
| 55 |
+
# Catch ambiguous rotation by checking the magnitude of singular values
|
| 56 |
+
if (S.abs() <= 1e-15).any() and not (num_points < (dim + 1)):
|
| 57 |
+
print(
|
| 58 |
+
"Warning: Excessively low rank of "
|
| 59 |
+
+ "cross-correlation between aligned point clouds. "
|
| 60 |
+
+ "`WeightedRigidAlign` cannot return a unique rotation."
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
# Compute the rotation matrix
|
| 64 |
+
rot_matrix = torch.einsum("... i j, ... k j -> ... i k", U, V).to(
|
| 65 |
+
dtype=torch.float32
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# Ensure proper rotation matrix with determinant 1
|
| 69 |
+
F = torch.eye(dim, dtype=cov_matrix_32.dtype, device=cov_matrix.device)[
|
| 70 |
+
None
|
| 71 |
+
].repeat(*batch_size, 1, 1)
|
| 72 |
+
F[..., -1, -1] = torch.det(rot_matrix)
|
| 73 |
+
rot_matrix = einsum(U, F, V, "... i j, ... j k, ... l k -> ... i l")
|
| 74 |
+
rot_matrix = rot_matrix.to(dtype=original_dtype)
|
| 75 |
+
|
| 76 |
+
# Apply the rotation and translation
|
| 77 |
+
aligned_coords = (
|
| 78 |
+
einsum(true_coords_centered, rot_matrix, "... n i, ... j i -> ... n j")
|
| 79 |
+
+ pred_centroid
|
| 80 |
+
)
|
| 81 |
+
aligned_coords.detach_()
|
| 82 |
+
|
| 83 |
+
return aligned_coords
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def smooth_lddt_loss(
|
| 87 |
+
pred_coords, # Float['b n 3'],
|
| 88 |
+
true_coords, # Float['b n 3'],
|
| 89 |
+
is_nucleotide, # Bool['b n'],
|
| 90 |
+
coords_mask, # Bool['b n'] | None = None,
|
| 91 |
+
nucleic_acid_cutoff: float = 30.0,
|
| 92 |
+
other_cutoff: float = 15.0,
|
| 93 |
+
multiplicity: int = 1,
|
| 94 |
+
): # -> Float['']:
|
| 95 |
+
"""Algorithm 27
|
| 96 |
+
pred_coords: predicted coordinates
|
| 97 |
+
true_coords: true coordinates
|
| 98 |
+
Note: for efficiency pred_coords is the only one with the multiplicity expanded
|
| 99 |
+
TODO: add weighing which overweight the smooth lddt contribution close to t=0 (not present in the paper)
|
| 100 |
+
"""
|
| 101 |
+
lddt = []
|
| 102 |
+
for i in range(true_coords.shape[0]):
|
| 103 |
+
true_dists = torch.cdist(true_coords[i], true_coords[i])
|
| 104 |
+
|
| 105 |
+
is_nucleotide_i = is_nucleotide[i // multiplicity]
|
| 106 |
+
coords_mask_i = coords_mask[i // multiplicity]
|
| 107 |
+
|
| 108 |
+
is_nucleotide_pair = is_nucleotide_i.unsqueeze(-1).expand(
|
| 109 |
+
-1, is_nucleotide_i.shape[-1]
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
mask = is_nucleotide_pair * (true_dists < nucleic_acid_cutoff).float()
|
| 113 |
+
mask += (1 - is_nucleotide_pair) * (true_dists < other_cutoff).float()
|
| 114 |
+
mask *= 1 - torch.eye(pred_coords.shape[1], device=pred_coords.device)
|
| 115 |
+
mask *= coords_mask_i.unsqueeze(-1)
|
| 116 |
+
mask *= coords_mask_i.unsqueeze(-2)
|
| 117 |
+
|
| 118 |
+
valid_pairs = mask.nonzero()
|
| 119 |
+
true_dists_i = true_dists[valid_pairs[:, 0], valid_pairs[:, 1]]
|
| 120 |
+
|
| 121 |
+
pred_coords_i1 = pred_coords[i, valid_pairs[:, 0]]
|
| 122 |
+
pred_coords_i2 = pred_coords[i, valid_pairs[:, 1]]
|
| 123 |
+
pred_dists_i = F.pairwise_distance(pred_coords_i1, pred_coords_i2)
|
| 124 |
+
|
| 125 |
+
dist_diff_i = torch.abs(true_dists_i - pred_dists_i)
|
| 126 |
+
|
| 127 |
+
eps_i = (
|
| 128 |
+
F.sigmoid(0.5 - dist_diff_i)
|
| 129 |
+
+ F.sigmoid(1.0 - dist_diff_i)
|
| 130 |
+
+ F.sigmoid(2.0 - dist_diff_i)
|
| 131 |
+
+ F.sigmoid(4.0 - dist_diff_i)
|
| 132 |
+
) / 4.0
|
| 133 |
+
|
| 134 |
+
lddt_i = eps_i.sum() / (valid_pairs.shape[0] + 1e-5)
|
| 135 |
+
lddt.append(lddt_i)
|
| 136 |
+
|
| 137 |
+
# average over batch & multiplicity
|
| 138 |
+
return 1.0 - torch.stack(lddt, dim=0).mean(dim=0)
|