lhallee commited on
Commit
69d2b14
·
verified ·
1 Parent(s): 2833e41

Upload vb_loss_diffusionv2.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. 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)