lhallee commited on
Commit
a284246
·
verified ·
1 Parent(s): 61d2004

Upload vb_modules_trunkv2.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. vb_modules_trunkv2.py +833 -833
vb_modules_trunkv2.py CHANGED
@@ -1,833 +1,833 @@
1
- from typing import Dict, Tuple
2
-
3
- import torch
4
- from torch import Tensor, nn
5
- from torch.nn.functional import one_hot
6
-
7
- from . import vb_const as const
8
- from .vb_layers_outer_product_mean import OuterProductMean
9
- from .vb_layers_pair_averaging import PairWeightedAveraging
10
- from .vb_layers_pairformer import (
11
- PairformerNoSeqLayer,
12
- PairformerNoSeqModule,
13
- get_dropout_mask,
14
- )
15
- from .vb_layers_transition import Transition
16
- from .vb_modules_encodersv2 import (
17
- AtomAttentionEncoder,
18
- AtomEncoder,
19
- FourierEmbedding,
20
- )
21
-
22
-
23
- class ContactConditioning(nn.Module):
24
- def __init__(self, token_z: int, cutoff_min: float, cutoff_max: float):
25
- super().__init__()
26
-
27
- self.fourier_embedding = FourierEmbedding(token_z)
28
- self.encoder = nn.Linear(
29
- token_z + len(const.contact_conditioning_info) - 1, token_z
30
- )
31
- self.encoding_unspecified = nn.Parameter(torch.zeros(token_z))
32
- self.encoding_unselected = nn.Parameter(torch.zeros(token_z))
33
- self.cutoff_min = cutoff_min
34
- self.cutoff_max = cutoff_max
35
-
36
- def forward(self, feats):
37
- assert const.contact_conditioning_info["UNSPECIFIED"] == 0
38
- assert const.contact_conditioning_info["UNSELECTED"] == 1
39
- contact_conditioning = feats["contact_conditioning"][:, :, :, 2:]
40
- contact_threshold = feats["contact_threshold"]
41
- contact_threshold_normalized = (contact_threshold - self.cutoff_min) / (
42
- self.cutoff_max - self.cutoff_min
43
- )
44
- contact_threshold_fourier = self.fourier_embedding(
45
- contact_threshold_normalized.flatten()
46
- ).reshape(contact_threshold_normalized.shape + (-1,))
47
-
48
- contact_conditioning = torch.cat(
49
- [
50
- contact_conditioning,
51
- contact_threshold_normalized.unsqueeze(-1),
52
- contact_threshold_fourier,
53
- ],
54
- dim=-1,
55
- )
56
- contact_conditioning = self.encoder(contact_conditioning)
57
-
58
- contact_conditioning = (
59
- contact_conditioning
60
- * (
61
- 1
62
- - feats["contact_conditioning"][:, :, :, 0:2].sum(dim=-1, keepdim=True)
63
- )
64
- + self.encoding_unspecified * feats["contact_conditioning"][:, :, :, 0:1]
65
- + self.encoding_unselected * feats["contact_conditioning"][:, :, :, 1:2]
66
- )
67
- return contact_conditioning
68
-
69
-
70
- class InputEmbedder(nn.Module):
71
- def __init__(
72
- self,
73
- atom_s: int,
74
- atom_z: int,
75
- token_s: int,
76
- token_z: int,
77
- atoms_per_window_queries: int,
78
- atoms_per_window_keys: int,
79
- atom_feature_dim: int,
80
- atom_encoder_depth: int,
81
- atom_encoder_heads: int,
82
- activation_checkpointing: bool = False,
83
- add_method_conditioning: bool = False,
84
- add_modified_flag: bool = False,
85
- add_cyclic_flag: bool = False,
86
- add_mol_type_feat: bool = False,
87
- use_no_atom_char: bool = False,
88
- use_atom_backbone_feat: bool = False,
89
- use_residue_feats_atoms: bool = False,
90
- ) -> None:
91
- """Initialize the input embedder.
92
-
93
- Parameters
94
- ----------
95
- atom_s : int
96
- The atom embedding size.
97
- atom_z : int
98
- The atom pairwise embedding size.
99
- token_s : int
100
- The token embedding size.
101
-
102
- """
103
- super().__init__()
104
- self.token_s = token_s
105
- self.add_method_conditioning = add_method_conditioning
106
- self.add_modified_flag = add_modified_flag
107
- self.add_cyclic_flag = add_cyclic_flag
108
- self.add_mol_type_feat = add_mol_type_feat
109
-
110
- self.atom_encoder = AtomEncoder(
111
- atom_s=atom_s,
112
- atom_z=atom_z,
113
- token_s=token_s,
114
- token_z=token_z,
115
- atoms_per_window_queries=atoms_per_window_queries,
116
- atoms_per_window_keys=atoms_per_window_keys,
117
- atom_feature_dim=atom_feature_dim,
118
- structure_prediction=False,
119
- use_no_atom_char=use_no_atom_char,
120
- use_atom_backbone_feat=use_atom_backbone_feat,
121
- use_residue_feats_atoms=use_residue_feats_atoms,
122
- )
123
-
124
- self.atom_enc_proj_z = nn.Sequential(
125
- nn.LayerNorm(atom_z),
126
- nn.Linear(atom_z, atom_encoder_depth * atom_encoder_heads, bias=False),
127
- )
128
-
129
- self.atom_attention_encoder = AtomAttentionEncoder(
130
- atom_s=atom_s,
131
- token_s=token_s,
132
- atoms_per_window_queries=atoms_per_window_queries,
133
- atoms_per_window_keys=atoms_per_window_keys,
134
- atom_encoder_depth=atom_encoder_depth,
135
- atom_encoder_heads=atom_encoder_heads,
136
- structure_prediction=False,
137
- activation_checkpointing=activation_checkpointing,
138
- )
139
-
140
- self.res_type_encoding = nn.Linear(const.num_tokens, token_s, bias=False)
141
- self.msa_profile_encoding = nn.Linear(const.num_tokens + 1, token_s, bias=False)
142
-
143
- if add_method_conditioning:
144
- self.method_conditioning_init = nn.Embedding(
145
- const.num_method_types, token_s
146
- )
147
- self.method_conditioning_init.weight.data.fill_(0)
148
- if add_modified_flag:
149
- self.modified_conditioning_init = nn.Embedding(2, token_s)
150
- self.modified_conditioning_init.weight.data.fill_(0)
151
- if add_cyclic_flag:
152
- self.cyclic_conditioning_init = nn.Linear(1, token_s, bias=False)
153
- self.cyclic_conditioning_init.weight.data.fill_(0)
154
- if add_mol_type_feat:
155
- self.mol_type_conditioning_init = nn.Embedding(
156
- len(const.chain_type_ids), token_s
157
- )
158
- self.mol_type_conditioning_init.weight.data.fill_(0)
159
-
160
- def forward(self, feats: Dict[str, Tensor], affinity: bool = False) -> Tensor:
161
- """Perform the forward pass.
162
-
163
- Parameters
164
- ----------
165
- feats : dict[str, Tensor]
166
- Input features
167
-
168
- Returns
169
- -------
170
- Tensor
171
- The embedded tokens.
172
-
173
- """
174
- # Load relevant features
175
- res_type = feats["res_type"].float()
176
- if affinity:
177
- profile = feats["profile_affinity"]
178
- deletion_mean = feats["deletion_mean_affinity"].unsqueeze(-1)
179
- else:
180
- profile = feats["profile"]
181
- deletion_mean = feats["deletion_mean"].unsqueeze(-1)
182
-
183
- # Compute input embedding
184
- q, c, p, to_keys = self.atom_encoder(feats)
185
- atom_enc_bias = self.atom_enc_proj_z(p)
186
- a, _, _, _ = self.atom_attention_encoder(
187
- feats=feats,
188
- q=q,
189
- c=c,
190
- atom_enc_bias=atom_enc_bias,
191
- to_keys=to_keys,
192
- )
193
-
194
- s = (
195
- a
196
- + self.res_type_encoding(res_type)
197
- + self.msa_profile_encoding(torch.cat([profile, deletion_mean], dim=-1))
198
- )
199
-
200
- if self.add_method_conditioning:
201
- s = s + self.method_conditioning_init(feats["method_feature"])
202
- if self.add_modified_flag:
203
- s = s + self.modified_conditioning_init(feats["modified"])
204
- if self.add_cyclic_flag:
205
- cyclic = feats["cyclic_period"].clamp(max=1.0).unsqueeze(-1)
206
- s = s + self.cyclic_conditioning_init(cyclic)
207
- if self.add_mol_type_feat:
208
- s = s + self.mol_type_conditioning_init(feats["mol_type"])
209
-
210
- return s
211
-
212
-
213
- class TemplateModule(nn.Module):
214
- """Template module."""
215
-
216
- def __init__(
217
- self,
218
- token_z: int,
219
- template_dim: int,
220
- template_blocks: int,
221
- dropout: float = 0.25,
222
- pairwise_head_width: int = 32,
223
- pairwise_num_heads: int = 4,
224
- post_layer_norm: bool = False,
225
- activation_checkpointing: bool = False,
226
- min_dist: float = 3.25,
227
- max_dist: float = 50.75,
228
- num_bins: int = 38,
229
- **kwargs,
230
- ) -> None:
231
- """Initialize the template module.
232
-
233
- Parameters
234
- ----------
235
- token_z : int
236
- The token pairwise embedding size.
237
-
238
- """
239
- super().__init__()
240
- self.min_dist = min_dist
241
- self.max_dist = max_dist
242
- self.num_bins = num_bins
243
- self.relu = nn.ReLU()
244
- self.z_norm = nn.LayerNorm(token_z)
245
- self.v_norm = nn.LayerNorm(template_dim)
246
- self.z_proj = nn.Linear(token_z, template_dim, bias=False)
247
- self.a_proj = nn.Linear(
248
- const.num_tokens * 2 + num_bins + 5,
249
- template_dim,
250
- bias=False,
251
- )
252
- self.u_proj = nn.Linear(template_dim, token_z, bias=False)
253
- self.pairformer = PairformerNoSeqModule(
254
- template_dim,
255
- num_blocks=template_blocks,
256
- dropout=dropout,
257
- pairwise_head_width=pairwise_head_width,
258
- pairwise_num_heads=pairwise_num_heads,
259
- post_layer_norm=post_layer_norm,
260
- activation_checkpointing=activation_checkpointing,
261
- )
262
-
263
- def forward(
264
- self,
265
- z: Tensor,
266
- feats: Dict[str, Tensor],
267
- pair_mask: Tensor,
268
- use_kernels: bool = False,
269
- ) -> Tensor:
270
- """Perform the forward pass.
271
-
272
- Parameters
273
- ----------
274
- z : Tensor
275
- The pairwise embeddings
276
- feats : dict[str, Tensor]
277
- Input features
278
- pair_mask : Tensor
279
- The pair mask
280
-
281
- Returns
282
- -------
283
- Tensor
284
- The updated pairwise embeddings.
285
-
286
- """
287
- # Load relevant features
288
- asym_id = feats["asym_id"]
289
- res_type = feats["template_restype"]
290
- frame_rot = feats["template_frame_rot"]
291
- frame_t = feats["template_frame_t"]
292
- frame_mask = feats["template_mask_frame"]
293
- cb_coords = feats["template_cb"]
294
- ca_coords = feats["template_ca"]
295
- cb_mask = feats["template_mask_cb"]
296
- template_mask = feats["template_mask"].any(dim=2).float()
297
- num_templates = template_mask.sum(dim=1)
298
- num_templates = num_templates.clamp(min=1)
299
-
300
- # Compute pairwise masks
301
- b_cb_mask = cb_mask[:, :, :, None] * cb_mask[:, :, None, :]
302
- b_frame_mask = frame_mask[:, :, :, None] * frame_mask[:, :, None, :]
303
-
304
- b_cb_mask = b_cb_mask[..., None]
305
- b_frame_mask = b_frame_mask[..., None]
306
-
307
- # Compute asym mask, template features only attend within the same chain
308
- B, T = res_type.shape[:2] # noqa: N806
309
- asym_mask = (asym_id[:, :, None] == asym_id[:, None, :]).float()
310
- asym_mask = asym_mask[:, None].expand(-1, T, -1, -1)
311
-
312
- # Compute template features
313
- with torch.autocast(device_type="cuda", enabled=False):
314
- # Compute distogram
315
- cb_dists = torch.cdist(cb_coords, cb_coords)
316
- boundaries = torch.linspace(self.min_dist, self.max_dist, self.num_bins - 1)
317
- boundaries = boundaries.to(cb_dists.device)
318
- distogram = (cb_dists[..., None] > boundaries).sum(dim=-1).long()
319
- distogram = one_hot(distogram, num_classes=self.num_bins)
320
-
321
- # Compute unit vector in each frame
322
- frame_rot = frame_rot.unsqueeze(2).transpose(-1, -2)
323
- frame_t = frame_t.unsqueeze(2).unsqueeze(-1)
324
- ca_coords = ca_coords.unsqueeze(3).unsqueeze(-1)
325
- vector = torch.matmul(frame_rot, (ca_coords - frame_t))
326
- norm = torch.norm(vector, dim=-1, keepdim=True)
327
- unit_vector = torch.where(norm > 0, vector / norm, torch.zeros_like(vector))
328
- unit_vector = unit_vector.squeeze(-1)
329
-
330
- # Concatenate input features
331
- a_tij = [distogram, b_cb_mask, unit_vector, b_frame_mask]
332
- a_tij = torch.cat(a_tij, dim=-1)
333
- a_tij = a_tij * asym_mask.unsqueeze(-1)
334
-
335
- res_type_i = res_type[:, :, :, None]
336
- res_type_j = res_type[:, :, None, :]
337
- res_type_i = res_type_i.expand(-1, -1, -1, res_type.size(2), -1)
338
- res_type_j = res_type_j.expand(-1, -1, res_type.size(2), -1, -1)
339
- a_tij = torch.cat([a_tij, res_type_i, res_type_j], dim=-1)
340
- a_tij = self.a_proj(a_tij)
341
-
342
- # Expand mask
343
- pair_mask = pair_mask[:, None].expand(-1, T, -1, -1)
344
- pair_mask = pair_mask.reshape(B * T, *pair_mask.shape[2:])
345
-
346
- # Compute input projections
347
- v = self.z_proj(self.z_norm(z[:, None])) + a_tij
348
- v = v.view(B * T, *v.shape[2:])
349
- v = v + self.pairformer(v, pair_mask, use_kernels=use_kernels)
350
- v = self.v_norm(v)
351
- v = v.view(B, T, *v.shape[1:])
352
-
353
- # Aggregate templates
354
- template_mask = template_mask[:, :, None, None, None]
355
- num_templates = num_templates[:, None, None, None]
356
- u = (v * template_mask).sum(dim=1) / num_templates.to(v)
357
-
358
- # Compute output projection
359
- u = self.u_proj(self.relu(u))
360
- return u
361
-
362
-
363
- class TemplateV2Module(nn.Module):
364
- """Template module."""
365
-
366
- def __init__(
367
- self,
368
- token_z: int,
369
- template_dim: int,
370
- template_blocks: int,
371
- dropout: float = 0.25,
372
- pairwise_head_width: int = 32,
373
- pairwise_num_heads: int = 4,
374
- post_layer_norm: bool = False,
375
- activation_checkpointing: bool = False,
376
- min_dist: float = 3.25,
377
- max_dist: float = 50.75,
378
- num_bins: int = 38,
379
- **kwargs,
380
- ) -> None:
381
- """Initialize the template module.
382
-
383
- Parameters
384
- ----------
385
- token_z : int
386
- The token pairwise embedding size.
387
-
388
- """
389
- super().__init__()
390
- self.min_dist = min_dist
391
- self.max_dist = max_dist
392
- self.num_bins = num_bins
393
- self.relu = nn.ReLU()
394
- self.z_norm = nn.LayerNorm(token_z)
395
- self.v_norm = nn.LayerNorm(template_dim)
396
- self.z_proj = nn.Linear(token_z, template_dim, bias=False)
397
- self.a_proj = nn.Linear(
398
- const.num_tokens * 2 + num_bins + 5,
399
- template_dim,
400
- bias=False,
401
- )
402
- self.u_proj = nn.Linear(template_dim, token_z, bias=False)
403
- self.pairformer = PairformerNoSeqModule(
404
- template_dim,
405
- num_blocks=template_blocks,
406
- dropout=dropout,
407
- pairwise_head_width=pairwise_head_width,
408
- pairwise_num_heads=pairwise_num_heads,
409
- post_layer_norm=post_layer_norm,
410
- activation_checkpointing=activation_checkpointing,
411
- )
412
-
413
- def forward(
414
- self,
415
- z: Tensor,
416
- feats: Dict[str, Tensor],
417
- pair_mask: Tensor,
418
- use_kernels: bool = False,
419
- ) -> Tensor:
420
- """Perform the forward pass.
421
-
422
- Parameters
423
- ----------
424
- z : Tensor
425
- The pairwise embeddings
426
- feats : dict[str, Tensor]
427
- Input features
428
- pair_mask : Tensor
429
- The pair mask
430
-
431
- Returns
432
- -------
433
- Tensor
434
- The updated pairwise embeddings.
435
-
436
- """
437
- # Load relevant features
438
- res_type = feats["template_restype"]
439
- frame_rot = feats["template_frame_rot"]
440
- frame_t = feats["template_frame_t"]
441
- frame_mask = feats["template_mask_frame"]
442
- cb_coords = feats["template_cb"]
443
- ca_coords = feats["template_ca"]
444
- cb_mask = feats["template_mask_cb"]
445
- visibility_ids = feats["visibility_ids"]
446
- template_mask = feats["template_mask"].any(dim=2).float()
447
- num_templates = template_mask.sum(dim=1)
448
- num_templates = num_templates.clamp(min=1)
449
-
450
- # Compute pairwise masks
451
- b_cb_mask = cb_mask[:, :, :, None] * cb_mask[:, :, None, :]
452
- b_frame_mask = frame_mask[:, :, :, None] * frame_mask[:, :, None, :]
453
-
454
- b_cb_mask = b_cb_mask[..., None]
455
- b_frame_mask = b_frame_mask[..., None]
456
-
457
- # Compute asym mask, template features only attend within the same chain
458
- B, T = res_type.shape[:2] # noqa: N806
459
- tmlp_pair_mask = (
460
- visibility_ids[:, :, :, None] == visibility_ids[:, :, None, :]
461
- ).float()
462
-
463
- # Compute template features
464
- with torch.autocast(device_type="cuda", enabled=False):
465
- # Compute distogram
466
- cb_dists = torch.cdist(cb_coords, cb_coords)
467
- boundaries = torch.linspace(self.min_dist, self.max_dist, self.num_bins - 1)
468
- boundaries = boundaries.to(cb_dists.device)
469
- distogram = (cb_dists[..., None] > boundaries).sum(dim=-1).long()
470
- distogram = one_hot(distogram, num_classes=self.num_bins)
471
-
472
- # Compute unit vector in each frame
473
- frame_rot = frame_rot.unsqueeze(2).transpose(-1, -2)
474
- frame_t = frame_t.unsqueeze(2).unsqueeze(-1)
475
- ca_coords = ca_coords.unsqueeze(3).unsqueeze(-1)
476
- vector = torch.matmul(frame_rot, (ca_coords - frame_t))
477
- norm = torch.norm(vector, dim=-1, keepdim=True)
478
- unit_vector = torch.where(norm > 0, vector / norm, torch.zeros_like(vector))
479
- unit_vector = unit_vector.squeeze(-1)
480
-
481
- # Concatenate input features
482
- a_tij = [distogram, b_cb_mask, unit_vector, b_frame_mask]
483
- a_tij = torch.cat(a_tij, dim=-1)
484
- a_tij = a_tij * tmlp_pair_mask.unsqueeze(-1)
485
-
486
- res_type_i = res_type[:, :, :, None]
487
- res_type_j = res_type[:, :, None, :]
488
- res_type_i = res_type_i.expand(-1, -1, -1, res_type.size(2), -1)
489
- res_type_j = res_type_j.expand(-1, -1, res_type.size(2), -1, -1)
490
- a_tij = torch.cat([a_tij, res_type_i, res_type_j], dim=-1)
491
- a_tij = self.a_proj(a_tij)
492
-
493
- # Expand mask
494
- pair_mask = pair_mask[:, None].expand(-1, T, -1, -1)
495
- pair_mask = pair_mask.reshape(B * T, *pair_mask.shape[2:])
496
-
497
- # Compute input projections
498
- v = self.z_proj(self.z_norm(z[:, None])) + a_tij
499
- v = v.view(B * T, *v.shape[2:])
500
- v = v + self.pairformer(v, pair_mask, use_kernels=use_kernels)
501
- v = self.v_norm(v)
502
- v = v.view(B, T, *v.shape[1:])
503
-
504
- # Aggregate templates
505
- template_mask = template_mask[:, :, None, None, None]
506
- num_templates = num_templates[:, None, None, None]
507
- u = (v * template_mask).sum(dim=1) / num_templates.to(v)
508
-
509
- # Compute output projection
510
- u = self.u_proj(self.relu(u))
511
- return u
512
-
513
-
514
- class MSAModule(nn.Module):
515
- """MSA module."""
516
-
517
- def __init__(
518
- self,
519
- msa_s: int,
520
- token_z: int,
521
- token_s: int,
522
- msa_blocks: int,
523
- msa_dropout: float,
524
- z_dropout: float,
525
- pairwise_head_width: int = 32,
526
- pairwise_num_heads: int = 4,
527
- activation_checkpointing: bool = False,
528
- use_paired_feature: bool = True,
529
- subsample_msa: bool = False,
530
- num_subsampled_msa: int = 1024,
531
- **kwargs,
532
- ) -> None:
533
- """Initialize the MSA module.
534
-
535
- Parameters
536
- ----------
537
- token_z : int
538
- The token pairwise embedding size.
539
-
540
- """
541
- super().__init__()
542
- self.msa_blocks = msa_blocks
543
- self.msa_dropout = msa_dropout
544
- self.z_dropout = z_dropout
545
- self.use_paired_feature = use_paired_feature
546
- self.activation_checkpointing = activation_checkpointing
547
- self.subsample_msa = subsample_msa
548
- self.num_subsampled_msa = num_subsampled_msa
549
-
550
- self.s_proj = nn.Linear(token_s, msa_s, bias=False)
551
- self.msa_proj = nn.Linear(
552
- const.num_tokens + 2 + int(use_paired_feature),
553
- msa_s,
554
- bias=False,
555
- )
556
- self.layers = nn.ModuleList()
557
- for i in range(msa_blocks):
558
- self.layers.append(
559
- MSALayer(
560
- msa_s,
561
- token_z,
562
- msa_dropout,
563
- z_dropout,
564
- pairwise_head_width,
565
- pairwise_num_heads,
566
- )
567
- )
568
-
569
- def forward(
570
- self,
571
- z: Tensor,
572
- emb: Tensor,
573
- feats: Dict[str, Tensor],
574
- use_kernels: bool = False,
575
- ) -> Tensor:
576
- """Perform the forward pass.
577
-
578
- Parameters
579
- ----------
580
- z : Tensor
581
- The pairwise embeddings
582
- emb : Tensor
583
- The input embeddings
584
- feats : dict[str, Tensor]
585
- Input features
586
- use_kernels: bool
587
- Whether to use kernels for triangular updates
588
-
589
- Returns
590
- -------
591
- Tensor
592
- The output pairwise embeddings.
593
-
594
- """
595
- # Set chunk sizes
596
- if not self.training:
597
- if z.shape[1] > const.chunk_size_threshold:
598
- chunk_heads_pwa = True
599
- chunk_size_transition_z = 64
600
- chunk_size_transition_msa = 32
601
- chunk_size_outer_product = 4
602
- chunk_size_tri_attn = 128
603
- else:
604
- chunk_heads_pwa = False
605
- chunk_size_transition_z = None
606
- chunk_size_transition_msa = None
607
- chunk_size_outer_product = None
608
- chunk_size_tri_attn = 512
609
- else:
610
- chunk_heads_pwa = False
611
- chunk_size_transition_z = None
612
- chunk_size_transition_msa = None
613
- chunk_size_outer_product = None
614
- chunk_size_tri_attn = None
615
-
616
- # Load relevant features
617
- msa = feats["msa"]
618
- if msa.dtype in (torch.long, torch.int32, torch.int64):
619
- msa = torch.nn.functional.one_hot(msa, num_classes=const.num_tokens).float()
620
- # else: already float one-hot (soft/differentiable path)
621
- has_deletion = feats["has_deletion"].unsqueeze(-1)
622
- deletion_value = feats["deletion_value"].unsqueeze(-1)
623
- is_paired = feats["msa_paired"].unsqueeze(-1)
624
- msa_mask = feats["msa_mask"]
625
- token_mask = feats["token_pad_mask"].float()
626
- token_mask = token_mask[:, :, None] * token_mask[:, None, :]
627
-
628
- # Compute MSA embeddings
629
- if self.use_paired_feature:
630
- m = torch.cat([msa, has_deletion, deletion_value, is_paired], dim=-1)
631
- else:
632
- m = torch.cat([msa, has_deletion, deletion_value], dim=-1)
633
-
634
- # Subsample the MSA
635
- if self.subsample_msa:
636
- msa_indices = torch.randperm(msa.shape[1])[: self.num_subsampled_msa]
637
- m = m[:, msa_indices]
638
- msa_mask = msa_mask[:, msa_indices]
639
-
640
- # Compute input projections
641
- m = self.msa_proj(m)
642
- m = m + self.s_proj(emb).unsqueeze(1)
643
-
644
- # Perform MSA blocks
645
- for i in range(self.msa_blocks):
646
- if self.activation_checkpointing:
647
- z, m = torch.utils.checkpoint.checkpoint(
648
- self.layers[i],
649
- z,
650
- m,
651
- token_mask,
652
- msa_mask,
653
- chunk_heads_pwa,
654
- chunk_size_transition_z,
655
- chunk_size_transition_msa,
656
- chunk_size_outer_product,
657
- chunk_size_tri_attn,
658
- use_kernels,
659
- use_reentrant=False,
660
- )
661
- else:
662
- z, m = self.layers[i](
663
- z,
664
- m,
665
- token_mask,
666
- msa_mask,
667
- chunk_heads_pwa,
668
- chunk_size_transition_z,
669
- chunk_size_transition_msa,
670
- chunk_size_outer_product,
671
- chunk_size_tri_attn,
672
- use_kernels,
673
- )
674
- return z
675
-
676
-
677
- class MSALayer(nn.Module):
678
- """MSA module."""
679
-
680
- def __init__(
681
- self,
682
- msa_s: int,
683
- token_z: int,
684
- msa_dropout: float,
685
- z_dropout: float,
686
- pairwise_head_width: int = 32,
687
- pairwise_num_heads: int = 4,
688
- ) -> None:
689
- """Initialize the MSA module.
690
-
691
- Parameters
692
- ----------
693
- token_z : int
694
- The token pairwise embedding size.
695
-
696
- """
697
- super().__init__()
698
- self.msa_dropout = msa_dropout
699
- self.msa_transition = Transition(dim=msa_s, hidden=msa_s * 4)
700
- self.pair_weighted_averaging = PairWeightedAveraging(
701
- c_m=msa_s,
702
- c_z=token_z,
703
- c_h=32,
704
- num_heads=8,
705
- )
706
-
707
- self.pairformer_layer = PairformerNoSeqLayer(
708
- token_z=token_z,
709
- dropout=z_dropout,
710
- pairwise_head_width=pairwise_head_width,
711
- pairwise_num_heads=pairwise_num_heads,
712
- )
713
- self.outer_product_mean = OuterProductMean(
714
- c_in=msa_s,
715
- c_hidden=32,
716
- c_out=token_z,
717
- )
718
-
719
- def forward(
720
- self,
721
- z: Tensor,
722
- m: Tensor,
723
- token_mask: Tensor,
724
- msa_mask: Tensor,
725
- chunk_heads_pwa: bool = False,
726
- chunk_size_transition_z: int = None,
727
- chunk_size_transition_msa: int = None,
728
- chunk_size_outer_product: int = None,
729
- chunk_size_tri_attn: int = None,
730
- use_kernels: bool = False,
731
- ) -> Tuple[Tensor, Tensor]:
732
- """Perform the forward pass.
733
-
734
- Parameters
735
- ----------
736
- z : Tensor
737
- The pairwise embeddings
738
- emb : Tensor
739
- The input embeddings
740
- feats : dict[str, Tensor]
741
- Input features
742
-
743
- Returns
744
- -------
745
- Tensor
746
- The output pairwise embeddings.
747
-
748
- """
749
- # Communication to MSA stack
750
- msa_dropout = get_dropout_mask(self.msa_dropout, m, self.training)
751
- m = m + msa_dropout * self.pair_weighted_averaging(
752
- m, z, token_mask, chunk_heads_pwa
753
- )
754
- m = m + self.msa_transition(m, chunk_size_transition_msa)
755
-
756
- z = z + self.outer_product_mean(m, msa_mask, chunk_size_outer_product)
757
-
758
- # Compute pairwise stack
759
- z = self.pairformer_layer(
760
- z, token_mask, chunk_size_tri_attn, use_kernels=use_kernels
761
- )
762
-
763
- return z, m
764
-
765
-
766
- class BFactorModule(nn.Module):
767
- """BFactor Module."""
768
-
769
- def __init__(self, token_s: int, num_bins: int) -> None:
770
- """Initialize the bfactor module.
771
-
772
- Parameters
773
- ----------
774
- token_s : int
775
- The token embedding size.
776
-
777
- """
778
- super().__init__()
779
- self.bfactor = nn.Linear(token_s, num_bins)
780
- self.num_bins = num_bins
781
-
782
- def forward(self, s: Tensor) -> Tensor:
783
- """Perform the forward pass.
784
-
785
- Parameters
786
- ----------
787
- s : Tensor
788
- The sequence embeddings
789
-
790
- Returns
791
- -------
792
- Tensor
793
- The predicted bfactor histogram.
794
-
795
- """
796
- return self.bfactor(s)
797
-
798
-
799
- class DistogramModule(nn.Module):
800
- """Distogram Module."""
801
-
802
- def __init__(self, token_z: int, num_bins: int, num_distograms: int = 1) -> None:
803
- """Initialize the distogram module.
804
-
805
- Parameters
806
- ----------
807
- token_z : int
808
- The token pairwise embedding size.
809
-
810
- """
811
- super().__init__()
812
- self.distogram = nn.Linear(token_z, num_distograms * num_bins)
813
- self.num_distograms = num_distograms
814
- self.num_bins = num_bins
815
-
816
- def forward(self, z: Tensor) -> Tensor:
817
- """Perform the forward pass.
818
-
819
- Parameters
820
- ----------
821
- z : Tensor
822
- The pairwise embeddings
823
-
824
- Returns
825
- -------
826
- Tensor
827
- The predicted distogram.
828
-
829
- """
830
- z = z + z.transpose(1, 2)
831
- return self.distogram(z).reshape(
832
- z.shape[0], z.shape[1], z.shape[2], self.num_distograms, self.num_bins
833
- )
 
1
+ from typing import Dict, Tuple
2
+
3
+ import torch
4
+ from torch import Tensor, nn
5
+ from torch.nn.functional import one_hot
6
+
7
+ from . import vb_const as const
8
+ from .vb_layers_outer_product_mean import OuterProductMean
9
+ from .vb_layers_pair_averaging import PairWeightedAveraging
10
+ from .vb_layers_pairformer import (
11
+ PairformerNoSeqLayer,
12
+ PairformerNoSeqModule,
13
+ get_dropout_mask,
14
+ )
15
+ from .vb_layers_transition import Transition
16
+ from .vb_modules_encodersv2 import (
17
+ AtomAttentionEncoder,
18
+ AtomEncoder,
19
+ FourierEmbedding,
20
+ )
21
+
22
+
23
+ class ContactConditioning(nn.Module):
24
+ def __init__(self, token_z: int, cutoff_min: float, cutoff_max: float):
25
+ super().__init__()
26
+
27
+ self.fourier_embedding = FourierEmbedding(token_z)
28
+ self.encoder = nn.Linear(
29
+ token_z + len(const.contact_conditioning_info) - 1, token_z
30
+ )
31
+ self.encoding_unspecified = nn.Parameter(torch.zeros(token_z))
32
+ self.encoding_unselected = nn.Parameter(torch.zeros(token_z))
33
+ self.cutoff_min = cutoff_min
34
+ self.cutoff_max = cutoff_max
35
+
36
+ def forward(self, feats):
37
+ assert const.contact_conditioning_info["UNSPECIFIED"] == 0
38
+ assert const.contact_conditioning_info["UNSELECTED"] == 1
39
+ contact_conditioning = feats["contact_conditioning"][:, :, :, 2:]
40
+ contact_threshold = feats["contact_threshold"]
41
+ contact_threshold_normalized = (contact_threshold - self.cutoff_min) / (
42
+ self.cutoff_max - self.cutoff_min
43
+ )
44
+ contact_threshold_fourier = self.fourier_embedding(
45
+ contact_threshold_normalized.flatten()
46
+ ).reshape(contact_threshold_normalized.shape + (-1,))
47
+
48
+ contact_conditioning = torch.cat(
49
+ [
50
+ contact_conditioning,
51
+ contact_threshold_normalized.unsqueeze(-1),
52
+ contact_threshold_fourier,
53
+ ],
54
+ dim=-1,
55
+ )
56
+ contact_conditioning = self.encoder(contact_conditioning)
57
+
58
+ contact_conditioning = (
59
+ contact_conditioning
60
+ * (
61
+ 1
62
+ - feats["contact_conditioning"][:, :, :, 0:2].sum(dim=-1, keepdim=True)
63
+ )
64
+ + self.encoding_unspecified * feats["contact_conditioning"][:, :, :, 0:1]
65
+ + self.encoding_unselected * feats["contact_conditioning"][:, :, :, 1:2]
66
+ )
67
+ return contact_conditioning
68
+
69
+
70
+ class InputEmbedder(nn.Module):
71
+ def __init__(
72
+ self,
73
+ atom_s: int,
74
+ atom_z: int,
75
+ token_s: int,
76
+ token_z: int,
77
+ atoms_per_window_queries: int,
78
+ atoms_per_window_keys: int,
79
+ atom_feature_dim: int,
80
+ atom_encoder_depth: int,
81
+ atom_encoder_heads: int,
82
+ activation_checkpointing: bool = False,
83
+ add_method_conditioning: bool = False,
84
+ add_modified_flag: bool = False,
85
+ add_cyclic_flag: bool = False,
86
+ add_mol_type_feat: bool = False,
87
+ use_no_atom_char: bool = False,
88
+ use_atom_backbone_feat: bool = False,
89
+ use_residue_feats_atoms: bool = False,
90
+ ) -> None:
91
+ """Initialize the input embedder.
92
+
93
+ Parameters
94
+ ----------
95
+ atom_s : int
96
+ The atom embedding size.
97
+ atom_z : int
98
+ The atom pairwise embedding size.
99
+ token_s : int
100
+ The token embedding size.
101
+
102
+ """
103
+ super().__init__()
104
+ self.token_s = token_s
105
+ self.add_method_conditioning = add_method_conditioning
106
+ self.add_modified_flag = add_modified_flag
107
+ self.add_cyclic_flag = add_cyclic_flag
108
+ self.add_mol_type_feat = add_mol_type_feat
109
+
110
+ self.atom_encoder = AtomEncoder(
111
+ atom_s=atom_s,
112
+ atom_z=atom_z,
113
+ token_s=token_s,
114
+ token_z=token_z,
115
+ atoms_per_window_queries=atoms_per_window_queries,
116
+ atoms_per_window_keys=atoms_per_window_keys,
117
+ atom_feature_dim=atom_feature_dim,
118
+ structure_prediction=False,
119
+ use_no_atom_char=use_no_atom_char,
120
+ use_atom_backbone_feat=use_atom_backbone_feat,
121
+ use_residue_feats_atoms=use_residue_feats_atoms,
122
+ )
123
+
124
+ self.atom_enc_proj_z = nn.Sequential(
125
+ nn.LayerNorm(atom_z),
126
+ nn.Linear(atom_z, atom_encoder_depth * atom_encoder_heads, bias=False),
127
+ )
128
+
129
+ self.atom_attention_encoder = AtomAttentionEncoder(
130
+ atom_s=atom_s,
131
+ token_s=token_s,
132
+ atoms_per_window_queries=atoms_per_window_queries,
133
+ atoms_per_window_keys=atoms_per_window_keys,
134
+ atom_encoder_depth=atom_encoder_depth,
135
+ atom_encoder_heads=atom_encoder_heads,
136
+ structure_prediction=False,
137
+ activation_checkpointing=activation_checkpointing,
138
+ )
139
+
140
+ self.res_type_encoding = nn.Linear(const.num_tokens, token_s, bias=False)
141
+ self.msa_profile_encoding = nn.Linear(const.num_tokens + 1, token_s, bias=False)
142
+
143
+ if add_method_conditioning:
144
+ self.method_conditioning_init = nn.Embedding(
145
+ const.num_method_types, token_s
146
+ )
147
+ self.method_conditioning_init.weight.data.fill_(0)
148
+ if add_modified_flag:
149
+ self.modified_conditioning_init = nn.Embedding(2, token_s)
150
+ self.modified_conditioning_init.weight.data.fill_(0)
151
+ if add_cyclic_flag:
152
+ self.cyclic_conditioning_init = nn.Linear(1, token_s, bias=False)
153
+ self.cyclic_conditioning_init.weight.data.fill_(0)
154
+ if add_mol_type_feat:
155
+ self.mol_type_conditioning_init = nn.Embedding(
156
+ len(const.chain_type_ids), token_s
157
+ )
158
+ self.mol_type_conditioning_init.weight.data.fill_(0)
159
+
160
+ def forward(self, feats: Dict[str, Tensor], affinity: bool = False) -> Tensor:
161
+ """Perform the forward pass.
162
+
163
+ Parameters
164
+ ----------
165
+ feats : dict[str, Tensor]
166
+ Input features
167
+
168
+ Returns
169
+ -------
170
+ Tensor
171
+ The embedded tokens.
172
+
173
+ """
174
+ # Load relevant features
175
+ res_type = feats["res_type"].float()
176
+ if affinity:
177
+ profile = feats["profile_affinity"]
178
+ deletion_mean = feats["deletion_mean_affinity"].unsqueeze(-1)
179
+ else:
180
+ profile = feats["profile"]
181
+ deletion_mean = feats["deletion_mean"].unsqueeze(-1)
182
+
183
+ # Compute input embedding
184
+ q, c, p, to_keys = self.atom_encoder(feats)
185
+ atom_enc_bias = self.atom_enc_proj_z(p)
186
+ a, _, _, _ = self.atom_attention_encoder(
187
+ feats=feats,
188
+ q=q,
189
+ c=c,
190
+ atom_enc_bias=atom_enc_bias,
191
+ to_keys=to_keys,
192
+ )
193
+
194
+ s = (
195
+ a
196
+ + self.res_type_encoding(res_type)
197
+ + self.msa_profile_encoding(torch.cat([profile, deletion_mean], dim=-1))
198
+ )
199
+
200
+ if self.add_method_conditioning:
201
+ s = s + self.method_conditioning_init(feats["method_feature"])
202
+ if self.add_modified_flag:
203
+ s = s + self.modified_conditioning_init(feats["modified"])
204
+ if self.add_cyclic_flag:
205
+ cyclic = feats["cyclic_period"].clamp(max=1.0).unsqueeze(-1)
206
+ s = s + self.cyclic_conditioning_init(cyclic)
207
+ if self.add_mol_type_feat:
208
+ s = s + self.mol_type_conditioning_init(feats["mol_type"])
209
+
210
+ return s
211
+
212
+
213
+ class TemplateModule(nn.Module):
214
+ """Template module."""
215
+
216
+ def __init__(
217
+ self,
218
+ token_z: int,
219
+ template_dim: int,
220
+ template_blocks: int,
221
+ dropout: float = 0.25,
222
+ pairwise_head_width: int = 32,
223
+ pairwise_num_heads: int = 4,
224
+ post_layer_norm: bool = False,
225
+ activation_checkpointing: bool = False,
226
+ min_dist: float = 3.25,
227
+ max_dist: float = 50.75,
228
+ num_bins: int = 38,
229
+ **kwargs,
230
+ ) -> None:
231
+ """Initialize the template module.
232
+
233
+ Parameters
234
+ ----------
235
+ token_z : int
236
+ The token pairwise embedding size.
237
+
238
+ """
239
+ super().__init__()
240
+ self.min_dist = min_dist
241
+ self.max_dist = max_dist
242
+ self.num_bins = num_bins
243
+ self.relu = nn.ReLU()
244
+ self.z_norm = nn.LayerNorm(token_z)
245
+ self.v_norm = nn.LayerNorm(template_dim)
246
+ self.z_proj = nn.Linear(token_z, template_dim, bias=False)
247
+ self.a_proj = nn.Linear(
248
+ const.num_tokens * 2 + num_bins + 5,
249
+ template_dim,
250
+ bias=False,
251
+ )
252
+ self.u_proj = nn.Linear(template_dim, token_z, bias=False)
253
+ self.pairformer = PairformerNoSeqModule(
254
+ template_dim,
255
+ num_blocks=template_blocks,
256
+ dropout=dropout,
257
+ pairwise_head_width=pairwise_head_width,
258
+ pairwise_num_heads=pairwise_num_heads,
259
+ post_layer_norm=post_layer_norm,
260
+ activation_checkpointing=activation_checkpointing,
261
+ )
262
+
263
+ def forward(
264
+ self,
265
+ z: Tensor,
266
+ feats: Dict[str, Tensor],
267
+ pair_mask: Tensor,
268
+ use_kernels: bool = False,
269
+ ) -> Tensor:
270
+ """Perform the forward pass.
271
+
272
+ Parameters
273
+ ----------
274
+ z : Tensor
275
+ The pairwise embeddings
276
+ feats : dict[str, Tensor]
277
+ Input features
278
+ pair_mask : Tensor
279
+ The pair mask
280
+
281
+ Returns
282
+ -------
283
+ Tensor
284
+ The updated pairwise embeddings.
285
+
286
+ """
287
+ # Load relevant features
288
+ asym_id = feats["asym_id"]
289
+ res_type = feats["template_restype"]
290
+ frame_rot = feats["template_frame_rot"]
291
+ frame_t = feats["template_frame_t"]
292
+ frame_mask = feats["template_mask_frame"]
293
+ cb_coords = feats["template_cb"]
294
+ ca_coords = feats["template_ca"]
295
+ cb_mask = feats["template_mask_cb"]
296
+ template_mask = feats["template_mask"].any(dim=2).float()
297
+ num_templates = template_mask.sum(dim=1)
298
+ num_templates = num_templates.clamp(min=1)
299
+
300
+ # Compute pairwise masks
301
+ b_cb_mask = cb_mask[:, :, :, None] * cb_mask[:, :, None, :]
302
+ b_frame_mask = frame_mask[:, :, :, None] * frame_mask[:, :, None, :]
303
+
304
+ b_cb_mask = b_cb_mask[..., None]
305
+ b_frame_mask = b_frame_mask[..., None]
306
+
307
+ # Compute asym mask, template features only attend within the same chain
308
+ B, T = res_type.shape[:2] # noqa: N806
309
+ asym_mask = (asym_id[:, :, None] == asym_id[:, None, :]).float()
310
+ asym_mask = asym_mask[:, None].expand(-1, T, -1, -1)
311
+
312
+ # Compute template features
313
+ with torch.autocast(device_type="cuda", enabled=False):
314
+ # Compute distogram
315
+ cb_dists = torch.cdist(cb_coords, cb_coords)
316
+ boundaries = torch.linspace(self.min_dist, self.max_dist, self.num_bins - 1)
317
+ boundaries = boundaries.to(cb_dists.device)
318
+ distogram = (cb_dists[..., None] > boundaries).sum(dim=-1).long()
319
+ distogram = one_hot(distogram, num_classes=self.num_bins)
320
+
321
+ # Compute unit vector in each frame
322
+ frame_rot = frame_rot.unsqueeze(2).transpose(-1, -2)
323
+ frame_t = frame_t.unsqueeze(2).unsqueeze(-1)
324
+ ca_coords = ca_coords.unsqueeze(3).unsqueeze(-1)
325
+ vector = torch.matmul(frame_rot, (ca_coords - frame_t))
326
+ norm = torch.norm(vector, dim=-1, keepdim=True)
327
+ unit_vector = torch.where(norm > 0, vector / norm, torch.zeros_like(vector))
328
+ unit_vector = unit_vector.squeeze(-1)
329
+
330
+ # Concatenate input features
331
+ a_tij = [distogram, b_cb_mask, unit_vector, b_frame_mask]
332
+ a_tij = torch.cat(a_tij, dim=-1)
333
+ a_tij = a_tij * asym_mask.unsqueeze(-1)
334
+
335
+ res_type_i = res_type[:, :, :, None]
336
+ res_type_j = res_type[:, :, None, :]
337
+ res_type_i = res_type_i.expand(-1, -1, -1, res_type.size(2), -1)
338
+ res_type_j = res_type_j.expand(-1, -1, res_type.size(2), -1, -1)
339
+ a_tij = torch.cat([a_tij, res_type_i, res_type_j], dim=-1)
340
+ a_tij = self.a_proj(a_tij)
341
+
342
+ # Expand mask
343
+ pair_mask = pair_mask[:, None].expand(-1, T, -1, -1)
344
+ pair_mask = pair_mask.reshape(B * T, *pair_mask.shape[2:])
345
+
346
+ # Compute input projections
347
+ v = self.z_proj(self.z_norm(z[:, None])) + a_tij
348
+ v = v.view(B * T, *v.shape[2:])
349
+ v = v + self.pairformer(v, pair_mask, use_kernels=use_kernels)
350
+ v = self.v_norm(v)
351
+ v = v.view(B, T, *v.shape[1:])
352
+
353
+ # Aggregate templates
354
+ template_mask = template_mask[:, :, None, None, None]
355
+ num_templates = num_templates[:, None, None, None]
356
+ u = (v * template_mask).sum(dim=1) / num_templates.to(v)
357
+
358
+ # Compute output projection
359
+ u = self.u_proj(self.relu(u))
360
+ return u
361
+
362
+
363
+ class TemplateV2Module(nn.Module):
364
+ """Template module."""
365
+
366
+ def __init__(
367
+ self,
368
+ token_z: int,
369
+ template_dim: int,
370
+ template_blocks: int,
371
+ dropout: float = 0.25,
372
+ pairwise_head_width: int = 32,
373
+ pairwise_num_heads: int = 4,
374
+ post_layer_norm: bool = False,
375
+ activation_checkpointing: bool = False,
376
+ min_dist: float = 3.25,
377
+ max_dist: float = 50.75,
378
+ num_bins: int = 38,
379
+ **kwargs,
380
+ ) -> None:
381
+ """Initialize the template module.
382
+
383
+ Parameters
384
+ ----------
385
+ token_z : int
386
+ The token pairwise embedding size.
387
+
388
+ """
389
+ super().__init__()
390
+ self.min_dist = min_dist
391
+ self.max_dist = max_dist
392
+ self.num_bins = num_bins
393
+ self.relu = nn.ReLU()
394
+ self.z_norm = nn.LayerNorm(token_z)
395
+ self.v_norm = nn.LayerNorm(template_dim)
396
+ self.z_proj = nn.Linear(token_z, template_dim, bias=False)
397
+ self.a_proj = nn.Linear(
398
+ const.num_tokens * 2 + num_bins + 5,
399
+ template_dim,
400
+ bias=False,
401
+ )
402
+ self.u_proj = nn.Linear(template_dim, token_z, bias=False)
403
+ self.pairformer = PairformerNoSeqModule(
404
+ template_dim,
405
+ num_blocks=template_blocks,
406
+ dropout=dropout,
407
+ pairwise_head_width=pairwise_head_width,
408
+ pairwise_num_heads=pairwise_num_heads,
409
+ post_layer_norm=post_layer_norm,
410
+ activation_checkpointing=activation_checkpointing,
411
+ )
412
+
413
+ def forward(
414
+ self,
415
+ z: Tensor,
416
+ feats: Dict[str, Tensor],
417
+ pair_mask: Tensor,
418
+ use_kernels: bool = False,
419
+ ) -> Tensor:
420
+ """Perform the forward pass.
421
+
422
+ Parameters
423
+ ----------
424
+ z : Tensor
425
+ The pairwise embeddings
426
+ feats : dict[str, Tensor]
427
+ Input features
428
+ pair_mask : Tensor
429
+ The pair mask
430
+
431
+ Returns
432
+ -------
433
+ Tensor
434
+ The updated pairwise embeddings.
435
+
436
+ """
437
+ # Load relevant features
438
+ res_type = feats["template_restype"]
439
+ frame_rot = feats["template_frame_rot"]
440
+ frame_t = feats["template_frame_t"]
441
+ frame_mask = feats["template_mask_frame"]
442
+ cb_coords = feats["template_cb"]
443
+ ca_coords = feats["template_ca"]
444
+ cb_mask = feats["template_mask_cb"]
445
+ visibility_ids = feats["visibility_ids"]
446
+ template_mask = feats["template_mask"].any(dim=2).float()
447
+ num_templates = template_mask.sum(dim=1)
448
+ num_templates = num_templates.clamp(min=1)
449
+
450
+ # Compute pairwise masks
451
+ b_cb_mask = cb_mask[:, :, :, None] * cb_mask[:, :, None, :]
452
+ b_frame_mask = frame_mask[:, :, :, None] * frame_mask[:, :, None, :]
453
+
454
+ b_cb_mask = b_cb_mask[..., None]
455
+ b_frame_mask = b_frame_mask[..., None]
456
+
457
+ # Compute asym mask, template features only attend within the same chain
458
+ B, T = res_type.shape[:2] # noqa: N806
459
+ tmlp_pair_mask = (
460
+ visibility_ids[:, :, :, None] == visibility_ids[:, :, None, :]
461
+ ).float()
462
+
463
+ # Compute template features
464
+ with torch.autocast(device_type="cuda", enabled=False):
465
+ # Compute distogram
466
+ cb_dists = torch.cdist(cb_coords, cb_coords)
467
+ boundaries = torch.linspace(self.min_dist, self.max_dist, self.num_bins - 1)
468
+ boundaries = boundaries.to(cb_dists.device)
469
+ distogram = (cb_dists[..., None] > boundaries).sum(dim=-1).long()
470
+ distogram = one_hot(distogram, num_classes=self.num_bins)
471
+
472
+ # Compute unit vector in each frame
473
+ frame_rot = frame_rot.unsqueeze(2).transpose(-1, -2)
474
+ frame_t = frame_t.unsqueeze(2).unsqueeze(-1)
475
+ ca_coords = ca_coords.unsqueeze(3).unsqueeze(-1)
476
+ vector = torch.matmul(frame_rot, (ca_coords - frame_t))
477
+ norm = torch.norm(vector, dim=-1, keepdim=True)
478
+ unit_vector = torch.where(norm > 0, vector / norm, torch.zeros_like(vector))
479
+ unit_vector = unit_vector.squeeze(-1)
480
+
481
+ # Concatenate input features
482
+ a_tij = [distogram, b_cb_mask, unit_vector, b_frame_mask]
483
+ a_tij = torch.cat(a_tij, dim=-1)
484
+ a_tij = a_tij * tmlp_pair_mask.unsqueeze(-1)
485
+
486
+ res_type_i = res_type[:, :, :, None]
487
+ res_type_j = res_type[:, :, None, :]
488
+ res_type_i = res_type_i.expand(-1, -1, -1, res_type.size(2), -1)
489
+ res_type_j = res_type_j.expand(-1, -1, res_type.size(2), -1, -1)
490
+ a_tij = torch.cat([a_tij, res_type_i, res_type_j], dim=-1)
491
+ a_tij = self.a_proj(a_tij)
492
+
493
+ # Expand mask
494
+ pair_mask = pair_mask[:, None].expand(-1, T, -1, -1)
495
+ pair_mask = pair_mask.reshape(B * T, *pair_mask.shape[2:])
496
+
497
+ # Compute input projections
498
+ v = self.z_proj(self.z_norm(z[:, None])) + a_tij
499
+ v = v.view(B * T, *v.shape[2:])
500
+ v = v + self.pairformer(v, pair_mask, use_kernels=use_kernels)
501
+ v = self.v_norm(v)
502
+ v = v.view(B, T, *v.shape[1:])
503
+
504
+ # Aggregate templates
505
+ template_mask = template_mask[:, :, None, None, None]
506
+ num_templates = num_templates[:, None, None, None]
507
+ u = (v * template_mask).sum(dim=1) / num_templates.to(v)
508
+
509
+ # Compute output projection
510
+ u = self.u_proj(self.relu(u))
511
+ return u
512
+
513
+
514
+ class MSAModule(nn.Module):
515
+ """MSA module."""
516
+
517
+ def __init__(
518
+ self,
519
+ msa_s: int,
520
+ token_z: int,
521
+ token_s: int,
522
+ msa_blocks: int,
523
+ msa_dropout: float,
524
+ z_dropout: float,
525
+ pairwise_head_width: int = 32,
526
+ pairwise_num_heads: int = 4,
527
+ activation_checkpointing: bool = False,
528
+ use_paired_feature: bool = True,
529
+ subsample_msa: bool = False,
530
+ num_subsampled_msa: int = 1024,
531
+ **kwargs,
532
+ ) -> None:
533
+ """Initialize the MSA module.
534
+
535
+ Parameters
536
+ ----------
537
+ token_z : int
538
+ The token pairwise embedding size.
539
+
540
+ """
541
+ super().__init__()
542
+ self.msa_blocks = msa_blocks
543
+ self.msa_dropout = msa_dropout
544
+ self.z_dropout = z_dropout
545
+ self.use_paired_feature = use_paired_feature
546
+ self.activation_checkpointing = activation_checkpointing
547
+ self.subsample_msa = subsample_msa
548
+ self.num_subsampled_msa = num_subsampled_msa
549
+
550
+ self.s_proj = nn.Linear(token_s, msa_s, bias=False)
551
+ self.msa_proj = nn.Linear(
552
+ const.num_tokens + 2 + int(use_paired_feature),
553
+ msa_s,
554
+ bias=False,
555
+ )
556
+ self.layers = nn.ModuleList()
557
+ for i in range(msa_blocks):
558
+ self.layers.append(
559
+ MSALayer(
560
+ msa_s,
561
+ token_z,
562
+ msa_dropout,
563
+ z_dropout,
564
+ pairwise_head_width,
565
+ pairwise_num_heads,
566
+ )
567
+ )
568
+
569
+ def forward(
570
+ self,
571
+ z: Tensor,
572
+ emb: Tensor,
573
+ feats: Dict[str, Tensor],
574
+ use_kernels: bool = False,
575
+ ) -> Tensor:
576
+ """Perform the forward pass.
577
+
578
+ Parameters
579
+ ----------
580
+ z : Tensor
581
+ The pairwise embeddings
582
+ emb : Tensor
583
+ The input embeddings
584
+ feats : dict[str, Tensor]
585
+ Input features
586
+ use_kernels: bool
587
+ Whether to use kernels for triangular updates
588
+
589
+ Returns
590
+ -------
591
+ Tensor
592
+ The output pairwise embeddings.
593
+
594
+ """
595
+ # Set chunk sizes
596
+ if not self.training:
597
+ if z.shape[1] > const.chunk_size_threshold:
598
+ chunk_heads_pwa = True
599
+ chunk_size_transition_z = 64
600
+ chunk_size_transition_msa = 32
601
+ chunk_size_outer_product = 4
602
+ chunk_size_tri_attn = 128
603
+ else:
604
+ chunk_heads_pwa = False
605
+ chunk_size_transition_z = None
606
+ chunk_size_transition_msa = None
607
+ chunk_size_outer_product = None
608
+ chunk_size_tri_attn = 512
609
+ else:
610
+ chunk_heads_pwa = False
611
+ chunk_size_transition_z = None
612
+ chunk_size_transition_msa = None
613
+ chunk_size_outer_product = None
614
+ chunk_size_tri_attn = None
615
+
616
+ # Load relevant features
617
+ msa = feats["msa"]
618
+ if msa.dtype in (torch.long, torch.int32, torch.int64):
619
+ msa = torch.nn.functional.one_hot(msa, num_classes=const.num_tokens).float()
620
+ # else: already float one-hot (soft/differentiable path)
621
+ has_deletion = feats["has_deletion"].unsqueeze(-1)
622
+ deletion_value = feats["deletion_value"].unsqueeze(-1)
623
+ is_paired = feats["msa_paired"].unsqueeze(-1)
624
+ msa_mask = feats["msa_mask"]
625
+ token_mask = feats["token_pad_mask"].float()
626
+ token_mask = token_mask[:, :, None] * token_mask[:, None, :]
627
+
628
+ # Compute MSA embeddings
629
+ if self.use_paired_feature:
630
+ m = torch.cat([msa, has_deletion, deletion_value, is_paired], dim=-1)
631
+ else:
632
+ m = torch.cat([msa, has_deletion, deletion_value], dim=-1)
633
+
634
+ # Subsample the MSA
635
+ if self.subsample_msa:
636
+ msa_indices = torch.randperm(msa.shape[1])[: self.num_subsampled_msa]
637
+ m = m[:, msa_indices]
638
+ msa_mask = msa_mask[:, msa_indices]
639
+
640
+ # Compute input projections
641
+ m = self.msa_proj(m)
642
+ m = m + self.s_proj(emb).unsqueeze(1)
643
+
644
+ # Perform MSA blocks
645
+ for i in range(self.msa_blocks):
646
+ if self.activation_checkpointing:
647
+ z, m = torch.utils.checkpoint.checkpoint(
648
+ self.layers[i],
649
+ z,
650
+ m,
651
+ token_mask,
652
+ msa_mask,
653
+ chunk_heads_pwa,
654
+ chunk_size_transition_z,
655
+ chunk_size_transition_msa,
656
+ chunk_size_outer_product,
657
+ chunk_size_tri_attn,
658
+ use_kernels,
659
+ use_reentrant=False,
660
+ )
661
+ else:
662
+ z, m = self.layers[i](
663
+ z,
664
+ m,
665
+ token_mask,
666
+ msa_mask,
667
+ chunk_heads_pwa,
668
+ chunk_size_transition_z,
669
+ chunk_size_transition_msa,
670
+ chunk_size_outer_product,
671
+ chunk_size_tri_attn,
672
+ use_kernels,
673
+ )
674
+ return z
675
+
676
+
677
+ class MSALayer(nn.Module):
678
+ """MSA module."""
679
+
680
+ def __init__(
681
+ self,
682
+ msa_s: int,
683
+ token_z: int,
684
+ msa_dropout: float,
685
+ z_dropout: float,
686
+ pairwise_head_width: int = 32,
687
+ pairwise_num_heads: int = 4,
688
+ ) -> None:
689
+ """Initialize the MSA module.
690
+
691
+ Parameters
692
+ ----------
693
+ token_z : int
694
+ The token pairwise embedding size.
695
+
696
+ """
697
+ super().__init__()
698
+ self.msa_dropout = msa_dropout
699
+ self.msa_transition = Transition(dim=msa_s, hidden=msa_s * 4)
700
+ self.pair_weighted_averaging = PairWeightedAveraging(
701
+ c_m=msa_s,
702
+ c_z=token_z,
703
+ c_h=32,
704
+ num_heads=8,
705
+ )
706
+
707
+ self.pairformer_layer = PairformerNoSeqLayer(
708
+ token_z=token_z,
709
+ dropout=z_dropout,
710
+ pairwise_head_width=pairwise_head_width,
711
+ pairwise_num_heads=pairwise_num_heads,
712
+ )
713
+ self.outer_product_mean = OuterProductMean(
714
+ c_in=msa_s,
715
+ c_hidden=32,
716
+ c_out=token_z,
717
+ )
718
+
719
+ def forward(
720
+ self,
721
+ z: Tensor,
722
+ m: Tensor,
723
+ token_mask: Tensor,
724
+ msa_mask: Tensor,
725
+ chunk_heads_pwa: bool = False,
726
+ chunk_size_transition_z: int = None,
727
+ chunk_size_transition_msa: int = None,
728
+ chunk_size_outer_product: int = None,
729
+ chunk_size_tri_attn: int = None,
730
+ use_kernels: bool = False,
731
+ ) -> Tuple[Tensor, Tensor]:
732
+ """Perform the forward pass.
733
+
734
+ Parameters
735
+ ----------
736
+ z : Tensor
737
+ The pairwise embeddings
738
+ emb : Tensor
739
+ The input embeddings
740
+ feats : dict[str, Tensor]
741
+ Input features
742
+
743
+ Returns
744
+ -------
745
+ Tensor
746
+ The output pairwise embeddings.
747
+
748
+ """
749
+ # Communication to MSA stack
750
+ msa_dropout = get_dropout_mask(self.msa_dropout, m, self.training)
751
+ m = m + msa_dropout * self.pair_weighted_averaging(
752
+ m, z, token_mask, chunk_heads_pwa
753
+ )
754
+ m = m + self.msa_transition(m, chunk_size_transition_msa)
755
+
756
+ z = z + self.outer_product_mean(m, msa_mask, chunk_size_outer_product)
757
+
758
+ # Compute pairwise stack
759
+ z = self.pairformer_layer(
760
+ z, token_mask, chunk_size_tri_attn, use_kernels=use_kernels
761
+ )
762
+
763
+ return z, m
764
+
765
+
766
+ class BFactorModule(nn.Module):
767
+ """BFactor Module."""
768
+
769
+ def __init__(self, token_s: int, num_bins: int) -> None:
770
+ """Initialize the bfactor module.
771
+
772
+ Parameters
773
+ ----------
774
+ token_s : int
775
+ The token embedding size.
776
+
777
+ """
778
+ super().__init__()
779
+ self.bfactor = nn.Linear(token_s, num_bins)
780
+ self.num_bins = num_bins
781
+
782
+ def forward(self, s: Tensor) -> Tensor:
783
+ """Perform the forward pass.
784
+
785
+ Parameters
786
+ ----------
787
+ s : Tensor
788
+ The sequence embeddings
789
+
790
+ Returns
791
+ -------
792
+ Tensor
793
+ The predicted bfactor histogram.
794
+
795
+ """
796
+ return self.bfactor(s)
797
+
798
+
799
+ class DistogramModule(nn.Module):
800
+ """Distogram Module."""
801
+
802
+ def __init__(self, token_z: int, num_bins: int, num_distograms: int = 1) -> None:
803
+ """Initialize the distogram module.
804
+
805
+ Parameters
806
+ ----------
807
+ token_z : int
808
+ The token pairwise embedding size.
809
+
810
+ """
811
+ super().__init__()
812
+ self.distogram = nn.Linear(token_z, num_distograms * num_bins)
813
+ self.num_distograms = num_distograms
814
+ self.num_bins = num_bins
815
+
816
+ def forward(self, z: Tensor) -> Tensor:
817
+ """Perform the forward pass.
818
+
819
+ Parameters
820
+ ----------
821
+ z : Tensor
822
+ The pairwise embeddings
823
+
824
+ Returns
825
+ -------
826
+ Tensor
827
+ The predicted distogram.
828
+
829
+ """
830
+ z = z + z.transpose(1, 2)
831
+ return self.distogram(z).reshape(
832
+ z.shape[0], z.shape[1], z.shape[2], self.num_distograms, self.num_bins
833
+ )