wuxing0105 commited on
Commit
69362c7
·
verified ·
1 Parent(s): 894fa0b

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. .gitattributes +46 -10
  2. models/esm/__pycache__/esm1.cpython-310.pyc +0 -0
  3. models/esm/esmfold/v1/__pycache__/esmfold.cpython-310.pyc +0 -0
  4. models/esm/esmfold/v1/__pycache__/misc.cpython-310.pyc +0 -0
  5. models/esm/esmfold/v1/pretrained.py +199 -0
  6. models/esm/inverse_folding/__init__.py +8 -0
  7. models/esm/inverse_folding/__pycache__/features.cpython-310.pyc +0 -0
  8. models/esm/inverse_folding/__pycache__/gvp_encoder.cpython-310.pyc +0 -0
  9. models/esm/inverse_folding/__pycache__/gvp_utils.cpython-310.pyc +0 -0
  10. models/esm/inverse_folding/__pycache__/multichain_util.cpython-310.pyc +0 -0
  11. models/esm/inverse_folding/__pycache__/transformer_decoder.cpython-310.pyc +0 -0
  12. models/esm/inverse_folding/__pycache__/transformer_layer.cpython-310.pyc +0 -0
  13. models/esm/inverse_folding/__pycache__/util.cpython-310.pyc +0 -0
  14. models/esm/inverse_folding/features.py +352 -0
  15. models/esm/inverse_folding/gvp_modules.py +475 -0
  16. models/esm/inverse_folding/gvp_transformer.py +140 -0
  17. models/esm/inverse_folding/gvp_transformer_encoder.py +184 -0
  18. models/esm/inverse_folding/multichain_util.py +152 -0
  19. models/esm/inverse_folding/transformer_decoder.py +228 -0
  20. models/openfold/__init__.py +0 -0
  21. models/openfold/__pycache__/dropout.cpython-310.pyc +0 -0
  22. models/openfold/__pycache__/embedders.cpython-310.pyc +0 -0
  23. models/openfold/__pycache__/evoformer.cpython-310.pyc +0 -0
  24. models/openfold/__pycache__/heads.cpython-310.pyc +0 -0
  25. models/openfold/__pycache__/model.cpython-310.pyc +0 -0
  26. models/openfold/__pycache__/msa.cpython-310.pyc +0 -0
  27. models/openfold/__pycache__/outer_product_mean.cpython-310.pyc +0 -0
  28. models/openfold/__pycache__/pair_transition.cpython-310.pyc +0 -0
  29. models/openfold/__pycache__/primitives.cpython-310.pyc +0 -0
  30. models/openfold/__pycache__/structure_module.cpython-310.pyc +0 -0
  31. models/openfold/__pycache__/template.cpython-310.pyc +0 -0
  32. models/openfold/__pycache__/torchscript.cpython-310.pyc +0 -0
  33. models/openfold/__pycache__/triangular_attention.cpython-310.pyc +0 -0
  34. models/openfold/__pycache__/triangular_multiplicative_update.cpython-310.pyc +0 -0
  35. models/openfold/dropout.py +78 -0
  36. models/openfold/embedders.py +984 -0
  37. models/openfold/evoformer.py +1219 -0
  38. models/openfold/heads.py +267 -0
  39. models/openfold/model.py +591 -0
  40. models/openfold/msa.py +476 -0
  41. models/openfold/outer_product_mean.py +159 -0
  42. models/openfold/pair_transition.py +99 -0
  43. models/openfold/primitives.py +902 -0
  44. models/openfold/structure_module.py +1252 -0
  45. models/openfold/template.py +693 -0
  46. models/openfold/torchscript.py +215 -0
  47. models/openfold/triangular_attention.py +168 -0
  48. models/openfold/triangular_multiplicative_update.py +647 -0
  49. weight/.gitkeep +0 -0
  50. weight/esm_models/README.md +6 -0
.gitattributes CHANGED
@@ -1,35 +1,71 @@
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
 
4
  *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
  *.model filter=lfs diff=lfs merge=lfs -text
13
  *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
  *.onnx filter=lfs diff=lfs merge=lfs -text
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bin.* filter=lfs diff=lfs merge=lfs -text
5
  *.bz2 filter=lfs diff=lfs merge=lfs -text
 
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
 
11
  *.model filter=lfs diff=lfs merge=lfs -text
12
  *.msgpack filter=lfs diff=lfs merge=lfs -text
 
 
13
  *.onnx filter=lfs diff=lfs merge=lfs -text
14
  *.ot filter=lfs diff=lfs merge=lfs -text
15
  *.parquet filter=lfs diff=lfs merge=lfs -text
16
  *.pb filter=lfs diff=lfs merge=lfs -text
17
+
 
 
18
  *.pth filter=lfs diff=lfs merge=lfs -text
19
  *.rar filter=lfs diff=lfs merge=lfs -text
 
20
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
21
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
22
  *.tflite filter=lfs diff=lfs merge=lfs -text
23
  *.tgz filter=lfs diff=lfs merge=lfs -text
 
24
  *.xz filter=lfs diff=lfs merge=lfs -text
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
+ *.zstandard filter=lfs diff=lfs merge=lfs -text
27
+ *.tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *.db* filter=lfs diff=lfs merge=lfs -text
29
+ *.ark* filter=lfs diff=lfs merge=lfs -text
30
+ **/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
31
+ **/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
32
+ **/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
33
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
34
+
35
+ *.gguf* filter=lfs diff=lfs merge=lfs -text
36
+ *.ggml filter=lfs diff=lfs merge=lfs -text
37
+ *.llamafile* filter=lfs diff=lfs merge=lfs -text
38
+ *.pt2 filter=lfs diff=lfs merge=lfs -text
39
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
40
+ *.npy filter=lfs diff=lfs merge=lfs -text
41
+ *.npz filter=lfs diff=lfs merge=lfs -text
42
+ *.pickle filter=lfs diff=lfs merge=lfs -text
43
+
44
+ *.tar filter=lfs diff=lfs merge=lfs -text
45
+ *.wasm filter=lfs diff=lfs merge=lfs -text
46
  *.zst filter=lfs diff=lfs merge=lfs -text
47
  *tfevents* filter=lfs diff=lfs merge=lfs -text
48
+
49
+ checkpoints/simplefold_1.1B.ckpt filter=lfs diff=lfs merge=lfs -text
50
+ checkpoints/simplefold_100M.ckpt filter=lfs diff=lfs merge=lfs -text
51
+ checkpoints/simplefold_1.6B.ckpt filter=lfs diff=lfs merge=lfs -text
52
+ checkpoints/boltz1_conf.ckpt filter=lfs diff=lfs merge=lfs -text
53
+ checkpoints/plddt.ckpt filter=lfs diff=lfs merge=lfs -text
54
+ checkpoints/ccd.pkl filter=lfs diff=lfs merge=lfs -text
55
+ checkpoints/plddt_module_1.6B.ckpt filter=lfs diff=lfs merge=lfs -text
56
+
57
+ checkpoints/simplefold_700M.ckpt filter=lfs diff=lfs merge=lfs -text
58
+ checkpoints/simplefold_360M.ckpt filter=lfs diff=lfs merge=lfs -text
59
+ checkpoints/simplefold_3B.ckpt filter=lfs diff=lfs merge=lfs -text
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
70
+ *.pkl filter=lfs diff=lfs merge=lfs -text
71
+ *.pt filter=lfs diff=lfs merge=lfs -text
models/esm/__pycache__/esm1.cpython-310.pyc ADDED
Binary file (5.11 kB). View file
 
models/esm/esmfold/v1/__pycache__/esmfold.cpython-310.pyc ADDED
Binary file (11.6 kB). View file
 
models/esm/esmfold/v1/__pycache__/misc.cpython-310.pyc ADDED
Binary file (10.2 kB). View file
 
models/esm/esmfold/v1/pretrained.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from pathlib import Path
7
+
8
+ import torch
9
+
10
+ from models.esm.esmfold.v1.esmfold import ESMFold
11
+
12
+ def _upgrade_state_dict(model_state):
13
+ """Map legacy ESMFold checkpoint keys to the current OpenFold modules."""
14
+ point_projection_prefixes = (
15
+ "trunk.structure_module.ipa.linear_q_points",
16
+ "trunk.structure_module.ipa.linear_kv_points",
17
+ "trunk.structure_module.ipa.linear_k_points",
18
+ "trunk.structure_module.ipa.linear_v_points",
19
+ )
20
+ for prefix in point_projection_prefixes:
21
+ for suffix in ("weight", "bias"):
22
+ old_key = f"{prefix}.{suffix}"
23
+ new_key = f"{prefix}.linear.{suffix}"
24
+ if old_key in model_state and new_key not in model_state:
25
+ model_state[new_key] = model_state.pop(old_key)
26
+ return model_state
27
+
28
+ def _load_model(model_name):
29
+ if model_name.endswith(".pt"): # local, treat as filepath
30
+ model_path = Path(model_name)
31
+ model_data = torch.load(str(model_path), map_location="cpu")
32
+ else: # load from hub
33
+ raise FileNotFoundError(
34
+ f"Remote ESMFold download is disabled in the packaged SimpleFold runtime: {model_name}. "
35
+ "Pass a local .pt file instead."
36
+ )
37
+
38
+ cfg = model_data["cfg"]["model"]
39
+ model_state = _upgrade_state_dict(model_data["model"])
40
+ # model_state = model_data["model"]
41
+ model = ESMFold(esmfold_config=cfg)
42
+
43
+ expected_keys = set(model.state_dict().keys())
44
+ found_keys = set(model_state.keys())
45
+
46
+ missing_essential_keys = []
47
+ for missing_key in expected_keys - found_keys:
48
+ if not missing_key.startswith("esm."):
49
+ missing_essential_keys.append(missing_key)
50
+
51
+ if missing_essential_keys:
52
+ raise RuntimeError(f"Keys '{', '.join(missing_essential_keys)}' are missing.")
53
+
54
+ model.load_state_dict(model_state, strict=False)
55
+
56
+ return model
57
+
58
+
59
+ def esmfold_v0():
60
+ """
61
+ ESMFold v0 model with 3B ESM-2, 48 folding blocks.
62
+ This version was used for the paper (Lin et al, 2022). It was trained
63
+ on all PDB chains until 2020-05, to ensure temporal holdout with CASP14
64
+ and the CAMEO validation and test set reported there.
65
+ """
66
+ return _load_model("esmfold_3B_v0")
67
+
68
+
69
+ def esmfold_v1():
70
+ """
71
+ ESMFold v1 model using 3B ESM-2, 48 folding blocks.
72
+ ESMFold provides fast high accuracy atomic level structure prediction
73
+ directly from the individual sequence of a protein. ESMFold uses the ESM2
74
+ protein language model to extract meaningful representations from the
75
+ protein sequence.
76
+ """
77
+ return _load_model("esmfold_3B_v1")
78
+
79
+
80
+ def esmfold_structure_module_only_8M():
81
+ """
82
+ ESMFold baseline model using 8M ESM-2, 0 folding blocks.
83
+ ESM-2 here is trained out to 500K updates.
84
+ This is a model designed to test the capabilities of the language model
85
+ when ablated for number of parameters in the language model.
86
+ See table S1 in (Lin et al, 2022).
87
+ """
88
+ return _load_model("esmfold_structure_module_only_8M")
89
+
90
+
91
+ def esmfold_structure_module_only_8M_270K():
92
+ """
93
+ ESMFold baseline model using 8M ESM-2, 0 folding blocks.
94
+ ESM-2 here is trained out to 270K updates.
95
+ This is a model designed to test the capabilities of the language model
96
+ when ablated for number of parameters in the language model.
97
+ See table S1 in (Lin et al, 2022).
98
+ """
99
+ return _load_model("esmfold_structure_module_only_8M_270K")
100
+
101
+
102
+ def esmfold_structure_module_only_35M():
103
+ """
104
+ ESMFold baseline model using 35M ESM-2, 0 folding blocks.
105
+ ESM-2 here is trained out to 500K updates.
106
+ This is a model designed to test the capabilities of the language model
107
+ when ablated for number of parameters in the language model.
108
+ See table S1 in (Lin et al, 2022).
109
+ """
110
+ return _load_model("esmfold_structure_module_only_35M")
111
+
112
+
113
+ def esmfold_structure_module_only_35M_270K():
114
+ """
115
+ ESMFold baseline model using 35M ESM-2, 0 folding blocks.
116
+ ESM-2 here is trained out to 270K updates.
117
+ This is a model designed to test the capabilities of the language model
118
+ when ablated for number of parameters in the language model.
119
+ See table S1 in (Lin et al, 2022).
120
+ """
121
+ return _load_model("esmfold_structure_module_only_35M_270K")
122
+
123
+
124
+ def esmfold_structure_module_only_150M():
125
+ """
126
+ ESMFold baseline model using 150M ESM-2, 0 folding blocks.
127
+ ESM-2 here is trained out to 500K updates.
128
+ This is a model designed to test the capabilities of the language model
129
+ when ablated for number of parameters in the language model.
130
+ See table S1 in (Lin et al, 2022).
131
+ """
132
+ return _load_model("esmfold_structure_module_only_150M")
133
+
134
+
135
+ def esmfold_structure_module_only_150M_270K():
136
+ """
137
+ ESMFold baseline model using 150M ESM-2, 0 folding blocks.
138
+ ESM-2 here is trained out to 270K updates.
139
+ This is a model designed to test the capabilities of the language model
140
+ when ablated for number of parameters in the language model.
141
+ See table S1 in (Lin et al, 2022).
142
+ """
143
+ return _load_model("esmfold_structure_module_only_150M_270K")
144
+
145
+
146
+ def esmfold_structure_module_only_650M():
147
+ """
148
+ ESMFold baseline model using 650M ESM-2, 0 folding blocks.
149
+ ESM-2 here is trained out to 500K updates.
150
+ This is a model designed to test the capabilities of the language model
151
+ when ablated for number of parameters in the language model.
152
+ See table S1 in (Lin et al, 2022).
153
+ """
154
+ return _load_model("esmfold_structure_module_only_650M")
155
+
156
+
157
+ def esmfold_structure_module_only_650M_270K():
158
+ """
159
+ ESMFold baseline model using 650M ESM-2, 0 folding blocks.
160
+ ESM-2 here is trained out to 270K updates.
161
+ This is a model designed to test the capabilities of the language model
162
+ when ablated for number of parameters in the language model.
163
+ See table S1 in (Lin et al, 2022).
164
+ """
165
+ return _load_model("esmfold_structure_module_only_650M_270K")
166
+
167
+
168
+ def esmfold_structure_module_only_3B():
169
+ """
170
+ ESMFold baseline model using 3B ESM-2, 0 folding blocks.
171
+ ESM-2 here is trained out to 500K updates.
172
+ This is a model designed to test the capabilities of the language model
173
+ when ablated for number of parameters in the language model.
174
+ See table S1 in (Lin et al, 2022).
175
+ """
176
+ return _load_model("esmfold_structure_module_only_3B")
177
+
178
+
179
+ def esmfold_structure_module_only_3B_270K():
180
+ """
181
+ ESMFold baseline model using 3B ESM-2, 0 folding blocks.
182
+ ESM-2 here is trained out to 270K updates.
183
+ This is a model designed to test the capabilities of the language model
184
+ when ablated for number of parameters in the language model.
185
+ See table S1 in (Lin et al, 2022).
186
+ """
187
+ return _load_model("esmfold_structure_module_only_3B_270K")
188
+
189
+
190
+ def esmfold_structure_module_only_15B():
191
+ """
192
+ ESMFold baseline model using 15B ESM-2, 0 folding blocks.
193
+ ESM-2 here is trained out to 270K updates.
194
+ The 15B parameter ESM-2 was not trained out to 500K updates
195
+ This is a model designed to test the capabilities of the language model
196
+ when ablated for number of parameters in the language model.
197
+ See table S1 in (Lin et al, 2022).
198
+ """
199
+ return _load_model("esmfold_structure_module_only_15B")
models/esm/inverse_folding/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ from . import gvp_transformer
7
+ from . import util
8
+ from . import multichain_util
models/esm/inverse_folding/__pycache__/features.cpython-310.pyc ADDED
Binary file (9.04 kB). View file
 
models/esm/inverse_folding/__pycache__/gvp_encoder.cpython-310.pyc ADDED
Binary file (1.84 kB). View file
 
models/esm/inverse_folding/__pycache__/gvp_utils.cpython-310.pyc ADDED
Binary file (2.6 kB). View file
 
models/esm/inverse_folding/__pycache__/multichain_util.cpython-310.pyc ADDED
Binary file (5.38 kB). View file
 
models/esm/inverse_folding/__pycache__/transformer_decoder.cpython-310.pyc ADDED
Binary file (6.02 kB). View file
 
models/esm/inverse_folding/__pycache__/transformer_layer.cpython-310.pyc ADDED
Binary file (7.7 kB). View file
 
models/esm/inverse_folding/__pycache__/util.cpython-310.pyc ADDED
Binary file (12.4 kB). View file
 
models/esm/inverse_folding/features.py ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ #
6
+ # Portions of this file were adapted from the open source code for the following
7
+ # two papers:
8
+ #
9
+ # Ingraham, J., Garg, V., Barzilay, R., & Jaakkola, T. (2019). Generative
10
+ # models for graph-based protein design. Advances in Neural Information
11
+ # Processing Systems, 32.
12
+ #
13
+ # Jing, B., Eismann, S., Suriana, P., Townshend, R. J. L., & Dror, R. (2020).
14
+ # Learning from Protein Structure with Geometric Vector Perceptrons. In
15
+ # International Conference on Learning Representations.
16
+ #
17
+ # MIT License
18
+ #
19
+ # Copyright (c) 2020 Bowen Jing, Stephan Eismann, Patricia Suriana, Raphael Townshend, Ron Dror
20
+ #
21
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
22
+ # of this software and associated documentation files (the "Software"), to deal
23
+ # in the Software without restriction, including without limitation the rights
24
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
25
+ # copies of the Software, and to permit persons to whom the Software is
26
+ # furnished to do so, subject to the following conditions:
27
+ #
28
+ # The above copyright notice and this permission notice shall be included in all
29
+ # copies or substantial portions of the Software.
30
+ #
31
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
34
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
36
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37
+ # SOFTWARE.
38
+ #
39
+ # ================================================================
40
+ # The below license applies to the portions of the code (parts of
41
+ # src/datasets.py and src/models.py) adapted from Ingraham, et al.
42
+ # ================================================================
43
+ #
44
+ # MIT License
45
+ #
46
+ # Copyright (c) 2019 John Ingraham, Vikas Garg, Regina Barzilay, Tommi Jaakkola
47
+ #
48
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
49
+ # of this software and associated documentation files (the "Software"), to deal
50
+ # in the Software without restriction, including without limitation the rights
51
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
52
+ # copies of the Software, and to permit persons to whom the Software is
53
+ # furnished to do so, subject to the following conditions:
54
+ #
55
+ # The above copyright notice and this permission notice shall be included in all
56
+ # copies or substantial portions of the Software.
57
+ #
58
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
61
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
62
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
63
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
64
+ # SOFTWARE.
65
+
66
+ import math
67
+ import numpy as np
68
+ import torch
69
+ import torch.nn as nn
70
+ import torch.nn.functional as F
71
+
72
+ from .gvp_utils import flatten_graph
73
+ from .gvp_modules import GVP, LayerNorm
74
+ from .util import normalize, norm, nan_to_num, rbf
75
+
76
+
77
+ class GVPInputFeaturizer(nn.Module):
78
+
79
+ @staticmethod
80
+ def get_node_features(coords, coord_mask, with_coord_mask=True):
81
+ # scalar features
82
+ node_scalar_features = GVPInputFeaturizer._dihedrals(coords)
83
+ if with_coord_mask:
84
+ node_scalar_features = torch.cat([
85
+ node_scalar_features,
86
+ coord_mask.float().unsqueeze(-1)
87
+ ], dim=-1)
88
+ # vector features
89
+ X_ca = coords[:, :, 1]
90
+ orientations = GVPInputFeaturizer._orientations(X_ca)
91
+ sidechains = GVPInputFeaturizer._sidechains(coords)
92
+ node_vector_features = torch.cat([orientations, sidechains.unsqueeze(-2)], dim=-2)
93
+ return node_scalar_features, node_vector_features
94
+
95
+ @staticmethod
96
+ def _orientations(X):
97
+ forward = normalize(X[:, 1:] - X[:, :-1])
98
+ backward = normalize(X[:, :-1] - X[:, 1:])
99
+ forward = F.pad(forward, [0, 0, 0, 1])
100
+ backward = F.pad(backward, [0, 0, 1, 0])
101
+ return torch.cat([forward.unsqueeze(-2), backward.unsqueeze(-2)], -2)
102
+
103
+ @staticmethod
104
+ def _sidechains(X):
105
+ n, origin, c = X[:, :, 0], X[:, :, 1], X[:, :, 2]
106
+ c, n = normalize(c - origin), normalize(n - origin)
107
+ bisector = normalize(c + n)
108
+ perp = normalize(torch.cross(c, n, dim=-1))
109
+ vec = -bisector * math.sqrt(1 / 3) - perp * math.sqrt(2 / 3)
110
+ return vec
111
+
112
+ @staticmethod
113
+ def _dihedrals(X, eps=1e-7):
114
+ X = torch.flatten(X[:, :, :3], 1, 2)
115
+ bsz = X.shape[0]
116
+ dX = X[:, 1:] - X[:, :-1]
117
+ U = normalize(dX, dim=-1)
118
+ u_2 = U[:, :-2]
119
+ u_1 = U[:, 1:-1]
120
+ u_0 = U[:, 2:]
121
+
122
+ # Backbone normals
123
+ n_2 = normalize(torch.cross(u_2, u_1, dim=-1), dim=-1)
124
+ n_1 = normalize(torch.cross(u_1, u_0, dim=-1), dim=-1)
125
+
126
+ # Angle between normals
127
+ cosD = torch.sum(n_2 * n_1, -1)
128
+ cosD = torch.clamp(cosD, -1 + eps, 1 - eps)
129
+ D = torch.sign(torch.sum(u_2 * n_1, -1)) * torch.acos(cosD)
130
+
131
+ # This scheme will remove phi[0], psi[-1], omega[-1]
132
+ D = F.pad(D, [1, 2])
133
+ D = torch.reshape(D, [bsz, -1, 3])
134
+ # Lift angle representations to the circle
135
+ D_features = torch.cat([torch.cos(D), torch.sin(D)], -1)
136
+ return D_features
137
+
138
+ @staticmethod
139
+ def _positional_embeddings(edge_index,
140
+ num_embeddings=None,
141
+ num_positional_embeddings=16,
142
+ period_range=[2, 1000]):
143
+ # From https://github.com/jingraham/neurips19-graph-protein-design
144
+ num_embeddings = num_embeddings or num_positional_embeddings
145
+ d = edge_index[0] - edge_index[1]
146
+
147
+ frequency = torch.exp(
148
+ torch.arange(0, num_embeddings, 2, dtype=torch.float32,
149
+ device=edge_index.device)
150
+ * -(np.log(10000.0) / num_embeddings)
151
+ )
152
+ angles = d.unsqueeze(-1) * frequency
153
+ E = torch.cat((torch.cos(angles), torch.sin(angles)), -1)
154
+ return E
155
+
156
+ @staticmethod
157
+ def _dist(X, coord_mask, padding_mask, top_k_neighbors, eps=1e-8):
158
+ """ Pairwise euclidean distances """
159
+ bsz, maxlen = X.size(0), X.size(1)
160
+ coord_mask_2D = torch.unsqueeze(coord_mask,1) * torch.unsqueeze(coord_mask,2)
161
+ residue_mask = ~padding_mask
162
+ residue_mask_2D = torch.unsqueeze(residue_mask,1) * torch.unsqueeze(residue_mask,2)
163
+ dX = torch.unsqueeze(X,1) - torch.unsqueeze(X,2)
164
+ D = coord_mask_2D * norm(dX, dim=-1)
165
+
166
+ # sorting preference: first those with coords, then among the residues that
167
+ # exist but are masked use distance in sequence as tie breaker, and then the
168
+ # residues that came from padding are last
169
+ seqpos = torch.arange(maxlen, device=X.device)
170
+ Dseq = torch.abs(seqpos.unsqueeze(1) - seqpos.unsqueeze(0)).repeat(bsz, 1, 1)
171
+ D_adjust = nan_to_num(D) + (~coord_mask_2D) * (1e8 + Dseq*1e6) + (
172
+ ~residue_mask_2D) * (1e10)
173
+
174
+ if top_k_neighbors == -1:
175
+ D_neighbors = D_adjust
176
+ E_idx = seqpos.repeat(
177
+ *D_neighbors.shape[:-1], 1)
178
+ else:
179
+ # Identify k nearest neighbors (including self)
180
+ k = min(top_k_neighbors, X.size(1))
181
+ D_neighbors, E_idx = torch.topk(D_adjust, k, dim=-1, largest=False)
182
+
183
+ coord_mask_neighbors = (D_neighbors < 5e7)
184
+ residue_mask_neighbors = (D_neighbors < 5e9)
185
+ return D_neighbors, E_idx, coord_mask_neighbors, residue_mask_neighbors
186
+
187
+
188
+ class Normalize(nn.Module):
189
+ def __init__(self, features, epsilon=1e-6):
190
+ super(Normalize, self).__init__()
191
+ self.gain = nn.Parameter(torch.ones(features))
192
+ self.bias = nn.Parameter(torch.zeros(features))
193
+ self.epsilon = epsilon
194
+
195
+ def forward(self, x, dim=-1):
196
+ mu = x.mean(dim, keepdim=True)
197
+ sigma = torch.sqrt(x.var(dim, keepdim=True) + self.epsilon)
198
+ gain = self.gain
199
+ bias = self.bias
200
+ # Reshape
201
+ if dim != -1:
202
+ shape = [1] * len(mu.size())
203
+ shape[dim] = self.gain.size()[0]
204
+ gain = gain.view(shape)
205
+ bias = bias.view(shape)
206
+ return gain * (x - mu) / (sigma + self.epsilon) + bias
207
+
208
+
209
+ class DihedralFeatures(nn.Module):
210
+ def __init__(self, node_embed_dim):
211
+ """ Embed dihedral angle features. """
212
+ super(DihedralFeatures, self).__init__()
213
+ # 3 dihedral angles; sin and cos of each angle
214
+ node_in = 6
215
+ # Normalization and embedding
216
+ self.node_embedding = nn.Linear(node_in, node_embed_dim, bias=True)
217
+ self.norm_nodes = Normalize(node_embed_dim)
218
+
219
+ def forward(self, X):
220
+ """ Featurize coordinates as an attributed graph """
221
+ V = self._dihedrals(X)
222
+ V = self.node_embedding(V)
223
+ V = self.norm_nodes(V)
224
+ return V
225
+
226
+ @staticmethod
227
+ def _dihedrals(X, eps=1e-7, return_angles=False):
228
+ # First 3 coordinates are N, CA, C
229
+ X = X[:,:,:3,:].reshape(X.shape[0], 3*X.shape[1], 3)
230
+
231
+ # Shifted slices of unit vectors
232
+ dX = X[:,1:,:] - X[:,:-1,:]
233
+ U = F.normalize(dX, dim=-1)
234
+ u_2 = U[:,:-2,:]
235
+ u_1 = U[:,1:-1,:]
236
+ u_0 = U[:,2:,:]
237
+ # Backbone normals
238
+ n_2 = F.normalize(torch.cross(u_2, u_1, dim=-1), dim=-1)
239
+ n_1 = F.normalize(torch.cross(u_1, u_0, dim=-1), dim=-1)
240
+
241
+ # Angle between normals
242
+ cosD = (n_2 * n_1).sum(-1)
243
+ cosD = torch.clamp(cosD, -1+eps, 1-eps)
244
+ D = torch.sign((u_2 * n_1).sum(-1)) * torch.acos(cosD)
245
+
246
+ # This scheme will remove phi[0], psi[-1], omega[-1]
247
+ D = F.pad(D, (1,2), 'constant', 0)
248
+ D = D.view((D.size(0), int(D.size(1)/3), 3))
249
+ phi, psi, omega = torch.unbind(D,-1)
250
+
251
+ if return_angles:
252
+ return phi, psi, omega
253
+
254
+ # Lift angle representations to the circle
255
+ D_features = torch.cat((torch.cos(D), torch.sin(D)), 2)
256
+ return D_features
257
+
258
+
259
+ class GVPGraphEmbedding(GVPInputFeaturizer):
260
+
261
+ def __init__(self, args):
262
+ super().__init__()
263
+ self.top_k_neighbors = args.top_k_neighbors
264
+ self.num_positional_embeddings = 16
265
+ self.remove_edges_without_coords = True
266
+ node_input_dim = (7, 3)
267
+ edge_input_dim = (34, 1)
268
+ node_hidden_dim = (args.node_hidden_dim_scalar,
269
+ args.node_hidden_dim_vector)
270
+ edge_hidden_dim = (args.edge_hidden_dim_scalar,
271
+ args.edge_hidden_dim_vector)
272
+ self.embed_node = nn.Sequential(
273
+ GVP(node_input_dim, node_hidden_dim, activations=(None, None)),
274
+ LayerNorm(node_hidden_dim, eps=1e-4)
275
+ )
276
+ self.embed_edge = nn.Sequential(
277
+ GVP(edge_input_dim, edge_hidden_dim, activations=(None, None)),
278
+ LayerNorm(edge_hidden_dim, eps=1e-4)
279
+ )
280
+ self.embed_confidence = nn.Linear(16, args.node_hidden_dim_scalar)
281
+
282
+ def forward(self, coords, coord_mask, padding_mask, confidence):
283
+ with torch.no_grad():
284
+ node_features = self.get_node_features(coords, coord_mask)
285
+ edge_features, edge_index = self.get_edge_features(
286
+ coords, coord_mask, padding_mask)
287
+ node_embeddings_scalar, node_embeddings_vector = self.embed_node(node_features)
288
+ edge_embeddings = self.embed_edge(edge_features)
289
+
290
+ rbf_rep = rbf(confidence, 0., 1.)
291
+ node_embeddings = (
292
+ node_embeddings_scalar + self.embed_confidence(rbf_rep),
293
+ node_embeddings_vector
294
+ )
295
+
296
+ node_embeddings, edge_embeddings, edge_index = flatten_graph(
297
+ node_embeddings, edge_embeddings, edge_index)
298
+ return node_embeddings, edge_embeddings, edge_index
299
+
300
+ def get_edge_features(self, coords, coord_mask, padding_mask):
301
+ X_ca = coords[:, :, 1]
302
+ # Get distances to the top k neighbors
303
+ E_dist, E_idx, E_coord_mask, E_residue_mask = GVPInputFeaturizer._dist(
304
+ X_ca, coord_mask, padding_mask, self.top_k_neighbors)
305
+ # Flatten the graph to be batch size 1 for torch_geometric package
306
+ dest = E_idx
307
+ B, L, k = E_idx.shape[:3]
308
+ src = torch.arange(L, device=E_idx.device).view([1, L, 1]).expand(B, L, k)
309
+ # After flattening, [2, B, E]
310
+ edge_index = torch.stack([src, dest], dim=0).flatten(2, 3)
311
+ # After flattening, [B, E]
312
+ E_dist = E_dist.flatten(1, 2)
313
+ E_coord_mask = E_coord_mask.flatten(1, 2).unsqueeze(-1)
314
+ E_residue_mask = E_residue_mask.flatten(1, 2)
315
+ # Calculate relative positional embeddings and distance RBF
316
+ pos_embeddings = GVPInputFeaturizer._positional_embeddings(
317
+ edge_index,
318
+ num_positional_embeddings=self.num_positional_embeddings,
319
+ )
320
+ D_rbf = rbf(E_dist, 0., 20.)
321
+ # Calculate relative orientation
322
+ X_src = X_ca.unsqueeze(2).expand(-1, -1, k, -1).flatten(1, 2)
323
+ X_dest = torch.gather(
324
+ X_ca,
325
+ 1,
326
+ edge_index[1, :, :].unsqueeze(-1).expand([B, L*k, 3])
327
+ )
328
+ coord_mask_src = coord_mask.unsqueeze(2).expand(-1, -1, k).flatten(1, 2)
329
+ coord_mask_dest = torch.gather(
330
+ coord_mask,
331
+ 1,
332
+ edge_index[1, :, :].expand([B, L*k])
333
+ )
334
+ E_vectors = X_src - X_dest
335
+ # For the ones without coordinates, substitute in the average vector
336
+ E_vector_mean = torch.sum(E_vectors * E_coord_mask, dim=1,
337
+ keepdims=True) / torch.sum(E_coord_mask, dim=1, keepdims=True)
338
+ E_vectors = E_vectors * E_coord_mask + E_vector_mean * ~(E_coord_mask)
339
+ # Normalize and remove nans
340
+ edge_s = torch.cat([D_rbf, pos_embeddings], dim=-1)
341
+ edge_v = normalize(E_vectors).unsqueeze(-2)
342
+ edge_s, edge_v = map(nan_to_num, (edge_s, edge_v))
343
+ # Also add indications of whether the coordinates are present
344
+ edge_s = torch.cat([
345
+ edge_s,
346
+ (~coord_mask_src).float().unsqueeze(-1),
347
+ (~coord_mask_dest).float().unsqueeze(-1),
348
+ ], dim=-1)
349
+ edge_index[:, ~E_residue_mask] = -1
350
+ if self.remove_edges_without_coords:
351
+ edge_index[:, ~E_coord_mask.squeeze(-1)] = -1
352
+ return (edge_s, edge_v), edge_index.transpose(0, 1)
models/esm/inverse_folding/gvp_modules.py ADDED
@@ -0,0 +1,475 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contents of this file are from the open source code for
2
+ #
3
+ # Jing, B., Eismann, S., Suriana, P., Townshend, R. J. L., & Dror, R. (2020).
4
+ # Learning from Protein Structure with Geometric Vector Perceptrons. In
5
+ # International Conference on Learning Representations.
6
+ #
7
+ # MIT License
8
+ #
9
+ # Copyright (c) 2020 Bowen Jing, Stephan Eismann, Patricia Suriana, Raphael Townshend, Ron Dror
10
+ #
11
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ # of this software and associated documentation files (the "Software"), to deal
13
+ # in the Software without restriction, including without limitation the rights
14
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ # copies of the Software, and to permit persons to whom the Software is
16
+ # furnished to do so, subject to the following conditions:
17
+ #
18
+ # The above copyright notice and this permission notice shall be included in all
19
+ # copies or substantial portions of the Software.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ # SOFTWARE.
28
+
29
+ import typing as T
30
+ import torch
31
+ from torch import nn
32
+ import torch.nn.functional as F
33
+ from torch_geometric.nn import MessagePassing
34
+
35
+ def tuple_size(tp):
36
+ return tuple([0 if a is None else a.size() for a in tp])
37
+
38
+ def tuple_sum(tp1, tp2):
39
+ s1, v1 = tp1
40
+ s2, v2 = tp2
41
+ if v2 is None and v2 is None:
42
+ return (s1 + s2, None)
43
+ return (s1 + s2, v1 + v2)
44
+
45
+ def tuple_cat(*args, dim=-1):
46
+ '''
47
+ Concatenates any number of tuples (s, V) elementwise.
48
+
49
+ :param dim: dimension along which to concatenate when viewed
50
+ as the `dim` index for the scalar-channel tensors.
51
+ This means that `dim=-1` will be applied as
52
+ `dim=-2` for the vector-channel tensors.
53
+ '''
54
+ dim %= len(args[0][0].shape)
55
+ s_args, v_args = list(zip(*args))
56
+ return torch.cat(s_args, dim=dim), torch.cat(v_args, dim=dim)
57
+
58
+ def tuple_index(x, idx):
59
+ '''
60
+ Indexes into a tuple (s, V) along the first dimension.
61
+
62
+ :param idx: any object which can be used to index into a `torch.Tensor`
63
+ '''
64
+ return x[0][idx], x[1][idx]
65
+
66
+ def randn(n, dims, device="cpu"):
67
+ '''
68
+ Returns random tuples (s, V) drawn elementwise from a normal distribution.
69
+
70
+ :param n: number of data points
71
+ :param dims: tuple of dimensions (n_scalar, n_vector)
72
+
73
+ :return: (s, V) with s.shape = (n, n_scalar) and
74
+ V.shape = (n, n_vector, 3)
75
+ '''
76
+ return torch.randn(n, dims[0], device=device), \
77
+ torch.randn(n, dims[1], 3, device=device)
78
+
79
+ def _norm_no_nan(x, axis=-1, keepdims=False, eps=1e-8, sqrt=True):
80
+ '''
81
+ L2 norm of tensor clamped above a minimum value `eps`.
82
+
83
+ :param sqrt: if `False`, returns the square of the L2 norm
84
+ '''
85
+ # clamp is slow
86
+ # out = torch.clamp(torch.sum(torch.square(x), axis, keepdims), min=eps)
87
+ out = torch.sum(torch.square(x), axis, keepdims) + eps
88
+ return torch.sqrt(out) if sqrt else out
89
+
90
+ def _split(x, nv):
91
+ '''
92
+ Splits a merged representation of (s, V) back into a tuple.
93
+ Should be used only with `_merge(s, V)` and only if the tuple
94
+ representation cannot be used.
95
+
96
+ :param x: the `torch.Tensor` returned from `_merge`
97
+ :param nv: the number of vector channels in the input to `_merge`
98
+ '''
99
+ v = torch.reshape(x[..., -3*nv:], x.shape[:-1] + (nv, 3))
100
+ s = x[..., :-3*nv]
101
+ return s, v
102
+
103
+ def _merge(s, v):
104
+ '''
105
+ Merges a tuple (s, V) into a single `torch.Tensor`, where the
106
+ vector channels are flattened and appended to the scalar channels.
107
+ Should be used only if the tuple representation cannot be used.
108
+ Use `_split(x, nv)` to reverse.
109
+ '''
110
+ v = torch.reshape(v, v.shape[:-2] + (3*v.shape[-2],))
111
+ return torch.cat([s, v], -1)
112
+
113
+ class GVP(nn.Module):
114
+ '''
115
+ Geometric Vector Perceptron. See manuscript and README.md
116
+ for more details.
117
+
118
+ :param in_dims: tuple (n_scalar, n_vector)
119
+ :param out_dims: tuple (n_scalar, n_vector)
120
+ :param h_dim: intermediate number of vector channels, optional
121
+ :param activations: tuple of functions (scalar_act, vector_act)
122
+ :param tuple_io: whether to keep accepting tuple inputs and outputs when vi
123
+ or vo = 0
124
+ '''
125
+ def __init__(self, in_dims, out_dims, h_dim=None, vector_gate=False,
126
+ activations=(F.relu, torch.sigmoid), tuple_io=True,
127
+ eps=1e-8):
128
+ super(GVP, self).__init__()
129
+ self.si, self.vi = in_dims
130
+ self.so, self.vo = out_dims
131
+ self.tuple_io = tuple_io
132
+ if self.vi:
133
+ self.h_dim = h_dim or max(self.vi, self.vo)
134
+ self.wh = nn.Linear(self.vi, self.h_dim, bias=False)
135
+ self.ws = nn.Linear(self.h_dim + self.si, self.so)
136
+ if self.vo:
137
+ self.wv = nn.Linear(self.h_dim, self.vo, bias=False)
138
+ if vector_gate:
139
+ self.wg = nn.Linear(self.so, self.vo)
140
+ else:
141
+ self.ws = nn.Linear(self.si, self.so)
142
+
143
+ self.vector_gate = vector_gate
144
+ self.scalar_act, self.vector_act = activations
145
+ self.eps = eps
146
+
147
+ def forward(self, x):
148
+ '''
149
+ :param x: tuple (s, V) of `torch.Tensor`,
150
+ or (if vectors_in is 0), a single `torch.Tensor`
151
+ :return: tuple (s, V) of `torch.Tensor`,
152
+ or (if vectors_out is 0), a single `torch.Tensor`
153
+ '''
154
+ if self.vi:
155
+ s, v = x
156
+ v = torch.transpose(v, -1, -2)
157
+ vh = self.wh(v)
158
+ vn = _norm_no_nan(vh, axis=-2, eps=self.eps)
159
+ s = self.ws(torch.cat([s, vn], -1))
160
+ if self.scalar_act:
161
+ s = self.scalar_act(s)
162
+ if self.vo:
163
+ v = self.wv(vh)
164
+ v = torch.transpose(v, -1, -2)
165
+ if self.vector_gate:
166
+ g = self.wg(s).unsqueeze(-1)
167
+ else:
168
+ g = _norm_no_nan(v, axis=-1, keepdims=True, eps=self.eps)
169
+ if self.vector_act:
170
+ g = self.vector_act(g)
171
+ v = v * g
172
+ else:
173
+ if self.tuple_io:
174
+ assert x[1] is None
175
+ x = x[0]
176
+ s = self.ws(x)
177
+ if self.scalar_act:
178
+ s = self.scalar_act(s)
179
+ if self.vo:
180
+ v = torch.zeros(list(s.shape)[:-1] + [self.vo, 3],
181
+ device=s.device)
182
+
183
+ if self.vo:
184
+ return (s, v)
185
+ elif self.tuple_io:
186
+ return (s, None)
187
+ else:
188
+ return s
189
+
190
+
191
+ class _VDropout(nn.Module):
192
+ '''
193
+ Vector channel dropout where the elements of each
194
+ vector channel are dropped together.
195
+ '''
196
+ def __init__(self, drop_rate):
197
+ super(_VDropout, self).__init__()
198
+ self.drop_rate = drop_rate
199
+
200
+ def forward(self, x):
201
+ '''
202
+ :param x: `torch.Tensor` corresponding to vector channels
203
+ '''
204
+ if x is None:
205
+ return None
206
+ device = x.device
207
+ if not self.training:
208
+ return x
209
+ mask = torch.bernoulli(
210
+ (1 - self.drop_rate) * torch.ones(x.shape[:-1], device=device)
211
+ ).unsqueeze(-1)
212
+ x = mask * x / (1 - self.drop_rate)
213
+ return x
214
+
215
+ class Dropout(nn.Module):
216
+ '''
217
+ Combined dropout for tuples (s, V).
218
+ Takes tuples (s, V) as input and as output.
219
+ '''
220
+ def __init__(self, drop_rate):
221
+ super(Dropout, self).__init__()
222
+ self.sdropout = nn.Dropout(drop_rate)
223
+ self.vdropout = _VDropout(drop_rate)
224
+
225
+ def forward(self, x):
226
+ '''
227
+ :param x: tuple (s, V) of `torch.Tensor`,
228
+ or single `torch.Tensor`
229
+ (will be assumed to be scalar channels)
230
+ '''
231
+ if type(x) is torch.Tensor:
232
+ return self.sdropout(x)
233
+ s, v = x
234
+ return self.sdropout(s), self.vdropout(v)
235
+
236
+ class LayerNorm(nn.Module):
237
+ '''
238
+ Combined LayerNorm for tuples (s, V).
239
+ Takes tuples (s, V) as input and as output.
240
+ '''
241
+ def __init__(self, dims, tuple_io=True, eps=1e-8):
242
+ super(LayerNorm, self).__init__()
243
+ self.tuple_io = tuple_io
244
+ self.s, self.v = dims
245
+ self.scalar_norm = nn.LayerNorm(self.s)
246
+ self.eps = eps
247
+
248
+ def forward(self, x):
249
+ '''
250
+ :param x: tuple (s, V) of `torch.Tensor`,
251
+ or single `torch.Tensor`
252
+ (will be assumed to be scalar channels)
253
+ '''
254
+ if not self.v:
255
+ if self.tuple_io:
256
+ return self.scalar_norm(x[0]), None
257
+ return self.scalar_norm(x)
258
+ s, v = x
259
+ vn = _norm_no_nan(v, axis=-1, keepdims=True, sqrt=False, eps=self.eps)
260
+ nonzero_mask = (vn > 2 * self.eps)
261
+ vn = torch.sum(vn * nonzero_mask, dim=-2, keepdim=True
262
+ ) / (self.eps + torch.sum(nonzero_mask, dim=-2, keepdim=True))
263
+ vn = torch.sqrt(vn + self.eps)
264
+ v = nonzero_mask * (v / vn)
265
+ return self.scalar_norm(s), v
266
+
267
+ class GVPConv(MessagePassing):
268
+ '''
269
+ Graph convolution / message passing with Geometric Vector Perceptrons.
270
+ Takes in a graph with node and edge embeddings,
271
+ and returns new node embeddings.
272
+
273
+ This does NOT do residual updates and pointwise feedforward layers
274
+ ---see `GVPConvLayer`.
275
+
276
+ :param in_dims: input node embedding dimensions (n_scalar, n_vector)
277
+ :param out_dims: output node embedding dimensions (n_scalar, n_vector)
278
+ :param edge_dims: input edge embedding dimensions (n_scalar, n_vector)
279
+ :param n_layers: number of GVPs in the message function
280
+ :param module_list: preconstructed message function, overrides n_layers
281
+ :param aggr: should be "add" if some incoming edges are masked, as in
282
+ a masked autoregressive decoder architecture
283
+ '''
284
+ def __init__(self, in_dims, out_dims, edge_dims, n_layers=3,
285
+ vector_gate=False, module_list=None, aggr="mean", eps=1e-8,
286
+ activations=(F.relu, torch.sigmoid)):
287
+ super(GVPConv, self).__init__(aggr=aggr)
288
+ self.eps = eps
289
+ self.si, self.vi = in_dims
290
+ self.so, self.vo = out_dims
291
+ self.se, self.ve = edge_dims
292
+
293
+ module_list = module_list or []
294
+ if not module_list:
295
+ if n_layers == 1:
296
+ module_list.append(
297
+ GVP((2*self.si + self.se, 2*self.vi + self.ve),
298
+ (self.so, self.vo), activations=(None, None)))
299
+ else:
300
+ module_list.append(
301
+ GVP((2*self.si + self.se, 2*self.vi + self.ve), out_dims,
302
+ vector_gate=vector_gate, activations=activations)
303
+ )
304
+ for i in range(n_layers - 2):
305
+ module_list.append(GVP(out_dims, out_dims,
306
+ vector_gate=vector_gate))
307
+ module_list.append(GVP(out_dims, out_dims,
308
+ activations=(None, None)))
309
+ self.message_func = nn.Sequential(*module_list)
310
+
311
+ def forward(self, x, edge_index, edge_attr):
312
+ '''
313
+ :param x: tuple (s, V) of `torch.Tensor`
314
+ :param edge_index: array of shape [2, n_edges]
315
+ :param edge_attr: tuple (s, V) of `torch.Tensor`
316
+ '''
317
+ x_s, x_v = x
318
+ message = self.propagate(edge_index,
319
+ s=x_s, v=x_v.reshape(x_v.shape[0], 3*x_v.shape[1]),
320
+ edge_attr=edge_attr)
321
+ return _split(message, self.vo)
322
+
323
+ def message(self, s_i, v_i, s_j, v_j, edge_attr):
324
+ v_j = v_j.view(v_j.shape[0], v_j.shape[1]//3, 3)
325
+ v_i = v_i.view(v_i.shape[0], v_i.shape[1]//3, 3)
326
+ message = tuple_cat((s_j, v_j), edge_attr, (s_i, v_i))
327
+ message = self.message_func(message)
328
+ return _merge(*message)
329
+
330
+
331
+ class GVPConvLayer(nn.Module):
332
+ '''
333
+ Full graph convolution / message passing layer with
334
+ Geometric Vector Perceptrons. Residually updates node embeddings with
335
+ aggregated incoming messages, applies a pointwise feedforward
336
+ network to node embeddings, and returns updated node embeddings.
337
+
338
+ To only compute the aggregated messages, see `GVPConv`.
339
+
340
+ :param node_dims: node embedding dimensions (n_scalar, n_vector)
341
+ :param edge_dims: input edge embedding dimensions (n_scalar, n_vector)
342
+ :param n_message: number of GVPs to use in message function
343
+ :param n_feedforward: number of GVPs to use in feedforward function
344
+ :param drop_rate: drop probability in all dropout layers
345
+ :param autoregressive: if `True`, this `GVPConvLayer` will be used
346
+ with a different set of input node embeddings for messages
347
+ where src >= dst
348
+ '''
349
+ def __init__(self, node_dims, edge_dims, vector_gate=False,
350
+ n_message=3, n_feedforward=2, drop_rate=.1,
351
+ autoregressive=False, attention_heads=0,
352
+ conv_activations=(F.relu, torch.sigmoid),
353
+ n_edge_gvps=0, layernorm=True, eps=1e-8):
354
+
355
+ super(GVPConvLayer, self).__init__()
356
+ if attention_heads == 0:
357
+ self.conv = GVPConv(
358
+ node_dims, node_dims, edge_dims, n_layers=n_message,
359
+ vector_gate=vector_gate,
360
+ aggr="add" if autoregressive else "mean",
361
+ activations=conv_activations,
362
+ eps=eps,
363
+ )
364
+ else:
365
+ raise NotImplementedError
366
+ if layernorm:
367
+ self.norm = nn.ModuleList([LayerNorm(node_dims, eps=eps) for _ in range(2)])
368
+ else:
369
+ self.norm = nn.ModuleList([nn.Identity() for _ in range(2)])
370
+ self.dropout = nn.ModuleList([Dropout(drop_rate) for _ in range(2)])
371
+
372
+ ff_func = []
373
+ if n_feedforward == 1:
374
+ ff_func.append(GVP(node_dims, node_dims, activations=(None, None)))
375
+ else:
376
+ hid_dims = 4*node_dims[0], 2*node_dims[1]
377
+ ff_func.append(GVP(node_dims, hid_dims, vector_gate=vector_gate))
378
+ for i in range(n_feedforward-2):
379
+ ff_func.append(GVP(hid_dims, hid_dims, vector_gate=vector_gate))
380
+ ff_func.append(GVP(hid_dims, node_dims, activations=(None, None)))
381
+ self.ff_func = nn.Sequential(*ff_func)
382
+
383
+ self.edge_message_func = None
384
+ if n_edge_gvps > 0:
385
+ si, vi = node_dims
386
+ se, ve = edge_dims
387
+ module_list = [
388
+ GVP((2*si + se, 2*vi + ve), edge_dims, vector_gate=vector_gate)
389
+ ]
390
+ for i in range(n_edge_gvps - 2):
391
+ module_list.append(GVP(edge_dims, edge_dims,
392
+ vector_gate=vector_gate))
393
+ if n_edge_gvps > 1:
394
+ module_list.append(GVP(edge_dims, edge_dims,
395
+ activations=(None, None)))
396
+ self.edge_message_func = nn.Sequential(*module_list)
397
+ if layernorm:
398
+ self.edge_norm = LayerNorm(edge_dims, eps=eps)
399
+ else:
400
+ self.edge_norm = nn.Identity()
401
+ self.edge_dropout = Dropout(drop_rate)
402
+
403
+ def forward(self, x, edge_index, edge_attr,
404
+ autoregressive_x=None, node_mask=None):
405
+ '''
406
+ :param x: tuple (s, V) of `torch.Tensor`
407
+ :param edge_index: array of shape [2, n_edges]
408
+ :param edge_attr: tuple (s, V) of `torch.Tensor`
409
+ :param autoregressive_x: tuple (s, V) of `torch.Tensor`.
410
+ If not `None`, will be used as srcqq node embeddings
411
+ for forming messages where src >= dst. The corrent node
412
+ embeddings `x` will still be the base of the update and the
413
+ pointwise feedforward.
414
+ :param node_mask: array of type `bool` to index into the first
415
+ dim of node embeddings (s, V). If not `None`, only
416
+ these nodes will be updated.
417
+ '''
418
+ if self.edge_message_func:
419
+ src, dst = edge_index
420
+ if autoregressive_x is None:
421
+ x_src = x[0][src], x[1][src]
422
+ else:
423
+ mask = (src < dst).unsqueeze(-1)
424
+ x_src = (
425
+ torch.where(mask, x[0][src], autoregressive_x[0][src]),
426
+ torch.where(mask.unsqueeze(-1), x[1][src],
427
+ autoregressive_x[1][src])
428
+ )
429
+ x_dst = x[0][dst], x[1][dst]
430
+ x_edge = (
431
+ torch.cat([x_src[0], edge_attr[0], x_dst[0]], dim=-1),
432
+ torch.cat([x_src[1], edge_attr[1], x_dst[1]], dim=-2)
433
+ )
434
+ edge_attr_dh = self.edge_message_func(x_edge)
435
+ edge_attr = self.edge_norm(tuple_sum(edge_attr,
436
+ self.edge_dropout(edge_attr_dh)))
437
+
438
+ if autoregressive_x is not None:
439
+ # Guarding this import here to remove the dependency on torch_scatter, since this isn't used
440
+ # in ESM-IF1
441
+ from torch_scatter import scatter_add
442
+ src, dst = edge_index
443
+ mask = src < dst
444
+ edge_index_forward = edge_index[:, mask]
445
+ edge_index_backward = edge_index[:, ~mask]
446
+ edge_attr_forward = tuple_index(edge_attr, mask)
447
+ edge_attr_backward = tuple_index(edge_attr, ~mask)
448
+
449
+ dh = tuple_sum(
450
+ self.conv(x, edge_index_forward, edge_attr_forward),
451
+ self.conv(autoregressive_x, edge_index_backward, edge_attr_backward)
452
+ )
453
+
454
+ count = scatter_add(torch.ones_like(dst), dst,
455
+ dim_size=dh[0].size(0)).clamp(min=1).unsqueeze(-1)
456
+
457
+ dh = dh[0] / count, dh[1] / count.unsqueeze(-1)
458
+
459
+ else:
460
+ dh = self.conv(x, edge_index, edge_attr)
461
+
462
+ if node_mask is not None:
463
+ x_ = x
464
+ x, dh = tuple_index(x, node_mask), tuple_index(dh, node_mask)
465
+
466
+ x = self.norm[0](tuple_sum(x, self.dropout[0](dh)))
467
+
468
+ dh = self.ff_func(x)
469
+ x = self.norm[1](tuple_sum(x, self.dropout[1](dh)))
470
+
471
+ if node_mask is not None:
472
+ x_[0][node_mask], x_[1][node_mask] = x[0], x[1]
473
+ x = x_
474
+
475
+ return x, edge_attr
models/esm/inverse_folding/gvp_transformer.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import argparse
7
+ from typing import Any, Dict, List, Optional, Tuple, NamedTuple
8
+ import torch
9
+ from torch import nn
10
+ from torch import Tensor
11
+ import torch.nn.functional as F
12
+ from scipy.spatial import transform
13
+
14
+ from onescience.datapipes.esm import Alphabet
15
+
16
+ from .features import DihedralFeatures
17
+ from .gvp_encoder import GVPEncoder
18
+ from .gvp_utils import unflatten_graph
19
+ from .gvp_transformer_encoder import GVPTransformerEncoder
20
+ from .transformer_decoder import TransformerDecoder
21
+ from .util import rotate, CoordBatchConverter
22
+
23
+
24
+ class GVPTransformerModel(nn.Module):
25
+ """
26
+ GVP-Transformer inverse folding model.
27
+
28
+ Architecture: Geometric GVP-GNN as initial layers, followed by
29
+ sequence-to-sequence Transformer encoder and decoder.
30
+ """
31
+
32
+ def __init__(self, args, alphabet):
33
+ super().__init__()
34
+ encoder_embed_tokens = self.build_embedding(
35
+ args, alphabet, args.encoder_embed_dim,
36
+ )
37
+ decoder_embed_tokens = self.build_embedding(
38
+ args, alphabet, args.decoder_embed_dim,
39
+ )
40
+ encoder = self.build_encoder(args, alphabet, encoder_embed_tokens)
41
+ decoder = self.build_decoder(args, alphabet, decoder_embed_tokens)
42
+ self.args = args
43
+ self.encoder = encoder
44
+ self.decoder = decoder
45
+
46
+ @classmethod
47
+ def build_encoder(cls, args, src_dict, embed_tokens):
48
+ encoder = GVPTransformerEncoder(args, src_dict, embed_tokens)
49
+ return encoder
50
+
51
+ @classmethod
52
+ def build_decoder(cls, args, tgt_dict, embed_tokens):
53
+ decoder = TransformerDecoder(
54
+ args,
55
+ tgt_dict,
56
+ embed_tokens,
57
+ )
58
+ return decoder
59
+
60
+ @classmethod
61
+ def build_embedding(cls, args, dictionary, embed_dim):
62
+ num_embeddings = len(dictionary)
63
+ padding_idx = dictionary.padding_idx
64
+ emb = nn.Embedding(num_embeddings, embed_dim, padding_idx)
65
+ nn.init.normal_(emb.weight, mean=0, std=embed_dim ** -0.5)
66
+ nn.init.constant_(emb.weight[padding_idx], 0)
67
+ return emb
68
+
69
+ def forward(
70
+ self,
71
+ coords,
72
+ padding_mask,
73
+ confidence,
74
+ prev_output_tokens,
75
+ return_all_hiddens: bool = False,
76
+ features_only: bool = False,
77
+ ):
78
+ encoder_out = self.encoder(coords, padding_mask, confidence,
79
+ return_all_hiddens=return_all_hiddens)
80
+ logits, extra = self.decoder(
81
+ prev_output_tokens,
82
+ encoder_out=encoder_out,
83
+ features_only=features_only,
84
+ return_all_hiddens=return_all_hiddens,
85
+ )
86
+ return logits, extra
87
+
88
+ def sample(self, coords, partial_seq=None, temperature=1.0, confidence=None, device=None):
89
+ """
90
+ Samples sequences based on multinomial sampling (no beam search).
91
+
92
+ Args:
93
+ coords: L x 3 x 3 list representing one backbone
94
+ partial_seq: Optional, partial sequence with mask tokens if part of
95
+ the sequence is known
96
+ temperature: sampling temperature, use low temperature for higher
97
+ sequence recovery and high temperature for higher diversity
98
+ confidence: optional length L list of confidence scores for coordinates
99
+ """
100
+ L = len(coords)
101
+ # Convert to batch format
102
+ batch_converter = CoordBatchConverter(self.decoder.dictionary)
103
+ batch_coords, confidence, _, _, padding_mask = (
104
+ batch_converter([(coords, confidence, None)], device=device)
105
+ )
106
+
107
+ # Start with prepend token
108
+ mask_idx = self.decoder.dictionary.get_idx('<mask>')
109
+ sampled_tokens = torch.full((1, 1+L), mask_idx, dtype=int)
110
+ sampled_tokens[0, 0] = self.decoder.dictionary.get_idx('<cath>')
111
+ if partial_seq is not None:
112
+ for i, c in enumerate(partial_seq):
113
+ sampled_tokens[0, i+1] = self.decoder.dictionary.get_idx(c)
114
+
115
+ # Save incremental states for faster sampling
116
+ incremental_state = dict()
117
+
118
+ # Run encoder only once
119
+ encoder_out = self.encoder(batch_coords, padding_mask, confidence)
120
+
121
+ # Make sure all tensors are on the same device if a GPU is present
122
+ if device:
123
+ sampled_tokens = sampled_tokens.to(device)
124
+
125
+ # Decode one token at a time
126
+ for i in range(1, L+1):
127
+ logits, _ = self.decoder(
128
+ sampled_tokens[:, :i],
129
+ encoder_out,
130
+ incremental_state=incremental_state,
131
+ )
132
+ logits = logits[0].transpose(0, 1)
133
+ logits /= temperature
134
+ probs = F.softmax(logits, dim=-1)
135
+ if sampled_tokens[0, i] == mask_idx:
136
+ sampled_tokens[:, i] = torch.multinomial(probs, 1).squeeze(-1)
137
+ sampled_seq = sampled_tokens[0, 1:]
138
+
139
+ # Convert back to string via lookup
140
+ return ''.join([self.decoder.dictionary.get_tok(a) for a in sampled_seq])
models/esm/inverse_folding/gvp_transformer_encoder.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # Contents of this file were adapted from the open source fairseq repository.
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ import argparse
9
+ import math
10
+ from typing import Dict, List, Optional
11
+
12
+ import torch
13
+ import torch.nn as nn
14
+ from torch import Tensor
15
+
16
+ from onescience.modules.esm import SinusoidalPositionalEmbedding
17
+ from .features import GVPInputFeaturizer, DihedralFeatures
18
+ from .gvp_encoder import GVPEncoder
19
+ from .transformer_layer import TransformerEncoderLayer
20
+ from .util import nan_to_num, get_rotation_frames, rotate, rbf
21
+
22
+
23
+ class GVPTransformerEncoder(nn.Module):
24
+ """
25
+ Transformer encoder consisting of *args.encoder.layers* layers. Each layer
26
+ is a :class:`TransformerEncoderLayer`.
27
+
28
+ Args:
29
+ args (argparse.Namespace): parsed command-line arguments
30
+ dictionary (~fairseq.data.Dictionary): encoding dictionary
31
+ embed_tokens (torch.nn.Embedding): input embedding
32
+ """
33
+
34
+ def __init__(self, args, dictionary, embed_tokens):
35
+ super().__init__()
36
+ self.args = args
37
+ self.dictionary = dictionary
38
+
39
+ self.dropout_module = nn.Dropout(args.dropout)
40
+
41
+ embed_dim = embed_tokens.embedding_dim
42
+ self.padding_idx = embed_tokens.padding_idx
43
+
44
+ self.embed_tokens = embed_tokens
45
+ self.embed_scale = math.sqrt(embed_dim)
46
+ self.embed_positions = SinusoidalPositionalEmbedding(
47
+ embed_dim,
48
+ self.padding_idx,
49
+ )
50
+ self.embed_gvp_input_features = nn.Linear(15, embed_dim)
51
+ self.embed_confidence = nn.Linear(16, embed_dim)
52
+ self.embed_dihedrals = DihedralFeatures(embed_dim)
53
+
54
+ gvp_args = argparse.Namespace()
55
+ for k, v in vars(args).items():
56
+ if k.startswith("gvp_"):
57
+ setattr(gvp_args, k[4:], v)
58
+ self.gvp_encoder = GVPEncoder(gvp_args)
59
+ gvp_out_dim = gvp_args.node_hidden_dim_scalar + (3 *
60
+ gvp_args.node_hidden_dim_vector)
61
+ self.embed_gvp_output = nn.Linear(gvp_out_dim, embed_dim)
62
+
63
+ self.layers = nn.ModuleList([])
64
+ self.layers.extend(
65
+ [self.build_encoder_layer(args) for i in range(args.encoder_layers)]
66
+ )
67
+ self.num_layers = len(self.layers)
68
+ self.layer_norm = nn.LayerNorm(embed_dim)
69
+
70
+ def build_encoder_layer(self, args):
71
+ return TransformerEncoderLayer(args)
72
+
73
+ def forward_embedding(self, coords, padding_mask, confidence):
74
+ """
75
+ Args:
76
+ coords: N, CA, C backbone coordinates in shape length x 3 (atoms) x 3
77
+ padding_mask: boolean Tensor (true for padding) of shape length
78
+ confidence: confidence scores between 0 and 1 of shape length
79
+ """
80
+ components = dict()
81
+ coord_mask = torch.all(torch.all(torch.isfinite(coords), dim=-1), dim=-1)
82
+ coords = nan_to_num(coords)
83
+ mask_tokens = (
84
+ padding_mask * self.dictionary.padding_idx +
85
+ ~padding_mask * self.dictionary.get_idx("<mask>")
86
+ )
87
+ components["tokens"] = self.embed_tokens(mask_tokens) * self.embed_scale
88
+ components["diherals"] = self.embed_dihedrals(coords)
89
+
90
+ # GVP encoder
91
+ gvp_out_scalars, gvp_out_vectors = self.gvp_encoder(coords,
92
+ coord_mask, padding_mask, confidence)
93
+ R = get_rotation_frames(coords)
94
+ # Rotate to local rotation frame for rotation-invariance
95
+ gvp_out_features = torch.cat([
96
+ gvp_out_scalars,
97
+ rotate(gvp_out_vectors, R.transpose(-2, -1)).flatten(-2, -1),
98
+ ], dim=-1)
99
+ components["gvp_out"] = self.embed_gvp_output(gvp_out_features)
100
+
101
+ components["confidence"] = self.embed_confidence(
102
+ rbf(confidence, 0., 1.))
103
+
104
+ # In addition to GVP encoder outputs, also directly embed GVP input node
105
+ # features to the Transformer
106
+ scalar_features, vector_features = GVPInputFeaturizer.get_node_features(
107
+ coords, coord_mask, with_coord_mask=False)
108
+ features = torch.cat([
109
+ scalar_features,
110
+ rotate(vector_features, R.transpose(-2, -1)).flatten(-2, -1),
111
+ ], dim=-1)
112
+ components["gvp_input_features"] = self.embed_gvp_input_features(features)
113
+
114
+ embed = sum(components.values())
115
+ # for k, v in components.items():
116
+ # print(k, torch.mean(v, dim=(0,1)), torch.std(v, dim=(0,1)))
117
+
118
+ x = embed
119
+ x = x + self.embed_positions(mask_tokens)
120
+ x = self.dropout_module(x)
121
+ return x, components
122
+
123
+ def forward(
124
+ self,
125
+ coords,
126
+ encoder_padding_mask,
127
+ confidence,
128
+ return_all_hiddens: bool = False,
129
+ ):
130
+ """
131
+ Args:
132
+ coords (Tensor): backbone coordinates
133
+ shape batch_size x num_residues x num_atoms (3 for N, CA, C) x 3
134
+ encoder_padding_mask (ByteTensor): the positions of
135
+ padding elements of shape `(batch_size x num_residues)`
136
+ confidence (Tensor): the confidence score of shape (batch_size x
137
+ num_residues). The value is between 0. and 1. for each residue
138
+ coordinate, or -1. if no coordinate is given
139
+ return_all_hiddens (bool, optional): also return all of the
140
+ intermediate hidden states (default: False).
141
+
142
+ Returns:
143
+ dict:
144
+ - **encoder_out** (Tensor): the last encoder layer's output of
145
+ shape `(num_residues, batch_size, embed_dim)`
146
+ - **encoder_padding_mask** (ByteTensor): the positions of
147
+ padding elements of shape `(batch_size, num_residues)`
148
+ - **encoder_embedding** (Tensor): the (scaled) embedding lookup
149
+ of shape `(batch_size, num_residues, embed_dim)`
150
+ - **encoder_states** (List[Tensor]): all intermediate
151
+ hidden states of shape `(num_residues, batch_size, embed_dim)`.
152
+ Only populated if *return_all_hiddens* is True.
153
+ """
154
+ x, encoder_embedding = self.forward_embedding(coords,
155
+ encoder_padding_mask, confidence)
156
+ # account for padding while computing the representation
157
+ x = x * (1 - encoder_padding_mask.unsqueeze(-1).type_as(x))
158
+
159
+ # B x T x C -> T x B x C
160
+ x = x.transpose(0, 1)
161
+
162
+ encoder_states = []
163
+
164
+ if return_all_hiddens:
165
+ encoder_states.append(x)
166
+
167
+ # encoder layers
168
+ for layer in self.layers:
169
+ x = layer(
170
+ x, encoder_padding_mask=encoder_padding_mask
171
+ )
172
+ if return_all_hiddens:
173
+ assert encoder_states is not None
174
+ encoder_states.append(x)
175
+
176
+ if self.layer_norm is not None:
177
+ x = self.layer_norm(x)
178
+
179
+ return {
180
+ "encoder_out": [x], # T x B x C
181
+ "encoder_padding_mask": [encoder_padding_mask], # B x T
182
+ "encoder_embedding": [encoder_embedding], # dictionary
183
+ "encoder_states": encoder_states, # List[T x B x C]
184
+ }
models/esm/inverse_folding/multichain_util.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ import biotite.structure
7
+ import numpy as np
8
+ import torch
9
+ from typing import Sequence, Tuple, List
10
+
11
+ from .esm.inverse_folding.util import (
12
+ load_structure,
13
+ extract_coords_from_structure,
14
+ load_coords,
15
+ get_sequence_loss,
16
+ get_encoder_output,
17
+ )
18
+
19
+
20
+ def extract_coords_from_complex(structure: biotite.structure.AtomArray):
21
+ """
22
+ Args:
23
+ structure: biotite AtomArray
24
+ Returns:
25
+ Tuple (coords_list, seq_list)
26
+ - coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
27
+ coordinates representing the backbone of each chain
28
+ - seqs: Dictionary mapping chain ids to native sequences of each chain
29
+ """
30
+ coords = {}
31
+ seqs = {}
32
+ all_chains = biotite.structure.get_chains(structure)
33
+ for chain_id in all_chains:
34
+ chain = structure[structure.chain_id == chain_id]
35
+ coords[chain_id], seqs[chain_id] = extract_coords_from_structure(chain)
36
+ return coords, seqs
37
+
38
+
39
+ def load_complex_coords(fpath, chains):
40
+ """
41
+ Args:
42
+ fpath: filepath to either pdb or cif file
43
+ chains: the chain ids (the order matters for autoregressive model)
44
+ Returns:
45
+ Tuple (coords_list, seq_list)
46
+ - coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
47
+ coordinates representing the backbone of each chain
48
+ - seqs: Dictionary mapping chain ids to native sequences of each chain
49
+ """
50
+ structure = load_structure(fpath, chains)
51
+ return extract_coords_from_complex(structure)
52
+
53
+
54
+ def _concatenate_coords(coords, target_chain_id, padding_length=10):
55
+ """
56
+ Args:
57
+ coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
58
+ coordinates representing the backbone of each chain
59
+ target_chain_id: The chain id to sample sequences for
60
+ padding_length: Length of padding between concatenated chains
61
+ Returns:
62
+ Tuple (coords, seq)
63
+ - coords is an L x 3 x 3 array for N, CA, C coordinates, a
64
+ concatenation of the chains with padding in between
65
+ - seq is the extracted sequence, with padding tokens inserted
66
+ between the concatenated chains
67
+ """
68
+ pad_coords = np.full((padding_length, 3, 3), np.nan, dtype=np.float32)
69
+ # For best performance, put the target chain first in concatenation.
70
+ coords_list = [coords[target_chain_id]]
71
+ for chain_id in coords:
72
+ if chain_id == target_chain_id:
73
+ continue
74
+ coords_list.append(pad_coords)
75
+ coords_list.append(coords[chain_id])
76
+ coords_concatenated = np.concatenate(coords_list, axis=0)
77
+ return coords_concatenated
78
+
79
+
80
+ def sample_sequence_in_complex(model, coords, target_chain_id, temperature=1.,
81
+ padding_length=10):
82
+ """
83
+ Samples sequence for one chain in a complex.
84
+ Args:
85
+ model: An instance of the GVPTransformer model
86
+ coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
87
+ coordinates representing the backbone of each chain
88
+ target_chain_id: The chain id to sample sequences for
89
+ padding_length: padding length in between chains
90
+ Returns:
91
+ Sampled sequence for the target chain
92
+ """
93
+ target_chain_len = coords[target_chain_id].shape[0]
94
+ all_coords = _concatenate_coords(coords, target_chain_id)
95
+ device = next(model.parameters()).device
96
+
97
+ # Supply padding tokens for other chains to avoid unused sampling for speed
98
+ padding_pattern = ['<pad>'] * all_coords.shape[0]
99
+ for i in range(target_chain_len):
100
+ padding_pattern[i] = '<mask>'
101
+ sampled = model.sample(all_coords, partial_seq=padding_pattern,
102
+ temperature=temperature, device=device)
103
+ sampled = sampled[:target_chain_len]
104
+ return sampled
105
+
106
+
107
+ def score_sequence_in_complex(model, alphabet, coords, target_chain_id,
108
+ target_seq, padding_length=10):
109
+ """
110
+ Scores sequence for one chain in a complex.
111
+ Args:
112
+ model: An instance of the GVPTransformer model
113
+ alphabet: Alphabet for the model
114
+ coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
115
+ coordinates representing the backbone of each chain
116
+ target_chain_id: The chain id to sample sequences for
117
+ target_seq: Target sequence for the target chain for scoring.
118
+ padding_length: padding length in between chains
119
+ Returns:
120
+ Tuple (ll_fullseq, ll_withcoord)
121
+ - ll_fullseq: Average log-likelihood over the full target chain
122
+ - ll_withcoord: Average log-likelihood in target chain excluding those
123
+ residues without coordinates
124
+ """
125
+ all_coords = _concatenate_coords(coords, target_chain_id)
126
+
127
+ loss, target_padding_mask = get_sequence_loss(model, alphabet, all_coords,
128
+ target_seq)
129
+ ll_fullseq = -np.sum(loss * ~target_padding_mask) / np.sum(
130
+ ~target_padding_mask)
131
+
132
+ # Also calculate average when excluding masked portions
133
+ coord_mask = np.all(np.isfinite(coords[target_chain_id]), axis=(-1, -2))
134
+ ll_withcoord = -np.sum(loss * coord_mask) / np.sum(coord_mask)
135
+ return ll_fullseq, ll_withcoord
136
+
137
+
138
+ def get_encoder_output_for_complex(model, alphabet, coords, target_chain_id):
139
+ """
140
+ Args:
141
+ model: An instance of the GVPTransformer model
142
+ alphabet: Alphabet for the model
143
+ coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
144
+ coordinates representing the backbone of each chain
145
+ target_chain_id: The chain id to sample sequences for
146
+ Returns:
147
+ Dictionary mapping chain id to encoder output for each chain
148
+ """
149
+ all_coords = _concatenate_coords(coords, target_chain_id)
150
+ all_rep = get_encoder_output(model, alphabet, all_coords)
151
+ target_chain_len = coords[target_chain_id].shape[0]
152
+ return all_rep[:target_chain_len]
models/esm/inverse_folding/transformer_decoder.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # Contents of this file were adapted from the open source fairseq repository.
4
+ #
5
+ # This source code is licensed under the MIT license found in the
6
+ # LICENSE file in the root directory of this source tree.
7
+
8
+ import math
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+ from torch import Tensor
14
+
15
+ from onescience.modules.esm import SinusoidalPositionalEmbedding
16
+ from .transformer_layer import TransformerDecoderLayer
17
+
18
+
19
+ def fill_with_neg_inf(t):
20
+ """FP16-compatible function that fills a tensor with -inf."""
21
+ return t.float().fill_(float("-inf")).type_as(t)
22
+
23
+
24
+ class TransformerDecoder(nn.Module):
25
+ """
26
+ Transformer decoder consisting of *args.decoder.layers* layers. Each layer
27
+ is a :class:`TransformerDecoderLayer`.
28
+
29
+ Args:
30
+ args (argparse.Namespace): parsed command-line arguments
31
+ dictionary (~fairseq.data.Dictionary): decoding dictionary
32
+ embed_tokens (torch.nn.Embedding): output embedding
33
+ no_encoder_attn (bool, optional): whether to attend to encoder outputs
34
+ (default: False).
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ args,
40
+ dictionary,
41
+ embed_tokens,
42
+ ):
43
+ super().__init__()
44
+ self.args = args
45
+ self.dictionary = dictionary
46
+ self._future_mask = torch.empty(0)
47
+
48
+ self.dropout_module = nn.Dropout(args.dropout)
49
+
50
+ input_embed_dim = embed_tokens.embedding_dim
51
+ embed_dim = args.decoder_embed_dim
52
+ self.embed_dim = embed_dim
53
+
54
+ self.padding_idx = embed_tokens.padding_idx
55
+
56
+ self.embed_tokens = embed_tokens
57
+ self.embed_scale = math.sqrt(embed_dim)
58
+
59
+ self.project_in_dim = (
60
+ nn.Linear(input_embed_dim, embed_dim, bias=False)
61
+ if embed_dim != input_embed_dim
62
+ else None
63
+ )
64
+ self.embed_positions = SinusoidalPositionalEmbedding(
65
+ embed_dim,
66
+ self.padding_idx,
67
+ )
68
+
69
+ self.layers = nn.ModuleList([])
70
+ self.layers.extend(
71
+ [
72
+ self.build_decoder_layer(args)
73
+ for _ in range(args.decoder_layers)
74
+ ]
75
+ )
76
+ self.num_layers = len(self.layers)
77
+ self.layer_norm = nn.LayerNorm(embed_dim)
78
+
79
+ self.build_output_projection(args, dictionary)
80
+
81
+ def build_output_projection(self, args, dictionary):
82
+ self.output_projection = nn.Linear(
83
+ args.decoder_embed_dim, len(dictionary), bias=False
84
+ )
85
+ nn.init.normal_(
86
+ self.output_projection.weight, mean=0, std=args.decoder_embed_dim ** -0.5
87
+ )
88
+
89
+ def build_decoder_layer(self, args):
90
+ return TransformerDecoderLayer(args)
91
+
92
+ def forward(
93
+ self,
94
+ prev_output_tokens,
95
+ encoder_out: Optional[Dict[str, List[Tensor]]] = None,
96
+ incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
97
+ features_only: bool = False,
98
+ return_all_hiddens: bool = False,
99
+ ):
100
+ """
101
+ Args:
102
+ prev_output_tokens (LongTensor): previous decoder outputs of shape
103
+ `(batch, tgt_len)`, for teacher forcing
104
+ encoder_out (optional): output from the encoder, used for
105
+ encoder-side attention, should be of size T x B x C
106
+ incremental_state (dict): dictionary used for storing state during
107
+ :ref:`Incremental decoding`
108
+ features_only (bool, optional): only return features without
109
+ applying output layer (default: False).
110
+
111
+ Returns:
112
+ tuple:
113
+ - the decoder's output of shape `(batch, tgt_len, vocab)`
114
+ - a dictionary with any model-specific outputs
115
+ """
116
+
117
+ x, extra = self.extract_features(
118
+ prev_output_tokens,
119
+ encoder_out=encoder_out,
120
+ incremental_state=incremental_state,
121
+ )
122
+
123
+ if not features_only:
124
+ x = self.output_layer(x)
125
+ x = x.transpose(1, 2) # B x T x C -> B x C x T
126
+ return x, extra
127
+
128
+ def extract_features(
129
+ self,
130
+ prev_output_tokens,
131
+ encoder_out: Optional[Dict[str, List[Tensor]]],
132
+ incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
133
+ ):
134
+ """
135
+ Similar to *forward* but only return features.
136
+
137
+ Includes several features from "Jointly Learning to Align and
138
+ Translate with Transformer Models" (Garg et al., EMNLP 2019).
139
+
140
+ Returns:
141
+ tuple:
142
+ - the decoder's features of shape `(batch, tgt_len, embed_dim)`
143
+ - a dictionary with any model-specific outputs
144
+ """
145
+ bs, slen = prev_output_tokens.size()
146
+
147
+ enc: Optional[Tensor] = None
148
+ padding_mask: Optional[Tensor] = None
149
+ if encoder_out is not None and len(encoder_out["encoder_out"]) > 0:
150
+ enc = encoder_out["encoder_out"][0]
151
+ assert (
152
+ enc.size()[1] == bs
153
+ ), f"Expected enc.shape == (t, {bs}, c) got {enc.shape}"
154
+ if encoder_out is not None and len(encoder_out["encoder_padding_mask"]) > 0:
155
+ padding_mask = encoder_out["encoder_padding_mask"][0]
156
+
157
+ # embed positions
158
+ positions = self.embed_positions(
159
+ prev_output_tokens
160
+ )
161
+
162
+ if incremental_state is not None:
163
+ prev_output_tokens = prev_output_tokens[:, -1:]
164
+ positions = positions[:, -1:]
165
+
166
+ # embed tokens and positions
167
+ x = self.embed_scale * self.embed_tokens(prev_output_tokens)
168
+
169
+ if self.project_in_dim is not None:
170
+ x = self.project_in_dim(x)
171
+
172
+ x += positions
173
+
174
+ x = self.dropout_module(x)
175
+
176
+ # B x T x C -> T x B x C
177
+ x = x.transpose(0, 1)
178
+
179
+ self_attn_padding_mask: Optional[Tensor] = None
180
+ if prev_output_tokens.eq(self.padding_idx).any():
181
+ self_attn_padding_mask = prev_output_tokens.eq(self.padding_idx)
182
+
183
+ # decoder layers
184
+ attn: Optional[Tensor] = None
185
+ inner_states: List[Optional[Tensor]] = [x]
186
+ for idx, layer in enumerate(self.layers):
187
+ if incremental_state is None:
188
+ self_attn_mask = self.buffered_future_mask(x)
189
+ else:
190
+ self_attn_mask = None
191
+
192
+ x, layer_attn, _ = layer(
193
+ x,
194
+ enc,
195
+ padding_mask,
196
+ incremental_state,
197
+ self_attn_mask=self_attn_mask,
198
+ self_attn_padding_mask=self_attn_padding_mask,
199
+ need_attn=False,
200
+ need_head_weights=False,
201
+ )
202
+ inner_states.append(x)
203
+
204
+ if self.layer_norm is not None:
205
+ x = self.layer_norm(x)
206
+
207
+ # T x B x C -> B x C x T
208
+ x = x.transpose(0, 1)
209
+
210
+ return x, {"inner_states": inner_states}
211
+
212
+ def output_layer(self, features):
213
+ """Project features to the vocabulary size."""
214
+ return self.output_projection(features)
215
+
216
+ def buffered_future_mask(self, tensor):
217
+ dim = tensor.size(0)
218
+ # self._future_mask.device != tensor.device is not working in TorchScript. This is a workaround.
219
+ if (
220
+ self._future_mask.size(0) == 0
221
+ or (not self._future_mask.device == tensor.device)
222
+ or self._future_mask.size(0) < dim
223
+ ):
224
+ self._future_mask = torch.triu(
225
+ fill_with_neg_inf(torch.zeros([dim, dim])), 1
226
+ )
227
+ self._future_mask = self._future_mask.to(tensor)
228
+ return self._future_mask[:dim, :dim]
models/openfold/__init__.py ADDED
File without changes
models/openfold/__pycache__/dropout.cpython-310.pyc ADDED
Binary file (2.09 kB). View file
 
models/openfold/__pycache__/embedders.cpython-310.pyc ADDED
Binary file (20.2 kB). View file
 
models/openfold/__pycache__/evoformer.cpython-310.pyc ADDED
Binary file (23.7 kB). View file
 
models/openfold/__pycache__/heads.cpython-310.pyc ADDED
Binary file (6.29 kB). View file
 
models/openfold/__pycache__/model.cpython-310.pyc ADDED
Binary file (12.5 kB). View file
 
models/openfold/__pycache__/msa.cpython-310.pyc ADDED
Binary file (10.3 kB). View file
 
models/openfold/__pycache__/outer_product_mean.cpython-310.pyc ADDED
Binary file (3.5 kB). View file
 
models/openfold/__pycache__/pair_transition.cpython-310.pyc ADDED
Binary file (2.24 kB). View file
 
models/openfold/__pycache__/primitives.cpython-310.pyc ADDED
Binary file (21.5 kB). View file
 
models/openfold/__pycache__/structure_module.cpython-310.pyc ADDED
Binary file (22.8 kB). View file
 
models/openfold/__pycache__/template.cpython-310.pyc ADDED
Binary file (14.8 kB). View file
 
models/openfold/__pycache__/torchscript.cpython-310.pyc ADDED
Binary file (5.31 kB). View file
 
models/openfold/__pycache__/triangular_attention.cpython-310.pyc ADDED
Binary file (3.41 kB). View file
 
models/openfold/__pycache__/triangular_multiplicative_update.cpython-310.pyc ADDED
Binary file (16 kB). View file
 
models/openfold/dropout.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ import torch
17
+ import torch.nn as nn
18
+ from functools import partialmethod
19
+ from typing import Union, List
20
+
21
+
22
+ class Dropout(nn.Module):
23
+ """
24
+ Implementation of dropout with the ability to share the dropout mask
25
+ along a particular dimension.
26
+
27
+ If not in training mode, this module computes the identity function.
28
+ """
29
+
30
+ def __init__(self, r: float, batch_dim: Union[int, List[int]]):
31
+ """
32
+ Args:
33
+ r:
34
+ Dropout rate
35
+ batch_dim:
36
+ Dimension(s) along which the dropout mask is shared
37
+ """
38
+ super(Dropout, self).__init__()
39
+
40
+ self.r = r
41
+ if type(batch_dim) == int:
42
+ batch_dim = [batch_dim]
43
+ self.batch_dim = batch_dim
44
+ self.dropout = nn.Dropout(self.r)
45
+
46
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
47
+ """
48
+ Args:
49
+ x:
50
+ Tensor to which dropout is applied. Can have any shape
51
+ compatible with self.batch_dim
52
+ """
53
+ shape = list(x.shape)
54
+ if self.batch_dim is not None:
55
+ for bd in self.batch_dim:
56
+ shape[bd] = 1
57
+ mask = x.new_ones(shape)
58
+ mask = self.dropout(mask)
59
+ x *= mask
60
+ return x
61
+
62
+
63
+ class DropoutRowwise(Dropout):
64
+ """
65
+ Convenience class for rowwise dropout as described in subsection
66
+ 1.11.6.
67
+ """
68
+
69
+ __init__ = partialmethod(Dropout.__init__, batch_dim=-3)
70
+
71
+
72
+ class DropoutColumnwise(Dropout):
73
+ """
74
+ Convenience class for columnwise dropout as described in subsection
75
+ 1.11.6.
76
+ """
77
+
78
+ __init__ = partialmethod(Dropout.__init__, batch_dim=-2)
models/openfold/embedders.py ADDED
@@ -0,0 +1,984 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from functools import partial
17
+
18
+ import torch
19
+ import torch.nn as nn
20
+ from typing import Tuple, Optional
21
+
22
+ from onescience.utils.openfold import all_atom_multimer
23
+ from onescience.utils.openfold.feats import (
24
+ pseudo_beta_fn,
25
+ dgram_from_positions,
26
+ build_template_angle_feat,
27
+ build_template_pair_feat,
28
+ )
29
+ from .openfold.primitives import Linear, LayerNorm
30
+ from .openfold.template import (
31
+ TemplatePairStack,
32
+ TemplatePointwiseAttention,
33
+ )
34
+ from onescience.utils.openfold import geometry
35
+ from onescience.utils.openfold.tensor_utils import add, one_hot, tensor_tree_map, dict_multimap
36
+
37
+
38
+ class InputEmbedder(nn.Module):
39
+ """
40
+ Embeds a subset of the input features.
41
+
42
+ Implements Algorithms 3 (InputEmbedder) and 4 (relpos).
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ tf_dim: int,
48
+ msa_dim: int,
49
+ c_z: int,
50
+ c_m: int,
51
+ relpos_k: int,
52
+ **kwargs,
53
+ ):
54
+ """
55
+ Args:
56
+ tf_dim:
57
+ Final dimension of the target features
58
+ msa_dim:
59
+ Final dimension of the MSA features
60
+ c_z:
61
+ Pair embedding dimension
62
+ c_m:
63
+ MSA embedding dimension
64
+ relpos_k:
65
+ Window size used in relative positional encoding
66
+ """
67
+ super(InputEmbedder, self).__init__()
68
+
69
+ self.tf_dim = tf_dim
70
+ self.msa_dim = msa_dim
71
+
72
+ self.c_z = c_z
73
+ self.c_m = c_m
74
+
75
+ self.linear_tf_z_i = Linear(tf_dim, c_z)
76
+ self.linear_tf_z_j = Linear(tf_dim, c_z)
77
+ self.linear_tf_m = Linear(tf_dim, c_m)
78
+ self.linear_msa_m = Linear(msa_dim, c_m)
79
+
80
+ # RPE stuff
81
+ self.relpos_k = relpos_k
82
+ self.no_bins = 2 * relpos_k + 1
83
+ self.linear_relpos = Linear(self.no_bins, c_z)
84
+
85
+ def relpos(self, ri: torch.Tensor):
86
+ """
87
+ Computes relative positional encodings
88
+
89
+ Implements Algorithm 4.
90
+
91
+ Args:
92
+ ri:
93
+ "residue_index" features of shape [*, N]
94
+ """
95
+ d = ri[..., None] - ri[..., None, :]
96
+ boundaries = torch.arange(
97
+ start=-self.relpos_k, end=self.relpos_k + 1, device=d.device
98
+ )
99
+ reshaped_bins = boundaries.view(((1,) * len(d.shape)) + (len(boundaries),))
100
+ d = d[..., None] - reshaped_bins
101
+ d = torch.abs(d)
102
+ d = torch.argmin(d, dim=-1)
103
+ d = nn.functional.one_hot(d, num_classes=len(boundaries)).float()
104
+ d = d.to(ri.dtype)
105
+ return self.linear_relpos(d)
106
+
107
+ def forward(
108
+ self,
109
+ tf: torch.Tensor,
110
+ ri: torch.Tensor,
111
+ msa: torch.Tensor,
112
+ inplace_safe: bool = False,
113
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
114
+ """
115
+ Args:
116
+ batch: Dict containing
117
+ "target_feat":
118
+ Features of shape [*, N_res, tf_dim]
119
+ "residue_index":
120
+ Features of shape [*, N_res]
121
+ "msa_feat":
122
+ Features of shape [*, N_clust, N_res, msa_dim]
123
+ Returns:
124
+ msa_emb:
125
+ [*, N_clust, N_res, C_m] MSA embedding
126
+ pair_emb:
127
+ [*, N_res, N_res, C_z] pair embedding
128
+
129
+ """
130
+ # [*, N_res, c_z]
131
+ tf_emb_i = self.linear_tf_z_i(tf)
132
+ tf_emb_j = self.linear_tf_z_j(tf)
133
+
134
+ # [*, N_res, N_res, c_z]
135
+ pair_emb = self.relpos(ri.type(tf_emb_i.dtype))
136
+ pair_emb = add(pair_emb,
137
+ tf_emb_i[..., None, :],
138
+ inplace=inplace_safe
139
+ )
140
+ pair_emb = add(pair_emb,
141
+ tf_emb_j[..., None, :, :],
142
+ inplace=inplace_safe
143
+ )
144
+
145
+ # [*, N_clust, N_res, c_m]
146
+ n_clust = msa.shape[-3]
147
+ tf_m = (
148
+ self.linear_tf_m(tf)
149
+ .unsqueeze(-3)
150
+ .expand(((-1,) * len(tf.shape[:-2]) + (n_clust, -1, -1)))
151
+ )
152
+ msa_emb = self.linear_msa_m(msa) + tf_m
153
+
154
+ return msa_emb, pair_emb
155
+
156
+
157
+ class InputEmbedderMultimer(nn.Module):
158
+ """
159
+ Embeds a subset of the input features.
160
+
161
+ Implements Algorithms 3 (InputEmbedder) and 4 (relpos).
162
+ """
163
+
164
+ def __init__(
165
+ self,
166
+ tf_dim: int,
167
+ msa_dim: int,
168
+ c_z: int,
169
+ c_m: int,
170
+ max_relative_idx: int,
171
+ use_chain_relative: bool,
172
+ max_relative_chain: int,
173
+ **kwargs,
174
+ ):
175
+ """
176
+ Args:
177
+ tf_dim:
178
+ Final dimension of the target features
179
+ msa_dim:
180
+ Final dimension of the MSA features
181
+ c_z:
182
+ Pair embedding dimension
183
+ c_m:
184
+ MSA embedding dimension
185
+ relpos_k:
186
+ Window size used in relative positional encoding
187
+ """
188
+ super(InputEmbedderMultimer, self).__init__()
189
+
190
+ self.tf_dim = tf_dim
191
+ self.msa_dim = msa_dim
192
+
193
+ self.c_z = c_z
194
+ self.c_m = c_m
195
+
196
+ self.linear_tf_z_i = Linear(tf_dim, c_z)
197
+ self.linear_tf_z_j = Linear(tf_dim, c_z)
198
+ self.linear_tf_m = Linear(tf_dim, c_m)
199
+ self.linear_msa_m = Linear(msa_dim, c_m)
200
+
201
+ # RPE stuff
202
+ self.max_relative_idx = max_relative_idx
203
+ self.use_chain_relative = use_chain_relative
204
+ self.max_relative_chain = max_relative_chain
205
+ if(self.use_chain_relative):
206
+ self.no_bins = (
207
+ 2 * max_relative_idx + 2 +
208
+ 1 +
209
+ 2 * max_relative_chain + 2
210
+ )
211
+ else:
212
+ self.no_bins = 2 * max_relative_idx + 1
213
+ self.linear_relpos = Linear(self.no_bins, c_z)
214
+
215
+ def relpos(self, batch):
216
+ pos = batch["residue_index"]
217
+ asym_id = batch["asym_id"]
218
+ asym_id_same = (asym_id[..., None] == asym_id[..., None, :])
219
+ offset = pos[..., None] - pos[..., None, :]
220
+
221
+ clipped_offset = torch.clamp(
222
+ offset + self.max_relative_idx, 0, 2 * self.max_relative_idx
223
+ )
224
+
225
+ rel_feats = []
226
+ if(self.use_chain_relative):
227
+ final_offset = torch.where(
228
+ asym_id_same,
229
+ clipped_offset,
230
+ (2 * self.max_relative_idx + 1) *
231
+ torch.ones_like(clipped_offset)
232
+ )
233
+ boundaries = torch.arange(
234
+ start=0, end=2 * self.max_relative_idx + 2, device=final_offset.device
235
+ )
236
+ rel_pos = one_hot(
237
+ final_offset,
238
+ boundaries,
239
+ )
240
+
241
+ rel_feats.append(rel_pos)
242
+
243
+ entity_id = batch["entity_id"]
244
+ entity_id_same = (entity_id[..., None] == entity_id[..., None, :])
245
+ rel_feats.append(entity_id_same[..., None].to(dtype=rel_pos.dtype))
246
+
247
+ sym_id = batch["sym_id"]
248
+ rel_sym_id = sym_id[..., None] - sym_id[..., None, :]
249
+
250
+ max_rel_chain = self.max_relative_chain
251
+ clipped_rel_chain = torch.clamp(
252
+ rel_sym_id + max_rel_chain,
253
+ 0,
254
+ 2 * max_rel_chain,
255
+ )
256
+
257
+ final_rel_chain = torch.where(
258
+ entity_id_same,
259
+ clipped_rel_chain,
260
+ (2 * max_rel_chain + 1) *
261
+ torch.ones_like(clipped_rel_chain)
262
+ )
263
+
264
+ boundaries = torch.arange(
265
+ start=0, end=2 * max_rel_chain + 2, device=final_rel_chain.device
266
+ )
267
+ rel_chain = one_hot(
268
+ final_rel_chain,
269
+ boundaries,
270
+ )
271
+
272
+ rel_feats.append(rel_chain)
273
+ else:
274
+ boundaries = torch.arange(
275
+ start=0, end=2 * self.max_relative_idx + 1, device=clipped_offset.device
276
+ )
277
+ rel_pos = one_hot(
278
+ clipped_offset, boundaries,
279
+ )
280
+ rel_feats.append(rel_pos)
281
+
282
+ rel_feat = torch.cat(rel_feats, dim=-1).to(
283
+ self.linear_relpos.weight.dtype
284
+ )
285
+
286
+ return self.linear_relpos(rel_feat)
287
+
288
+ def forward(self, batch) -> Tuple[torch.Tensor, torch.Tensor]:
289
+ tf = batch["target_feat"]
290
+ msa = batch["msa_feat"]
291
+
292
+ # [*, N_res, c_z]
293
+ tf_emb_i = self.linear_tf_z_i(tf)
294
+ tf_emb_j = self.linear_tf_z_j(tf)
295
+
296
+ # [*, N_res, N_res, c_z]
297
+ pair_emb = tf_emb_i[..., None, :] + tf_emb_j[..., None, :, :]
298
+ pair_emb = pair_emb + self.relpos(batch)
299
+
300
+ # [*, N_clust, N_res, c_m]
301
+ n_clust = msa.shape[-3]
302
+ tf_m = (
303
+ self.linear_tf_m(tf)
304
+ .unsqueeze(-3)
305
+ .expand(((-1,) * len(tf.shape[:-2]) + (n_clust, -1, -1)))
306
+ )
307
+ msa_emb = self.linear_msa_m(msa) + tf_m
308
+
309
+ return msa_emb, pair_emb
310
+
311
+
312
+ class PreembeddingEmbedder(nn.Module):
313
+ """
314
+ Embeds the sequence pre-embedding passed to the model and the target_feat features.
315
+ """
316
+
317
+ def __init__(
318
+ self,
319
+ tf_dim: int,
320
+ preembedding_dim: int,
321
+ c_z: int,
322
+ c_m: int,
323
+ relpos_k: int,
324
+ **kwargs,
325
+ ):
326
+ """
327
+ Args:
328
+ tf_dim:
329
+ End channel dimension of the incoming target features
330
+ preembedding_dim:
331
+ End channel dimension of the incoming embeddings
332
+ c_z:
333
+ Pair embedding dimension
334
+ c_m:
335
+ Single-Seq embedding dimension
336
+ relpos_k:
337
+ Window size used in relative position encoding
338
+ """
339
+ super(PreembeddingEmbedder, self).__init__()
340
+
341
+ self.tf_dim = tf_dim
342
+ self.preembedding_dim = preembedding_dim
343
+
344
+ self.c_z = c_z
345
+ self.c_m = c_m
346
+
347
+ self.linear_tf_m = Linear(tf_dim, c_m)
348
+ self.linear_preemb_m = Linear(self.preembedding_dim, c_m)
349
+ self.linear_preemb_z_i = Linear(self.preembedding_dim, c_z)
350
+ self.linear_preemb_z_j = Linear(self.preembedding_dim, c_z)
351
+
352
+ # Relative Positional Encoding
353
+ self.relpos_k = relpos_k
354
+ self.no_bins = 2 * relpos_k + 1
355
+ self.linear_relpos = Linear(self.no_bins, c_z)
356
+
357
+ def relpos(self, ri: torch.Tensor):
358
+ """
359
+ Computes relative positional encodings
360
+ Args:
361
+ ri:
362
+ "residue_index" feature of shape [*, N]
363
+ Returns:
364
+ Relative positional encoding of protein using the
365
+ residue_index feature
366
+ """
367
+ d = ri[..., None] - ri[..., None, :]
368
+ boundaries = torch.arange(
369
+ start=-self.relpos_k, end=self.relpos_k + 1, device=d.device
370
+ )
371
+ reshaped_bins = boundaries.view(((1,) * len(d.shape)) + (len(boundaries),))
372
+ d = d[..., None] - reshaped_bins
373
+ d = torch.abs(d)
374
+ d = torch.argmin(d, dim=-1)
375
+ d = nn.functional.one_hot(d, num_classes=len(boundaries)).float()
376
+ d = d.to(ri.dtype)
377
+ return self.linear_relpos(d)
378
+
379
+ def forward(
380
+ self,
381
+ tf: torch.Tensor,
382
+ ri: torch.Tensor,
383
+ preemb: torch.Tensor,
384
+ inplace_safe: bool = False,
385
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
386
+
387
+ tf_m = (
388
+ self.linear_tf_m(tf)
389
+ .unsqueeze(-3)
390
+ )
391
+ preemb_emb = self.linear_preemb_m(preemb[..., None, :, :]) + tf_m
392
+ preemb_emb_i = self.linear_preemb_z_i(preemb)
393
+ preemb_emb_j = self.linear_preemb_z_j(preemb)
394
+
395
+ pair_emb = self.relpos(ri.type(preemb_emb_i.dtype))
396
+ pair_emb = add(pair_emb,
397
+ preemb_emb_i[..., None, :],
398
+ inplace=inplace_safe)
399
+ pair_emb = add(pair_emb,
400
+ preemb_emb_j[..., None, :, :],
401
+ inplace=inplace_safe)
402
+
403
+ return preemb_emb, pair_emb
404
+
405
+
406
+ class RecyclingEmbedder(nn.Module):
407
+ """
408
+ Embeds the output of an iteration of the model for recycling.
409
+
410
+ Implements Algorithm 32.
411
+ """
412
+ def __init__(
413
+ self,
414
+ c_m: int,
415
+ c_z: int,
416
+ min_bin: float,
417
+ max_bin: float,
418
+ no_bins: int,
419
+ inf: float = 1e8,
420
+ **kwargs,
421
+ ):
422
+ """
423
+ Args:
424
+ c_m:
425
+ MSA channel dimension
426
+ c_z:
427
+ Pair embedding channel dimension
428
+ min_bin:
429
+ Smallest distogram bin (Angstroms)
430
+ max_bin:
431
+ Largest distogram bin (Angstroms)
432
+ no_bins:
433
+ Number of distogram bins
434
+ """
435
+ super(RecyclingEmbedder, self).__init__()
436
+
437
+ self.c_m = c_m
438
+ self.c_z = c_z
439
+ self.min_bin = min_bin
440
+ self.max_bin = max_bin
441
+ self.no_bins = no_bins
442
+ self.inf = inf
443
+
444
+ self.linear = Linear(self.no_bins, self.c_z)
445
+ self.layer_norm_m = LayerNorm(self.c_m)
446
+ self.layer_norm_z = LayerNorm(self.c_z)
447
+
448
+ def forward(
449
+ self,
450
+ m: torch.Tensor,
451
+ z: torch.Tensor,
452
+ x: torch.Tensor,
453
+ inplace_safe: bool = False,
454
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
455
+ """
456
+ Args:
457
+ m:
458
+ First row of the MSA embedding. [*, N_res, C_m]
459
+ z:
460
+ [*, N_res, N_res, C_z] pair embedding
461
+ x:
462
+ [*, N_res, 3] predicted C_beta coordinates
463
+ Returns:
464
+ m:
465
+ [*, N_res, C_m] MSA embedding update
466
+ z:
467
+ [*, N_res, N_res, C_z] pair embedding update
468
+ """
469
+ # [*, N, C_m]
470
+ m_update = self.layer_norm_m(m)
471
+ if(inplace_safe):
472
+ m.copy_(m_update)
473
+ m_update = m
474
+
475
+ # [*, N, N, C_z]
476
+ z_update = self.layer_norm_z(z)
477
+ if(inplace_safe):
478
+ z.copy_(z_update)
479
+ z_update = z
480
+
481
+ # This squared method might become problematic in FP16 mode.
482
+ bins = torch.linspace(
483
+ self.min_bin,
484
+ self.max_bin,
485
+ self.no_bins,
486
+ dtype=x.dtype,
487
+ device=x.device,
488
+ requires_grad=False,
489
+ )
490
+ squared_bins = bins ** 2
491
+ upper = torch.cat(
492
+ [squared_bins[1:], squared_bins.new_tensor([self.inf])], dim=-1
493
+ )
494
+ d = torch.sum(
495
+ (x[..., None, :] - x[..., None, :, :]) ** 2, dim=-1, keepdims=True
496
+ )
497
+
498
+ # [*, N, N, no_bins]
499
+ d = ((d > squared_bins) * (d < upper)).type(x.dtype)
500
+
501
+ # [*, N, N, C_z]
502
+ d = self.linear(d)
503
+ z_update = add(z_update, d, inplace_safe)
504
+
505
+ return m_update, z_update
506
+
507
+
508
+ class TemplateSingleEmbedder(nn.Module):
509
+ """
510
+ Embeds the "template_angle_feat" feature.
511
+
512
+ Implements Algorithm 2, line 7.
513
+ """
514
+
515
+ def __init__(
516
+ self,
517
+ c_in: int,
518
+ c_out: int,
519
+ **kwargs,
520
+ ):
521
+ """
522
+ Args:
523
+ c_in:
524
+ Final dimension of "template_angle_feat"
525
+ c_out:
526
+ Output channel dimension
527
+ """
528
+ super(TemplateSingleEmbedder, self).__init__()
529
+
530
+ self.c_out = c_out
531
+ self.c_in = c_in
532
+
533
+ self.linear_1 = Linear(self.c_in, self.c_out, init="relu")
534
+ self.relu = nn.ReLU()
535
+ self.linear_2 = Linear(self.c_out, self.c_out, init="relu")
536
+
537
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
538
+ """
539
+ Args:
540
+ x: [*, N_templ, N_res, c_in] "template_angle_feat" features
541
+ Returns:
542
+ x: [*, N_templ, N_res, C_out] embedding
543
+ """
544
+ x = self.linear_1(x)
545
+ x = self.relu(x)
546
+ x = self.linear_2(x)
547
+
548
+ return x
549
+
550
+
551
+ class TemplatePairEmbedder(nn.Module):
552
+ """
553
+ Embeds "template_pair_feat" features.
554
+
555
+ Implements Algorithm 2, line 9.
556
+ """
557
+
558
+ def __init__(
559
+ self,
560
+ c_in: int,
561
+ c_out: int,
562
+ **kwargs,
563
+ ):
564
+ """
565
+ Args:
566
+ c_in:
567
+
568
+ c_out:
569
+ Output channel dimension
570
+ """
571
+ super(TemplatePairEmbedder, self).__init__()
572
+
573
+ self.c_in = c_in
574
+ self.c_out = c_out
575
+
576
+ # Despite there being no relu nearby, the source uses that initializer
577
+ self.linear = Linear(self.c_in, self.c_out, init="relu")
578
+
579
+ def forward(
580
+ self,
581
+ x: torch.Tensor,
582
+ ) -> torch.Tensor:
583
+ """
584
+ Args:
585
+ x:
586
+ [*, C_in] input tensor
587
+ Returns:
588
+ [*, C_out] output tensor
589
+ """
590
+ x = self.linear(x)
591
+
592
+ return x
593
+
594
+
595
+ class ExtraMSAEmbedder(nn.Module):
596
+ """
597
+ Embeds unclustered MSA sequences.
598
+
599
+ Implements Algorithm 2, line 15
600
+ """
601
+ def __init__(
602
+ self,
603
+ c_in: int,
604
+ c_out: int,
605
+ **kwargs,
606
+ ):
607
+ """
608
+ Args:
609
+ c_in:
610
+ Input channel dimension
611
+ c_out:
612
+ Output channel dimension
613
+ """
614
+ super(ExtraMSAEmbedder, self).__init__()
615
+
616
+ self.c_in = c_in
617
+ self.c_out = c_out
618
+
619
+ self.linear = Linear(self.c_in, self.c_out)
620
+
621
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
622
+ """
623
+ Args:
624
+ x:
625
+ [*, N_extra_seq, N_res, C_in] "extra_msa_feat" features
626
+ Returns:
627
+ [*, N_extra_seq, N_res, C_out] embedding
628
+ """
629
+ x = self.linear(x)
630
+
631
+ return x
632
+
633
+
634
+ class TemplateEmbedder(nn.Module):
635
+ def __init__(self, config):
636
+ super(TemplateEmbedder, self).__init__()
637
+
638
+ self.config = config
639
+ self.template_single_embedder = TemplateSingleEmbedder(
640
+ **config["template_single_embedder"],
641
+ )
642
+ self.template_pair_embedder = TemplatePairEmbedder(
643
+ **config["template_pair_embedder"],
644
+ )
645
+ self.template_pair_stack = TemplatePairStack(
646
+ **config["template_pair_stack"],
647
+ )
648
+ self.template_pointwise_att = TemplatePointwiseAttention(
649
+ **config["template_pointwise_attention"],
650
+ )
651
+
652
+ def forward(
653
+ self,
654
+ batch,
655
+ z,
656
+ pair_mask,
657
+ templ_dim,
658
+ chunk_size,
659
+ _mask_trans=True,
660
+ use_deepspeed_evo_attention=False,
661
+ use_lma=False,
662
+ inplace_safe=False
663
+ ):
664
+ # Embed the templates one at a time (with a poor man's vmap)
665
+ pair_embeds = []
666
+ n = z.shape[-2]
667
+ n_templ = batch["template_aatype"].shape[templ_dim]
668
+
669
+ if (inplace_safe):
670
+ # We'll preallocate the full pair tensor now to avoid manifesting
671
+ # a second copy during the stack later on
672
+ t_pair = z.new_zeros(
673
+ z.shape[:-3] +
674
+ (n_templ, n, n, self.config.template_pair_embedder.c_out)
675
+ )
676
+
677
+ for i in range(n_templ):
678
+ idx = batch["template_aatype"].new_tensor(i)
679
+ single_template_feats = tensor_tree_map(
680
+ lambda t: torch.index_select(t, templ_dim, idx).squeeze(templ_dim),
681
+ batch,
682
+ )
683
+
684
+ # [*, N, N, C_t]
685
+ t = build_template_pair_feat(
686
+ single_template_feats,
687
+ use_unit_vector=self.config.use_unit_vector,
688
+ inf=self.config.inf,
689
+ eps=self.config.eps,
690
+ **self.config.distogram,
691
+ ).to(z.dtype)
692
+ t = self.template_pair_embedder(t)
693
+
694
+ if (inplace_safe):
695
+ t_pair[..., i, :, :, :] = t
696
+ else:
697
+ pair_embeds.append(t)
698
+
699
+ del t
700
+
701
+ if (not inplace_safe):
702
+ t_pair = torch.stack(pair_embeds, dim=templ_dim)
703
+
704
+ del pair_embeds
705
+
706
+ # [*, S_t, N, N, C_z]
707
+ t = self.template_pair_stack(
708
+ t_pair,
709
+ pair_mask.unsqueeze(-3).to(dtype=z.dtype),
710
+ chunk_size=chunk_size,
711
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
712
+ use_lma=use_lma,
713
+ inplace_safe=inplace_safe,
714
+ _mask_trans=_mask_trans,
715
+ )
716
+ del t_pair
717
+
718
+ # [*, N, N, C_z]
719
+ t = self.template_pointwise_att(
720
+ t,
721
+ z,
722
+ template_mask=batch["template_mask"].to(dtype=z.dtype),
723
+ use_lma=use_lma,
724
+ )
725
+
726
+ t_mask = torch.sum(batch["template_mask"], dim=-1) > 0
727
+ # Append singletons
728
+ t_mask = t_mask.reshape(
729
+ *t_mask.shape, *([1] * (len(t.shape) - len(t_mask.shape)))
730
+ )
731
+
732
+ if (inplace_safe):
733
+ t *= t_mask
734
+ else:
735
+ t = t * t_mask
736
+
737
+ ret = {}
738
+
739
+ ret.update({"template_pair_embedding": t})
740
+
741
+ del t
742
+
743
+ if self.config.embed_angles:
744
+ template_angle_feat = build_template_angle_feat(
745
+ batch
746
+ )
747
+
748
+ # [*, S_t, N, C_m]
749
+ a = self.template_single_embedder(template_angle_feat)
750
+
751
+ ret["template_single_embedding"] = a
752
+
753
+ return ret
754
+
755
+
756
+ class TemplatePairEmbedderMultimer(nn.Module):
757
+ def __init__(self,
758
+ c_in: int,
759
+ c_out: int,
760
+ c_dgram: int,
761
+ c_aatype: int,
762
+ ):
763
+ super(TemplatePairEmbedderMultimer, self).__init__()
764
+
765
+ self.dgram_linear = Linear(c_dgram, c_out, init='relu')
766
+ self.aatype_linear_1 = Linear(c_aatype, c_out, init='relu')
767
+ self.aatype_linear_2 = Linear(c_aatype, c_out, init='relu')
768
+ self.query_embedding_layer_norm = LayerNorm(c_in)
769
+ self.query_embedding_linear = Linear(c_in, c_out, init='relu')
770
+
771
+ self.pseudo_beta_mask_linear = Linear(1, c_out, init='relu')
772
+ self.x_linear = Linear(1, c_out, init='relu')
773
+ self.y_linear = Linear(1, c_out, init='relu')
774
+ self.z_linear = Linear(1, c_out, init='relu')
775
+ self.backbone_mask_linear = Linear(1, c_out, init='relu')
776
+
777
+ def forward(self,
778
+ template_dgram: torch.Tensor,
779
+ aatype_one_hot: torch.Tensor,
780
+ query_embedding: torch.Tensor,
781
+ pseudo_beta_mask: torch.Tensor,
782
+ backbone_mask: torch.Tensor,
783
+ multichain_mask_2d: torch.Tensor,
784
+ unit_vector: geometry.Vec3Array,
785
+ ) -> torch.Tensor:
786
+ act = 0.
787
+
788
+ pseudo_beta_mask_2d = (
789
+ pseudo_beta_mask[..., None] * pseudo_beta_mask[..., None, :]
790
+ )
791
+ pseudo_beta_mask_2d *= multichain_mask_2d
792
+ template_dgram *= pseudo_beta_mask_2d[..., None]
793
+ act += self.dgram_linear(template_dgram)
794
+ act += self.pseudo_beta_mask_linear(pseudo_beta_mask_2d[..., None])
795
+
796
+ aatype_one_hot = aatype_one_hot.to(template_dgram.dtype)
797
+ act += self.aatype_linear_1(aatype_one_hot[..., None, :, :])
798
+ act += self.aatype_linear_2(aatype_one_hot[..., None, :])
799
+
800
+ backbone_mask_2d = (
801
+ backbone_mask[..., None] * backbone_mask[..., None, :]
802
+ )
803
+ backbone_mask_2d *= multichain_mask_2d
804
+ x, y, z = [(coord * backbone_mask_2d).to(dtype=query_embedding.dtype) for coord in unit_vector]
805
+ act += self.x_linear(x[..., None])
806
+ act += self.y_linear(y[..., None])
807
+ act += self.z_linear(z[..., None])
808
+
809
+ act += self.backbone_mask_linear(backbone_mask_2d[..., None].to(dtype=query_embedding.dtype))
810
+
811
+ query_embedding = self.query_embedding_layer_norm(query_embedding)
812
+ act += self.query_embedding_linear(query_embedding)
813
+
814
+ return act
815
+
816
+
817
+ class TemplateSingleEmbedderMultimer(nn.Module):
818
+ def __init__(self,
819
+ c_in: int,
820
+ c_out: int,
821
+ ):
822
+ super(TemplateSingleEmbedderMultimer, self).__init__()
823
+ self.template_single_embedder = Linear(c_in, c_out)
824
+ self.template_projector = Linear(c_out, c_out)
825
+
826
+ def forward(self,
827
+ batch,
828
+ atom_pos,
829
+ aatype_one_hot,
830
+ ):
831
+ out = {}
832
+
833
+ dtype = batch["template_all_atom_positions"].dtype
834
+
835
+ template_chi_angles, template_chi_mask = (
836
+ all_atom_multimer.compute_chi_angles(
837
+ atom_pos,
838
+ batch["template_all_atom_mask"],
839
+ batch["template_aatype"],
840
+ )
841
+ )
842
+
843
+ template_features = torch.cat(
844
+ [
845
+ aatype_one_hot,
846
+ torch.sin(template_chi_angles) * template_chi_mask,
847
+ torch.cos(template_chi_angles) * template_chi_mask,
848
+ template_chi_mask,
849
+ ],
850
+ dim=-1,
851
+ ).to(dtype=dtype)
852
+
853
+ template_mask = template_chi_mask[..., 0].to(dtype=dtype)
854
+
855
+ template_activations = self.template_single_embedder(
856
+ template_features
857
+ )
858
+ template_activations = torch.nn.functional.relu(
859
+ template_activations
860
+ )
861
+ template_activations = self.template_projector(
862
+ template_activations,
863
+ )
864
+
865
+ out["template_single_embedding"] = (
866
+ template_activations
867
+ )
868
+ out["template_mask"] = template_mask
869
+
870
+ return out
871
+
872
+
873
+ class TemplateEmbedderMultimer(nn.Module):
874
+ def __init__(self, config):
875
+ super(TemplateEmbedderMultimer, self).__init__()
876
+
877
+ self.config = config
878
+ self.template_pair_embedder = TemplatePairEmbedderMultimer(
879
+ **config["template_pair_embedder"],
880
+ )
881
+ self.template_single_embedder = TemplateSingleEmbedderMultimer(
882
+ **config["template_single_embedder"],
883
+ )
884
+ self.template_pair_stack = TemplatePairStack(
885
+ **config["template_pair_stack"],
886
+ )
887
+
888
+ self.linear_t = Linear(config.c_t, config.c_z)
889
+
890
+ def forward(self,
891
+ batch,
892
+ z,
893
+ padding_mask_2d,
894
+ templ_dim,
895
+ chunk_size,
896
+ multichain_mask_2d,
897
+ _mask_trans=True,
898
+ use_deepspeed_evo_attention=False,
899
+ use_lma=False,
900
+ inplace_safe=False
901
+ ):
902
+ template_embeds = []
903
+ n_templ = batch["template_aatype"].shape[templ_dim]
904
+ for i in range(n_templ):
905
+ idx = batch["template_aatype"].new_tensor(i)
906
+ single_template_feats = tensor_tree_map(
907
+ lambda t: torch.index_select(t, templ_dim, idx),
908
+ batch,
909
+ )
910
+
911
+ single_template_embeds = {}
912
+ act = 0.
913
+
914
+ template_positions, pseudo_beta_mask = pseudo_beta_fn(
915
+ single_template_feats["template_aatype"],
916
+ single_template_feats["template_all_atom_positions"],
917
+ single_template_feats["template_all_atom_mask"])
918
+
919
+ template_dgram = dgram_from_positions(
920
+ template_positions,
921
+ inf=self.config.inf,
922
+ **self.config.distogram,
923
+ )
924
+
925
+ aatype_one_hot = torch.nn.functional.one_hot(
926
+ single_template_feats["template_aatype"], 22,
927
+ )
928
+
929
+ raw_atom_pos = single_template_feats["template_all_atom_positions"]
930
+
931
+ # Vec3Arrays are required to be float32
932
+ atom_pos = geometry.Vec3Array.from_array(raw_atom_pos.to(dtype=torch.float32))
933
+
934
+ rigid, backbone_mask = all_atom_multimer.make_backbone_affine(
935
+ atom_pos,
936
+ single_template_feats["template_all_atom_mask"],
937
+ single_template_feats["template_aatype"],
938
+ )
939
+ points = rigid.translation
940
+ rigid_vec = rigid[..., None].inverse().apply_to_point(points)
941
+ unit_vector = rigid_vec.normalized()
942
+
943
+ pair_act = self.template_pair_embedder(
944
+ template_dgram,
945
+ aatype_one_hot,
946
+ z,
947
+ pseudo_beta_mask,
948
+ backbone_mask,
949
+ multichain_mask_2d,
950
+ unit_vector,
951
+ )
952
+
953
+ single_template_embeds["template_pair_embedding"] = pair_act
954
+ single_template_embeds.update(
955
+ self.template_single_embedder(
956
+ single_template_feats,
957
+ atom_pos,
958
+ aatype_one_hot,
959
+ )
960
+ )
961
+ template_embeds.append(single_template_embeds)
962
+
963
+ template_embeds = dict_multimap(
964
+ partial(torch.cat, dim=templ_dim),
965
+ template_embeds,
966
+ )
967
+
968
+ # [*, S_t, N, N, C_z]
969
+ t = self.template_pair_stack(
970
+ template_embeds["template_pair_embedding"],
971
+ padding_mask_2d.unsqueeze(-3).to(dtype=z.dtype),
972
+ chunk_size=chunk_size,
973
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
974
+ use_lma=use_lma,
975
+ inplace_safe=inplace_safe,
976
+ _mask_trans=_mask_trans,
977
+ )
978
+ # [*, N, N, C_z]
979
+ t = torch.sum(t, dim=-4) / n_templ
980
+ t = torch.nn.functional.relu(t)
981
+ t = self.linear_t(t)
982
+ template_embeds["template_pair_embedding"] = t
983
+
984
+ return template_embeds
models/openfold/evoformer.py ADDED
@@ -0,0 +1,1219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import math
16
+ import sys
17
+ import torch
18
+ import torch.nn as nn
19
+ from typing import Tuple, Sequence, Optional
20
+ from functools import partial
21
+ from abc import ABC, abstractmethod
22
+
23
+ from .openfold.primitives import Linear, LayerNorm
24
+ from .openfold.dropout import DropoutRowwise, DropoutColumnwise
25
+ from .openfold.msa import (
26
+ MSARowAttentionWithPairBias,
27
+ MSAColumnAttention,
28
+ MSAColumnGlobalAttention,
29
+ )
30
+ from .openfold.outer_product_mean import OuterProductMean
31
+ from .openfold.pair_transition import PairTransition
32
+ from .openfold.triangular_attention import (
33
+ TriangleAttention,
34
+ TriangleAttentionStartingNode,
35
+ TriangleAttentionEndingNode,
36
+ )
37
+ from .openfold.triangular_multiplicative_update import (
38
+ TriangleMultiplicationOutgoing,
39
+ TriangleMultiplicationIncoming,
40
+ FusedTriangleMultiplicationIncoming,
41
+ FusedTriangleMultiplicationOutgoing
42
+ )
43
+ from onescience.utils.openfold.checkpointing import checkpoint_blocks, get_checkpoint_fn
44
+ from onescience.utils.openfold.chunk_utils import chunk_layer, ChunkSizeTuner
45
+ from onescience.utils.openfold.tensor_utils import add
46
+
47
+
48
+ class MSATransition(nn.Module):
49
+ """
50
+ Feed-forward network applied to MSA activations after attention.
51
+
52
+ Implements Algorithm 9
53
+ """
54
+ def __init__(self, c_m, n):
55
+ """
56
+ Args:
57
+ c_m:
58
+ MSA channel dimension
59
+ n:
60
+ Factor multiplied to c_m to obtain the hidden channel
61
+ dimension
62
+ """
63
+ super(MSATransition, self).__init__()
64
+
65
+ self.c_m = c_m
66
+ self.n = n
67
+
68
+ self.layer_norm = LayerNorm(self.c_m)
69
+ self.linear_1 = Linear(self.c_m, self.n * self.c_m, init="relu")
70
+ self.relu = nn.ReLU()
71
+ self.linear_2 = Linear(self.n * self.c_m, self.c_m, init="final")
72
+
73
+ def _transition(self, m, mask):
74
+ m = self.layer_norm(m)
75
+ m = self.linear_1(m)
76
+ m = self.relu(m)
77
+ m = self.linear_2(m) * mask
78
+ return m
79
+
80
+ @torch.jit.ignore
81
+ def _chunk(self,
82
+ m: torch.Tensor,
83
+ mask: torch.Tensor,
84
+ chunk_size: int,
85
+ ) -> torch.Tensor:
86
+ return chunk_layer(
87
+ self._transition,
88
+ {"m": m, "mask": mask},
89
+ chunk_size=chunk_size,
90
+ no_batch_dims=len(m.shape[:-2]),
91
+ )
92
+
93
+ def forward(
94
+ self,
95
+ m: torch.Tensor,
96
+ mask: Optional[torch.Tensor] = None,
97
+ chunk_size: Optional[int] = None,
98
+ ) -> torch.Tensor:
99
+ """
100
+ Args:
101
+ m:
102
+ [*, N_seq, N_res, C_m] MSA activation
103
+ mask:
104
+ [*, N_seq, N_res, C_m] MSA mask
105
+ Returns:
106
+ m:
107
+ [*, N_seq, N_res, C_m] MSA activation update
108
+ """
109
+ # DISCREPANCY: DeepMind forgets to apply the MSA mask here.
110
+ if mask is None:
111
+ mask = m.new_ones(m.shape[:-1])
112
+
113
+ mask = mask.unsqueeze(-1)
114
+
115
+ if chunk_size is not None:
116
+ m = self._chunk(m, mask, chunk_size)
117
+ else:
118
+ m = self._transition(m, mask)
119
+
120
+ return m
121
+
122
+
123
+ class PairStack(nn.Module):
124
+ def __init__(
125
+ self,
126
+ c_z: int,
127
+ c_hidden_mul: int,
128
+ c_hidden_pair_att: int,
129
+ no_heads_pair: int,
130
+ transition_n: int,
131
+ pair_dropout: float,
132
+ fuse_projection_weights: bool,
133
+ inf: float,
134
+ eps: float
135
+ ):
136
+ super(PairStack, self).__init__()
137
+
138
+ if fuse_projection_weights:
139
+ self.tri_mul_out = FusedTriangleMultiplicationOutgoing(
140
+ c_z,
141
+ c_hidden_mul,
142
+ )
143
+ self.tri_mul_in = FusedTriangleMultiplicationIncoming(
144
+ c_z,
145
+ c_hidden_mul,
146
+ )
147
+ else:
148
+ self.tri_mul_out = TriangleMultiplicationOutgoing(
149
+ c_z,
150
+ c_hidden_mul,
151
+ )
152
+ self.tri_mul_in = TriangleMultiplicationIncoming(
153
+ c_z,
154
+ c_hidden_mul,
155
+ )
156
+
157
+ self.tri_att_start = TriangleAttention(
158
+ c_z,
159
+ c_hidden_pair_att,
160
+ no_heads_pair,
161
+ inf=inf,
162
+ )
163
+ self.tri_att_end = TriangleAttention(
164
+ c_z,
165
+ c_hidden_pair_att,
166
+ no_heads_pair,
167
+ inf=inf,
168
+ )
169
+
170
+ self.pair_transition = PairTransition(
171
+ c_z,
172
+ transition_n,
173
+ )
174
+
175
+ self.ps_dropout_row_layer = DropoutRowwise(pair_dropout)
176
+
177
+ def forward(self,
178
+ z: torch.Tensor,
179
+ pair_mask: torch.Tensor,
180
+ chunk_size: Optional[int] = None,
181
+ use_deepspeed_evo_attention: bool = False,
182
+ use_lma: bool = False,
183
+ inplace_safe: bool = False,
184
+ _mask_trans: bool = True,
185
+ _attn_chunk_size: Optional[int] = None
186
+ ) -> torch.Tensor:
187
+ # DeepMind doesn't mask these transitions in the source, so _mask_trans
188
+ # should be disabled to better approximate the exact activations of
189
+ # the original.
190
+ pair_trans_mask = pair_mask if _mask_trans else None
191
+
192
+ if (_attn_chunk_size is None):
193
+ _attn_chunk_size = chunk_size
194
+
195
+ tmu_update = self.tri_mul_out(
196
+ z,
197
+ mask=pair_mask,
198
+ inplace_safe=inplace_safe,
199
+ _add_with_inplace=True,
200
+ )
201
+ if (not inplace_safe):
202
+ z = z + self.ps_dropout_row_layer(tmu_update)
203
+ else:
204
+ z = tmu_update
205
+
206
+ del tmu_update
207
+
208
+ tmu_update = self.tri_mul_in(
209
+ z,
210
+ mask=pair_mask,
211
+ inplace_safe=inplace_safe,
212
+ _add_with_inplace=True,
213
+ )
214
+ if (not inplace_safe):
215
+ z = z + self.ps_dropout_row_layer(tmu_update)
216
+ else:
217
+ z = tmu_update
218
+
219
+ del tmu_update
220
+
221
+ z = add(z,
222
+ self.ps_dropout_row_layer(
223
+ self.tri_att_start(
224
+ z,
225
+ mask=pair_mask,
226
+ chunk_size=_attn_chunk_size,
227
+ use_memory_efficient_kernel=False,
228
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
229
+ use_lma=use_lma,
230
+ inplace_safe=inplace_safe,
231
+ )
232
+ ),
233
+ inplace=inplace_safe,
234
+ )
235
+
236
+ z = z.transpose(-2, -3)
237
+ if (inplace_safe):
238
+ z = z.contiguous()
239
+
240
+ z = add(z,
241
+ self.ps_dropout_row_layer(
242
+ self.tri_att_end(
243
+ z,
244
+ mask=pair_mask.transpose(-1, -2),
245
+ chunk_size=_attn_chunk_size,
246
+ use_memory_efficient_kernel=False,
247
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
248
+ use_lma=use_lma,
249
+ inplace_safe=inplace_safe,
250
+ )
251
+ ),
252
+ inplace=inplace_safe,
253
+ )
254
+
255
+ z = z.transpose(-2, -3)
256
+ if (inplace_safe):
257
+ z = z.contiguous()
258
+
259
+ z = add(z,
260
+ self.pair_transition(
261
+ z, mask=pair_trans_mask, chunk_size=chunk_size,
262
+ ),
263
+ inplace=inplace_safe,
264
+ )
265
+
266
+ return z
267
+
268
+
269
+ class MSABlock(nn.Module, ABC):
270
+ @abstractmethod
271
+ def __init__(self,
272
+ c_m: int,
273
+ c_z: int,
274
+ c_hidden_msa_att: int,
275
+ c_hidden_opm: int,
276
+ c_hidden_mul: int,
277
+ c_hidden_pair_att: int,
278
+ no_heads_msa: int,
279
+ no_heads_pair: int,
280
+ transition_n: int,
281
+ msa_dropout: float,
282
+ pair_dropout: float,
283
+ opm_first: bool,
284
+ fuse_projection_weights: bool,
285
+ inf: float,
286
+ eps: float,
287
+ ):
288
+ super(MSABlock, self).__init__()
289
+
290
+ self.opm_first = opm_first
291
+
292
+ self.msa_att_row = MSARowAttentionWithPairBias(
293
+ c_m=c_m,
294
+ c_z=c_z,
295
+ c_hidden=c_hidden_msa_att,
296
+ no_heads=no_heads_msa,
297
+ inf=inf,
298
+ )
299
+
300
+ self.msa_dropout_layer = DropoutRowwise(msa_dropout)
301
+
302
+ self.msa_transition = MSATransition(
303
+ c_m=c_m,
304
+ n=transition_n,
305
+ )
306
+
307
+ self.outer_product_mean = OuterProductMean(
308
+ c_m,
309
+ c_z,
310
+ c_hidden_opm,
311
+ )
312
+
313
+ self.pair_stack = PairStack(
314
+ c_z=c_z,
315
+ c_hidden_mul=c_hidden_mul,
316
+ c_hidden_pair_att=c_hidden_pair_att,
317
+ no_heads_pair=no_heads_pair,
318
+ transition_n=transition_n,
319
+ pair_dropout=pair_dropout,
320
+ fuse_projection_weights=fuse_projection_weights,
321
+ inf=inf,
322
+ eps=eps
323
+ )
324
+
325
+ def _compute_opm(self,
326
+ input_tensors: Sequence[torch.Tensor],
327
+ msa_mask: torch.Tensor,
328
+ chunk_size: Optional[int] = None,
329
+ inplace_safe: bool = False,
330
+ _offload_inference: bool = False
331
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
332
+
333
+ m, z = input_tensors
334
+
335
+ if (_offload_inference and inplace_safe):
336
+ # m: GPU, z: CPU
337
+ del m, z
338
+ assert (sys.getrefcount(input_tensors[1]) == 2)
339
+ input_tensors[1] = input_tensors[1].cpu()
340
+ m, z = input_tensors
341
+
342
+ opm = self.outer_product_mean(
343
+ m, mask=msa_mask, chunk_size=chunk_size, inplace_safe=inplace_safe
344
+ )
345
+
346
+ if (_offload_inference and inplace_safe):
347
+ # m: GPU, z: GPU
348
+ del m, z
349
+ assert (sys.getrefcount(input_tensors[0]) == 2)
350
+ input_tensors[1] = input_tensors[1].to(opm.device)
351
+ m, z = input_tensors
352
+
353
+ z = add(z, opm, inplace=inplace_safe)
354
+ del opm
355
+
356
+ return m, z
357
+
358
+ @abstractmethod
359
+ def forward(self,
360
+ m: Optional[torch.Tensor],
361
+ z: Optional[torch.Tensor],
362
+ msa_mask: torch.Tensor,
363
+ pair_mask: torch.Tensor,
364
+ chunk_size: Optional[int] = None,
365
+ use_deepspeed_evo_attention: bool = False,
366
+ use_lma: bool = False,
367
+ use_flash: bool = False,
368
+ inplace_safe: bool = False,
369
+ _mask_trans: bool = True,
370
+ _attn_chunk_size: Optional[int] = None,
371
+ _offload_inference: bool = False,
372
+ _offloadable_inputs: Optional[Sequence[torch.Tensor]] = None,
373
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
374
+ pass
375
+
376
+
377
+ class EvoformerBlock(MSABlock):
378
+ def __init__(self,
379
+ c_m: int,
380
+ c_z: int,
381
+ c_hidden_msa_att: int,
382
+ c_hidden_opm: int,
383
+ c_hidden_mul: int,
384
+ c_hidden_pair_att: int,
385
+ no_heads_msa: int,
386
+ no_heads_pair: int,
387
+ transition_n: int,
388
+ msa_dropout: float,
389
+ pair_dropout: float,
390
+ no_column_attention: bool,
391
+ opm_first: bool,
392
+ fuse_projection_weights: bool,
393
+ inf: float,
394
+ eps: float,
395
+ ):
396
+ super(EvoformerBlock, self).__init__(c_m=c_m,
397
+ c_z=c_z,
398
+ c_hidden_msa_att=c_hidden_msa_att,
399
+ c_hidden_opm=c_hidden_opm,
400
+ c_hidden_mul=c_hidden_mul,
401
+ c_hidden_pair_att=c_hidden_pair_att,
402
+ no_heads_msa=no_heads_msa,
403
+ no_heads_pair=no_heads_pair,
404
+ transition_n=transition_n,
405
+ msa_dropout=msa_dropout,
406
+ pair_dropout=pair_dropout,
407
+ opm_first=opm_first,
408
+ fuse_projection_weights=fuse_projection_weights,
409
+ inf=inf,
410
+ eps=eps)
411
+
412
+ # Specifically, seqemb mode does not use column attention
413
+ self.no_column_attention = no_column_attention
414
+
415
+ if not self.no_column_attention:
416
+ self.msa_att_col = MSAColumnAttention(
417
+ c_m,
418
+ c_hidden_msa_att,
419
+ no_heads_msa,
420
+ inf=inf,
421
+ )
422
+
423
+ def forward(self,
424
+ m: Optional[torch.Tensor],
425
+ z: Optional[torch.Tensor],
426
+ msa_mask: torch.Tensor,
427
+ pair_mask: torch.Tensor,
428
+ chunk_size: Optional[int] = None,
429
+ use_deepspeed_evo_attention: bool = False,
430
+ use_lma: bool = False,
431
+ use_flash: bool = False,
432
+ inplace_safe: bool = False,
433
+ _mask_trans: bool = True,
434
+ _attn_chunk_size: Optional[int] = None,
435
+ _offload_inference: bool = False,
436
+ _offloadable_inputs: Optional[Sequence[torch.Tensor]] = None,
437
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
438
+
439
+ msa_trans_mask = msa_mask if _mask_trans else None
440
+
441
+ if(_attn_chunk_size is None):
442
+ _attn_chunk_size = chunk_size
443
+
444
+ if(_offload_inference and inplace_safe):
445
+ input_tensors = _offloadable_inputs
446
+ del _offloadable_inputs
447
+ else:
448
+ input_tensors = [m, z]
449
+
450
+ m, z = input_tensors
451
+
452
+ if self.opm_first:
453
+ del m, z
454
+
455
+ m, z = self._compute_opm(input_tensors=input_tensors,
456
+ msa_mask=msa_mask,
457
+ chunk_size=chunk_size,
458
+ inplace_safe=inplace_safe,
459
+ _offload_inference=_offload_inference)
460
+
461
+ m = add(m,
462
+ self.msa_dropout_layer(
463
+ self.msa_att_row(
464
+ m,
465
+ z=z,
466
+ mask=msa_mask,
467
+ chunk_size=_attn_chunk_size,
468
+ use_memory_efficient_kernel=False,
469
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
470
+ use_lma=use_lma,
471
+ )
472
+ ),
473
+ inplace=inplace_safe,
474
+ )
475
+
476
+ if (_offload_inference and inplace_safe):
477
+ # m: GPU, z: CPU
478
+ del m, z
479
+ assert (sys.getrefcount(input_tensors[1]) == 2)
480
+ input_tensors[1] = input_tensors[1].cpu()
481
+ torch.cuda.empty_cache()
482
+ m, z = input_tensors
483
+
484
+ # Specifically, column attention is not used in seqemb mode.
485
+ if not self.no_column_attention:
486
+ m = add(m,
487
+ self.msa_att_col(
488
+ m,
489
+ mask=msa_mask,
490
+ chunk_size=chunk_size,
491
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
492
+ use_lma=use_lma,
493
+ use_flash=use_flash,
494
+ ),
495
+ inplace=inplace_safe,
496
+ )
497
+
498
+ m = add(
499
+ m,
500
+ self.msa_transition(
501
+ m, mask=msa_trans_mask, chunk_size=chunk_size,
502
+ ),
503
+ inplace=inplace_safe,
504
+ )
505
+
506
+ if not self.opm_first:
507
+ if (not inplace_safe):
508
+ input_tensors = [m, z]
509
+
510
+ del m, z
511
+
512
+ m, z = self._compute_opm(input_tensors=input_tensors,
513
+ msa_mask=msa_mask,
514
+ chunk_size=chunk_size,
515
+ inplace_safe=inplace_safe,
516
+ _offload_inference=_offload_inference)
517
+
518
+ if (_offload_inference and inplace_safe):
519
+ # m: CPU, z: GPU
520
+ del m, z
521
+ assert (sys.getrefcount(input_tensors[0]) == 2)
522
+ device = input_tensors[0].device
523
+ input_tensors[0] = input_tensors[0].cpu()
524
+ input_tensors[1] = input_tensors[1].to(device)
525
+ m, z = input_tensors
526
+
527
+ if (not inplace_safe):
528
+ input_tensors = [m, z]
529
+
530
+ del m, z
531
+
532
+ z = self.pair_stack(
533
+ z=input_tensors[1],
534
+ pair_mask=pair_mask,
535
+ chunk_size=chunk_size,
536
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
537
+ use_lma=use_lma,
538
+ inplace_safe=inplace_safe,
539
+ _mask_trans=_mask_trans,
540
+ _attn_chunk_size=_attn_chunk_size
541
+ )
542
+
543
+ if (_offload_inference and inplace_safe):
544
+ # m: GPU, z: GPU
545
+ device = z.device
546
+ assert (sys.getrefcount(input_tensors[0]) == 2)
547
+ input_tensors[0] = input_tensors[0].to(device)
548
+ m, _ = input_tensors
549
+ else:
550
+ m = input_tensors[0]
551
+
552
+ return m, z
553
+
554
+
555
+ class ExtraMSABlock(MSABlock):
556
+ """
557
+ Almost identical to the standard EvoformerBlock, except in that the
558
+ ExtraMSABlock uses GlobalAttention for MSA column attention and
559
+ requires more fine-grained control over checkpointing. Separated from
560
+ its twin to preserve the TorchScript-ability of the latter.
561
+ """
562
+ def __init__(self,
563
+ c_m: int,
564
+ c_z: int,
565
+ c_hidden_msa_att: int,
566
+ c_hidden_opm: int,
567
+ c_hidden_mul: int,
568
+ c_hidden_pair_att: int,
569
+ no_heads_msa: int,
570
+ no_heads_pair: int,
571
+ transition_n: int,
572
+ msa_dropout: float,
573
+ pair_dropout: float,
574
+ opm_first: bool,
575
+ fuse_projection_weights: bool,
576
+ inf: float,
577
+ eps: float,
578
+ ckpt: bool,
579
+ ):
580
+ super(ExtraMSABlock, self).__init__(c_m=c_m,
581
+ c_z=c_z,
582
+ c_hidden_msa_att=c_hidden_msa_att,
583
+ c_hidden_opm=c_hidden_opm,
584
+ c_hidden_mul=c_hidden_mul,
585
+ c_hidden_pair_att=c_hidden_pair_att,
586
+ no_heads_msa=no_heads_msa,
587
+ no_heads_pair=no_heads_pair,
588
+ transition_n=transition_n,
589
+ msa_dropout=msa_dropout,
590
+ pair_dropout=pair_dropout,
591
+ opm_first=opm_first,
592
+ fuse_projection_weights=fuse_projection_weights,
593
+ inf=inf,
594
+ eps=eps)
595
+
596
+ self.ckpt = ckpt
597
+
598
+ self.msa_att_col = MSAColumnGlobalAttention(
599
+ c_in=c_m,
600
+ c_hidden=c_hidden_msa_att,
601
+ no_heads=no_heads_msa,
602
+ inf=inf,
603
+ eps=eps,
604
+ )
605
+
606
+ def forward(self,
607
+ m: Optional[torch.Tensor],
608
+ z: Optional[torch.Tensor],
609
+ msa_mask: torch.Tensor,
610
+ pair_mask: torch.Tensor,
611
+ chunk_size: Optional[int] = None,
612
+ use_deepspeed_evo_attention: bool = False,
613
+ use_lma: bool = False,
614
+ inplace_safe: bool = False,
615
+ _mask_trans: bool = True,
616
+ _attn_chunk_size: Optional[int] = None,
617
+ _offload_inference: bool = False,
618
+ _offloadable_inputs: Optional[Sequence[torch.Tensor]] = None,
619
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
620
+ if(_attn_chunk_size is None):
621
+ _attn_chunk_size = chunk_size
622
+
623
+ if(_offload_inference and inplace_safe):
624
+ input_tensors = _offloadable_inputs
625
+ del _offloadable_inputs
626
+ else:
627
+ input_tensors = [m, z]
628
+
629
+ m, z = input_tensors
630
+
631
+ if self.opm_first:
632
+ del m, z
633
+
634
+ m, z = self._compute_opm(input_tensors=input_tensors,
635
+ msa_mask=msa_mask,
636
+ chunk_size=chunk_size,
637
+ inplace_safe=inplace_safe,
638
+ _offload_inference=_offload_inference)
639
+
640
+ m = add(m,
641
+ self.msa_dropout_layer(
642
+ self.msa_att_row(
643
+ m.clone() if torch.is_grad_enabled() else m,
644
+ z=z.clone() if torch.is_grad_enabled() else z,
645
+ mask=msa_mask,
646
+ chunk_size=_attn_chunk_size,
647
+ use_lma=use_lma,
648
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
649
+ use_memory_efficient_kernel=not (use_lma or use_deepspeed_evo_attention),
650
+ _checkpoint_chunks=
651
+ self.ckpt if torch.is_grad_enabled() else False,
652
+ )
653
+ ),
654
+ inplace=inplace_safe,
655
+ )
656
+
657
+ if (not inplace_safe):
658
+ input_tensors = [m, z]
659
+
660
+ del m, z
661
+
662
+ def fn(input_tensors):
663
+ m, z = input_tensors
664
+
665
+ if (_offload_inference and inplace_safe):
666
+ # m: GPU, z: CPU
667
+ del m, z
668
+ assert (sys.getrefcount(input_tensors[1]) == 2)
669
+ input_tensors[1] = input_tensors[1].cpu()
670
+ torch.cuda.empty_cache()
671
+ m, z = input_tensors
672
+
673
+ m = add(m,
674
+ self.msa_att_col(
675
+ m,
676
+ mask=msa_mask,
677
+ chunk_size=chunk_size,
678
+ use_lma=use_lma,
679
+ ),
680
+ inplace=inplace_safe,
681
+ )
682
+
683
+ m = add(
684
+ m,
685
+ self.msa_transition(
686
+ m, mask=msa_mask, chunk_size=chunk_size,
687
+ ),
688
+ inplace=inplace_safe,
689
+ )
690
+
691
+ if not self.opm_first:
692
+ if (not inplace_safe):
693
+ input_tensors = [m, z]
694
+
695
+ del m, z
696
+
697
+ m, z = self._compute_opm(input_tensors=input_tensors,
698
+ msa_mask=msa_mask,
699
+ chunk_size=chunk_size,
700
+ inplace_safe=inplace_safe,
701
+ _offload_inference=_offload_inference)
702
+
703
+ if (_offload_inference and inplace_safe):
704
+ # m: CPU, z: GPU
705
+ del m, z
706
+ assert (sys.getrefcount(input_tensors[0]) == 2)
707
+ device = input_tensors[0].device
708
+ input_tensors[0] = input_tensors[0].cpu()
709
+ input_tensors[1] = input_tensors[1].to(device)
710
+ m, z = input_tensors
711
+
712
+ if (not inplace_safe):
713
+ input_tensors = [m, z]
714
+
715
+ del m, z
716
+
717
+ z = self.pair_stack(
718
+ input_tensors[1],
719
+ pair_mask=pair_mask,
720
+ chunk_size=chunk_size,
721
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
722
+ use_lma=use_lma,
723
+ inplace_safe=inplace_safe,
724
+ _mask_trans=_mask_trans,
725
+ _attn_chunk_size=_attn_chunk_size
726
+ )
727
+
728
+ m = input_tensors[0]
729
+ if (_offload_inference and inplace_safe):
730
+ # m: GPU, z: GPU
731
+ device = z.device
732
+ del m
733
+ assert (sys.getrefcount(input_tensors[0]) == 2)
734
+ input_tensors[0] = input_tensors[0].to(device)
735
+ m, _ = input_tensors
736
+
737
+ return m, z
738
+
739
+ if (torch.is_grad_enabled() and self.ckpt):
740
+ checkpoint_fn = get_checkpoint_fn()
741
+ m, z = checkpoint_fn(fn, input_tensors)
742
+ else:
743
+ m, z = fn(input_tensors)
744
+
745
+ return m, z
746
+
747
+
748
+ class EvoformerStack(nn.Module):
749
+ """
750
+ Main Evoformer trunk.
751
+
752
+ Implements Algorithm 6.
753
+ """
754
+
755
+ def __init__(
756
+ self,
757
+ c_m: int,
758
+ c_z: int,
759
+ c_hidden_msa_att: int,
760
+ c_hidden_opm: int,
761
+ c_hidden_mul: int,
762
+ c_hidden_pair_att: int,
763
+ c_s: int,
764
+ no_heads_msa: int,
765
+ no_heads_pair: int,
766
+ no_blocks: int,
767
+ transition_n: int,
768
+ msa_dropout: float,
769
+ pair_dropout: float,
770
+ no_column_attention: bool,
771
+ opm_first: bool,
772
+ fuse_projection_weights: bool,
773
+ blocks_per_ckpt: int,
774
+ inf: float,
775
+ eps: float,
776
+ clear_cache_between_blocks: bool = False,
777
+ tune_chunk_size: bool = False,
778
+ **kwargs,
779
+ ):
780
+ """
781
+ Args:
782
+ c_m:
783
+ MSA channel dimension
784
+ c_z:
785
+ Pair channel dimension
786
+ c_hidden_msa_att:
787
+ Hidden dimension in MSA attention
788
+ c_hidden_opm:
789
+ Hidden dimension in outer product mean module
790
+ c_hidden_mul:
791
+ Hidden dimension in multiplicative updates
792
+ c_hidden_pair_att:
793
+ Hidden dimension in triangular attention
794
+ c_s:
795
+ Channel dimension of the output "single" embedding
796
+ no_heads_msa:
797
+ Number of heads used for MSA attention
798
+ no_heads_pair:
799
+ Number of heads used for pair attention
800
+ no_blocks:
801
+ Number of Evoformer blocks in the stack
802
+ transition_n:
803
+ Factor by which to multiply c_m to obtain the MSATransition
804
+ hidden dimension
805
+ msa_dropout:
806
+ Dropout rate for MSA activations
807
+ pair_dropout:
808
+ Dropout used for pair activations
809
+ no_column_attention:
810
+ When True, doesn't use column attention. Required for running
811
+ sequence embedding mode
812
+ opm_first:
813
+ When True, Outer Product Mean is performed at the beginning of
814
+ the Evoformer block instead of after the MSA Stack.
815
+ Used in Multimer pipeline.
816
+ fuse_projection_weights:
817
+ When True, uses FusedTriangleMultiplicativeUpdate variant in
818
+ the Pair Stack. Used in Multimer pipeline.
819
+ blocks_per_ckpt:
820
+ Number of Evoformer blocks in each activation checkpoint
821
+ clear_cache_between_blocks:
822
+ Whether to clear CUDA's GPU memory cache between blocks of the
823
+ stack. Slows down each block but can reduce fragmentation
824
+ tune_chunk_size:
825
+ Whether to dynamically tune the module's chunk size
826
+ """
827
+ super(EvoformerStack, self).__init__()
828
+
829
+ self.blocks_per_ckpt = blocks_per_ckpt
830
+ self.clear_cache_between_blocks = clear_cache_between_blocks
831
+
832
+ self.blocks = nn.ModuleList()
833
+
834
+ for _ in range(no_blocks):
835
+ block = EvoformerBlock(
836
+ c_m=c_m,
837
+ c_z=c_z,
838
+ c_hidden_msa_att=c_hidden_msa_att,
839
+ c_hidden_opm=c_hidden_opm,
840
+ c_hidden_mul=c_hidden_mul,
841
+ c_hidden_pair_att=c_hidden_pair_att,
842
+ no_heads_msa=no_heads_msa,
843
+ no_heads_pair=no_heads_pair,
844
+ transition_n=transition_n,
845
+ msa_dropout=msa_dropout,
846
+ pair_dropout=pair_dropout,
847
+ no_column_attention=no_column_attention,
848
+ opm_first=opm_first,
849
+ fuse_projection_weights=fuse_projection_weights,
850
+ inf=inf,
851
+ eps=eps,
852
+ )
853
+ self.blocks.append(block)
854
+
855
+ self.linear = Linear(c_m, c_s)
856
+
857
+ self.tune_chunk_size = tune_chunk_size
858
+ self.chunk_size_tuner = None
859
+ if(tune_chunk_size):
860
+ self.chunk_size_tuner = ChunkSizeTuner()
861
+
862
+ def _prep_blocks(self,
863
+ m: torch.Tensor,
864
+ z: torch.Tensor,
865
+ chunk_size: int,
866
+ use_deepspeed_evo_attention: bool,
867
+ use_lma: bool,
868
+ use_flash: bool,
869
+ msa_mask: Optional[torch.Tensor],
870
+ pair_mask: Optional[torch.Tensor],
871
+ inplace_safe: bool,
872
+ _mask_trans: bool,
873
+ ):
874
+ blocks = [
875
+ partial(
876
+ b,
877
+ msa_mask=msa_mask,
878
+ pair_mask=pair_mask,
879
+ chunk_size=chunk_size,
880
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
881
+ use_lma=use_lma,
882
+ use_flash=use_flash,
883
+ inplace_safe=inplace_safe,
884
+ _mask_trans=_mask_trans,
885
+ )
886
+ for b in self.blocks
887
+ ]
888
+
889
+ if(self.clear_cache_between_blocks):
890
+ def block_with_cache_clear(block, *args, **kwargs):
891
+ torch.cuda.empty_cache()
892
+ return block(*args, **kwargs)
893
+
894
+ blocks = [partial(block_with_cache_clear, b) for b in blocks]
895
+
896
+ if(chunk_size is not None and self.chunk_size_tuner is not None):
897
+ assert(not self.training)
898
+ tuned_chunk_size = self.chunk_size_tuner.tune_chunk_size(
899
+ representative_fn=blocks[0],
900
+ # We don't want to write in-place during chunk tuning runs
901
+ args=(m.clone(), z.clone(),),
902
+ min_chunk_size=chunk_size,
903
+ )
904
+ blocks = [
905
+ partial(b,
906
+ chunk_size=tuned_chunk_size,
907
+ # A temporary measure to address torch's occasional
908
+ # inability to allocate large tensors
909
+ _attn_chunk_size=max(chunk_size, tuned_chunk_size // 4),
910
+ ) for b in blocks
911
+ ]
912
+
913
+ return blocks
914
+
915
+ def _forward_offload(self,
916
+ input_tensors: Sequence[torch.Tensor],
917
+ msa_mask: torch.Tensor,
918
+ pair_mask: torch.Tensor,
919
+ chunk_size: int,
920
+ use_deepspeed_evo_attention: bool = False,
921
+ use_lma: bool = False,
922
+ use_flash: bool = False,
923
+ _mask_trans: bool = True,
924
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
925
+ assert(not (self.training or torch.is_grad_enabled()))
926
+ blocks = self._prep_blocks(
927
+ # We are very careful not to create references to these tensors in
928
+ # this function
929
+ m=input_tensors[0],
930
+ z=input_tensors[1],
931
+ chunk_size=chunk_size,
932
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
933
+ use_lma=use_lma,
934
+ use_flash=use_flash,
935
+ msa_mask=msa_mask,
936
+ pair_mask=pair_mask,
937
+ inplace_safe=True,
938
+ _mask_trans=_mask_trans,
939
+ )
940
+
941
+ for b in blocks:
942
+ m, z = b(
943
+ None,
944
+ None,
945
+ _offload_inference=True,
946
+ _offloadable_inputs=input_tensors,
947
+ )
948
+ input_tensors[0] = m
949
+ input_tensors[1] = z
950
+ del m, z
951
+
952
+ m, z = input_tensors
953
+
954
+ s = self.linear(m[..., 0, :, :])
955
+
956
+ return m, z, s
957
+
958
+ def forward(self,
959
+ m: torch.Tensor,
960
+ z: torch.Tensor,
961
+ msa_mask: torch.Tensor,
962
+ pair_mask: torch.Tensor,
963
+ chunk_size: int,
964
+ use_deepspeed_evo_attention: bool = False,
965
+ use_lma: bool = False,
966
+ use_flash: bool = False,
967
+ inplace_safe: bool = False,
968
+ _mask_trans: bool = True,
969
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
970
+ """
971
+ Args:
972
+ m:
973
+ [*, N_seq, N_res, C_m] MSA embedding
974
+ z:
975
+ [*, N_res, N_res, C_z] pair embedding
976
+ msa_mask:
977
+ [*, N_seq, N_res] MSA mask
978
+ pair_mask:
979
+ [*, N_res, N_res] pair mask
980
+ chunk_size:
981
+ Inference-time subbatch size. Acts as a minimum if
982
+ self.tune_chunk_size is True
983
+ use_deepspeed_evo_attention:
984
+ Whether to use DeepSpeed memory efficient kernel.
985
+ Mutually exclusive with use_lma and use_flash.
986
+ use_lma:
987
+ Whether to use low-memory attention during inference.
988
+ Mutually exclusive with use_flash and use_deepspeed_evo_attention.
989
+ use_flash:
990
+ Whether to use FlashAttention where possible. Mutually
991
+ exclusive with use_lma and use_deepspeed_evo_attention.
992
+ Returns:
993
+ m:
994
+ [*, N_seq, N_res, C_m] MSA embedding
995
+ z:
996
+ [*, N_res, N_res, C_z] pair embedding
997
+ s:
998
+ [*, N_res, C_s] single embedding (or None if extra MSA stack)
999
+ """
1000
+ blocks = self._prep_blocks(
1001
+ m=m,
1002
+ z=z,
1003
+ chunk_size=chunk_size,
1004
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
1005
+ use_lma=use_lma,
1006
+ use_flash=use_flash,
1007
+ msa_mask=msa_mask,
1008
+ pair_mask=pair_mask,
1009
+ inplace_safe=inplace_safe,
1010
+ _mask_trans=_mask_trans,
1011
+ )
1012
+
1013
+ blocks_per_ckpt = self.blocks_per_ckpt
1014
+ if(not torch.is_grad_enabled()):
1015
+ blocks_per_ckpt = None
1016
+
1017
+ m, z = checkpoint_blocks(
1018
+ blocks,
1019
+ args=(m, z),
1020
+ blocks_per_ckpt=blocks_per_ckpt,
1021
+ )
1022
+
1023
+ s = self.linear(m[..., 0, :, :])
1024
+
1025
+ return m, z, s
1026
+
1027
+
1028
+ class ExtraMSAStack(nn.Module):
1029
+ """
1030
+ Implements Algorithm 18.
1031
+ """
1032
+ def __init__(self,
1033
+ c_m: int,
1034
+ c_z: int,
1035
+ c_hidden_msa_att: int,
1036
+ c_hidden_opm: int,
1037
+ c_hidden_mul: int,
1038
+ c_hidden_pair_att: int,
1039
+ no_heads_msa: int,
1040
+ no_heads_pair: int,
1041
+ no_blocks: int,
1042
+ transition_n: int,
1043
+ msa_dropout: float,
1044
+ pair_dropout: float,
1045
+ opm_first: bool,
1046
+ fuse_projection_weights: bool,
1047
+ inf: float,
1048
+ eps: float,
1049
+ ckpt: bool,
1050
+ clear_cache_between_blocks: bool = False,
1051
+ tune_chunk_size: bool = False,
1052
+ **kwargs,
1053
+ ):
1054
+ super(ExtraMSAStack, self).__init__()
1055
+
1056
+ self.ckpt = ckpt
1057
+ self.clear_cache_between_blocks = clear_cache_between_blocks
1058
+ self.blocks = nn.ModuleList()
1059
+ for _ in range(no_blocks):
1060
+ block = ExtraMSABlock(
1061
+ c_m=c_m,
1062
+ c_z=c_z,
1063
+ c_hidden_msa_att=c_hidden_msa_att,
1064
+ c_hidden_opm=c_hidden_opm,
1065
+ c_hidden_mul=c_hidden_mul,
1066
+ c_hidden_pair_att=c_hidden_pair_att,
1067
+ no_heads_msa=no_heads_msa,
1068
+ no_heads_pair=no_heads_pair,
1069
+ transition_n=transition_n,
1070
+ msa_dropout=msa_dropout,
1071
+ pair_dropout=pair_dropout,
1072
+ opm_first=opm_first,
1073
+ fuse_projection_weights=fuse_projection_weights,
1074
+ inf=inf,
1075
+ eps=eps,
1076
+ ckpt=False,
1077
+ )
1078
+ self.blocks.append(block)
1079
+
1080
+ self.tune_chunk_size = tune_chunk_size
1081
+ self.chunk_size_tuner = None
1082
+ if(tune_chunk_size):
1083
+ self.chunk_size_tuner = ChunkSizeTuner()
1084
+
1085
+ def _prep_blocks(self,
1086
+ m: torch.Tensor,
1087
+ z: torch.Tensor,
1088
+ chunk_size: int,
1089
+ use_deepspeed_evo_attention: bool,
1090
+ use_lma: bool,
1091
+ msa_mask: Optional[torch.Tensor],
1092
+ pair_mask: Optional[torch.Tensor],
1093
+ inplace_safe: bool,
1094
+ _mask_trans: bool,
1095
+ ):
1096
+ blocks = [
1097
+ partial(
1098
+ b,
1099
+ msa_mask=msa_mask,
1100
+ pair_mask=pair_mask,
1101
+ chunk_size=chunk_size,
1102
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
1103
+ use_lma=use_lma,
1104
+ inplace_safe=inplace_safe,
1105
+ _mask_trans=_mask_trans,
1106
+ ) for b in self.blocks
1107
+ ]
1108
+
1109
+ def clear_cache(b, *args, **kwargs):
1110
+ torch.cuda.empty_cache()
1111
+ return b(*args, **kwargs)
1112
+
1113
+ if(self.clear_cache_between_blocks):
1114
+ blocks = [partial(clear_cache, b) for b in blocks]
1115
+
1116
+ if(chunk_size is not None and self.chunk_size_tuner is not None):
1117
+ tuned_chunk_size = self.chunk_size_tuner.tune_chunk_size(
1118
+ representative_fn=blocks[0],
1119
+ # Tensors cloned to avoid getting written to in-place
1120
+ # A corollary is that chunk size tuning should be disabled for
1121
+ # large N, when z gets really big
1122
+ args=(m.clone(), z.clone(),),
1123
+ min_chunk_size=chunk_size,
1124
+ )
1125
+ blocks = [
1126
+ partial(b,
1127
+ chunk_size=tuned_chunk_size,
1128
+ # A temporary measure to address torch's occasional
1129
+ # inability to allocate large tensors
1130
+ _attn_chunk_size=max(chunk_size, tuned_chunk_size // 4),
1131
+ ) for b in blocks
1132
+ ]
1133
+
1134
+ return blocks
1135
+
1136
+ def _forward_offload(self,
1137
+ input_tensors: Sequence[torch.Tensor],
1138
+ chunk_size: int,
1139
+ use_deepspeed_evo_attention: bool = False,
1140
+ use_lma: bool = False,
1141
+ msa_mask: Optional[torch.Tensor] = None,
1142
+ pair_mask: Optional[torch.Tensor] = None,
1143
+ _mask_trans: bool = True,
1144
+ ) -> torch.Tensor:
1145
+ assert(not (self.training or torch.is_grad_enabled()))
1146
+ blocks = self._prep_blocks(
1147
+ # We are very careful not to create references to these tensors in
1148
+ # this function
1149
+ m=input_tensors[0],
1150
+ z=input_tensors[1],
1151
+ chunk_size=chunk_size,
1152
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
1153
+ use_lma=use_lma,
1154
+ msa_mask=msa_mask,
1155
+ pair_mask=pair_mask,
1156
+ inplace_safe=True,
1157
+ _mask_trans=_mask_trans,
1158
+ )
1159
+
1160
+ for b in blocks:
1161
+ m, z = b(
1162
+ None,
1163
+ None,
1164
+ _offload_inference=True,
1165
+ _offloadable_inputs=input_tensors,
1166
+ )
1167
+ input_tensors[0] = m
1168
+ input_tensors[1] = z
1169
+ del m, z
1170
+
1171
+ return input_tensors[1]
1172
+
1173
+ def forward(self,
1174
+ m: torch.Tensor,
1175
+ z: torch.Tensor,
1176
+ msa_mask: Optional[torch.Tensor],
1177
+ pair_mask: Optional[torch.Tensor],
1178
+ chunk_size: int,
1179
+ use_deepspeed_evo_attention: bool = False,
1180
+ use_lma: bool = False,
1181
+ inplace_safe: bool = False,
1182
+ _mask_trans: bool = True,
1183
+ ) -> torch.Tensor:
1184
+ """
1185
+ Args:
1186
+ m:
1187
+ [*, N_extra, N_res, C_m] extra MSA embedding
1188
+ z:
1189
+ [*, N_res, N_res, C_z] pair embedding
1190
+ chunk_size: Inference-time subbatch size for Evoformer modules
1191
+ use_deepspeed_evo_attention: Whether to use DeepSpeed memory-efficient kernel
1192
+ use_lma: Whether to use low-memory attention during inference
1193
+ msa_mask:
1194
+ Optional [*, N_extra, N_res] MSA mask
1195
+ pair_mask:
1196
+ Optional [*, N_res, N_res] pair mask
1197
+ Returns:
1198
+ [*, N_res, N_res, C_z] pair update
1199
+ """
1200
+ checkpoint_fn = get_checkpoint_fn()
1201
+ blocks = self._prep_blocks(
1202
+ m=m,
1203
+ z=z,
1204
+ chunk_size=chunk_size,
1205
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
1206
+ use_lma=use_lma,
1207
+ msa_mask=msa_mask,
1208
+ pair_mask=pair_mask,
1209
+ inplace_safe=inplace_safe,
1210
+ _mask_trans=_mask_trans,
1211
+ )
1212
+
1213
+ for b in blocks:
1214
+ if(self.ckpt and torch.is_grad_enabled()):
1215
+ m, z = checkpoint_fn(b, m, z)
1216
+ else:
1217
+ m, z = b(m, z)
1218
+
1219
+ return z
models/openfold/heads.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import torch
17
+ import torch.nn as nn
18
+
19
+ from .openfold.primitives import Linear, LayerNorm
20
+ from onescience.utils.openfold.loss import (
21
+ compute_plddt,
22
+ compute_tm,
23
+ compute_predicted_aligned_error,
24
+ )
25
+ from onescience.utils.openfold.precision_utils import is_fp16_enabled
26
+
27
+
28
+ class AuxiliaryHeads(nn.Module):
29
+ def __init__(self, config):
30
+ super(AuxiliaryHeads, self).__init__()
31
+
32
+ self.plddt = PerResidueLDDTCaPredictor(
33
+ **config["lddt"],
34
+ )
35
+
36
+ self.distogram = DistogramHead(
37
+ **config["distogram"],
38
+ )
39
+
40
+ self.masked_msa = MaskedMSAHead(
41
+ **config["masked_msa"],
42
+ )
43
+
44
+ self.experimentally_resolved = ExperimentallyResolvedHead(
45
+ **config["experimentally_resolved"],
46
+ )
47
+
48
+ if config.tm.enabled:
49
+ self.tm = TMScoreHead(
50
+ **config.tm,
51
+ )
52
+
53
+ self.config = config
54
+
55
+ def forward(self, outputs):
56
+ aux_out = {}
57
+ lddt_logits = self.plddt(outputs["sm"]["single"])
58
+ aux_out["lddt_logits"] = lddt_logits
59
+
60
+ # Required for relaxation later on
61
+ aux_out["plddt"] = compute_plddt(lddt_logits)
62
+
63
+ distogram_logits = self.distogram(outputs["pair"])
64
+ aux_out["distogram_logits"] = distogram_logits
65
+
66
+ masked_msa_logits = self.masked_msa(outputs["msa"])
67
+ aux_out["masked_msa_logits"] = masked_msa_logits
68
+
69
+ experimentally_resolved_logits = self.experimentally_resolved(
70
+ outputs["single"]
71
+ )
72
+ aux_out[
73
+ "experimentally_resolved_logits"
74
+ ] = experimentally_resolved_logits
75
+
76
+ if self.config.tm.enabled:
77
+ tm_logits = self.tm(outputs["pair"])
78
+ aux_out["tm_logits"] = tm_logits
79
+ aux_out["ptm_score"] = compute_tm(
80
+ tm_logits, **self.config.tm
81
+ )
82
+ asym_id = outputs.get("asym_id")
83
+ if asym_id is not None:
84
+ aux_out["iptm_score"] = compute_tm(
85
+ tm_logits, asym_id=asym_id, interface=True, **self.config.tm
86
+ )
87
+ aux_out["weighted_ptm_score"] = (self.config.tm["iptm_weight"] * aux_out["iptm_score"]
88
+ + self.config.tm["ptm_weight"] * aux_out["ptm_score"])
89
+
90
+ aux_out.update(
91
+ compute_predicted_aligned_error(
92
+ tm_logits,
93
+ **self.config.tm,
94
+ )
95
+ )
96
+
97
+ return aux_out
98
+
99
+
100
+ class PerResidueLDDTCaPredictor(nn.Module):
101
+ def __init__(self, no_bins, c_in, c_hidden):
102
+ super(PerResidueLDDTCaPredictor, self).__init__()
103
+
104
+ self.no_bins = no_bins
105
+ self.c_in = c_in
106
+ self.c_hidden = c_hidden
107
+
108
+ self.layer_norm = LayerNorm(self.c_in)
109
+
110
+ self.linear_1 = Linear(self.c_in, self.c_hidden, init="relu")
111
+ self.linear_2 = Linear(self.c_hidden, self.c_hidden, init="relu")
112
+ self.linear_3 = Linear(self.c_hidden, self.no_bins, init="final")
113
+
114
+ self.relu = nn.ReLU()
115
+
116
+ def forward(self, s):
117
+ s = self.layer_norm(s)
118
+ s = self.linear_1(s)
119
+ s = self.relu(s)
120
+ s = self.linear_2(s)
121
+ s = self.relu(s)
122
+ s = self.linear_3(s)
123
+
124
+ return s
125
+
126
+
127
+ class DistogramHead(nn.Module):
128
+ """
129
+ Computes a distogram probability distribution.
130
+
131
+ For use in computation of distogram loss, subsection 1.9.8
132
+ """
133
+
134
+ def __init__(self, c_z, no_bins, **kwargs):
135
+ """
136
+ Args:
137
+ c_z:
138
+ Input channel dimension
139
+ no_bins:
140
+ Number of distogram bins
141
+ """
142
+ super(DistogramHead, self).__init__()
143
+
144
+ self.c_z = c_z
145
+ self.no_bins = no_bins
146
+
147
+ self.linear = Linear(self.c_z, self.no_bins, init="final")
148
+
149
+ def _forward(self, z): # [*, N, N, C_z]
150
+ """
151
+ Args:
152
+ z:
153
+ [*, N_res, N_res, C_z] pair embedding
154
+ Returns:
155
+ [*, N, N, no_bins] distogram probability distribution
156
+ """
157
+ # [*, N, N, no_bins]
158
+ logits = self.linear(z)
159
+ logits = logits + logits.transpose(-2, -3)
160
+ return logits
161
+
162
+ def forward(self, z):
163
+ if(is_fp16_enabled()):
164
+ with torch.cuda.amp.autocast(enabled=False):
165
+ return self._forward(z.float())
166
+ else:
167
+ return self._forward(z)
168
+
169
+
170
+ class TMScoreHead(nn.Module):
171
+ """
172
+ For use in computation of TM-score, subsection 1.9.7
173
+ """
174
+
175
+ def __init__(self, c_z, no_bins, **kwargs):
176
+ """
177
+ Args:
178
+ c_z:
179
+ Input channel dimension
180
+ no_bins:
181
+ Number of bins
182
+ """
183
+ super(TMScoreHead, self).__init__()
184
+
185
+ self.c_z = c_z
186
+ self.no_bins = no_bins
187
+
188
+ self.linear = Linear(self.c_z, self.no_bins, init="final")
189
+
190
+ def forward(self, z):
191
+ """
192
+ Args:
193
+ z:
194
+ [*, N_res, N_res, C_z] pairwise embedding
195
+ Returns:
196
+ [*, N_res, N_res, no_bins] prediction
197
+ """
198
+ # [*, N, N, no_bins]
199
+ logits = self.linear(z)
200
+ return logits
201
+
202
+
203
+ class MaskedMSAHead(nn.Module):
204
+ """
205
+ For use in computation of masked MSA loss, subsection 1.9.9
206
+ """
207
+
208
+ def __init__(self, c_m, c_out, **kwargs):
209
+ """
210
+ Args:
211
+ c_m:
212
+ MSA channel dimension
213
+ c_out:
214
+ Output channel dimension
215
+ """
216
+ super(MaskedMSAHead, self).__init__()
217
+
218
+ self.c_m = c_m
219
+ self.c_out = c_out
220
+
221
+ self.linear = Linear(self.c_m, self.c_out, init="final")
222
+
223
+ def forward(self, m):
224
+ """
225
+ Args:
226
+ m:
227
+ [*, N_seq, N_res, C_m] MSA embedding
228
+ Returns:
229
+ [*, N_seq, N_res, C_out] reconstruction
230
+ """
231
+ # [*, N_seq, N_res, C_out]
232
+ logits = self.linear(m)
233
+ return logits
234
+
235
+
236
+ class ExperimentallyResolvedHead(nn.Module):
237
+ """
238
+ For use in computation of "experimentally resolved" loss, subsection
239
+ 1.9.10
240
+ """
241
+
242
+ def __init__(self, c_s, c_out, **kwargs):
243
+ """
244
+ Args:
245
+ c_s:
246
+ Input channel dimension
247
+ c_out:
248
+ Number of distogram bins
249
+ """
250
+ super(ExperimentallyResolvedHead, self).__init__()
251
+
252
+ self.c_s = c_s
253
+ self.c_out = c_out
254
+
255
+ self.linear = Linear(self.c_s, self.c_out, init="final")
256
+
257
+ def forward(self, s):
258
+ """
259
+ Args:
260
+ s:
261
+ [*, N_res, C_s] single embedding
262
+ Returns:
263
+ [*, N, C_out] logits
264
+ """
265
+ # [*, N, C_out]
266
+ logits = self.linear(s)
267
+ return logits
models/openfold/model.py ADDED
@@ -0,0 +1,591 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from functools import partial
16
+ import weakref
17
+
18
+ import torch
19
+ import torch.nn as nn
20
+
21
+ from onescience.datapipes.openfold import data_transforms_multimer
22
+ from onescience.utils.openfold.feats import (
23
+ pseudo_beta_fn,
24
+ build_extra_msa_feat,
25
+ dgram_from_positions,
26
+ atom14_to_atom37,
27
+ )
28
+ from onescience.utils.openfold.tensor_utils import masked_mean
29
+ from .openfold.embedders import (
30
+ InputEmbedder,
31
+ InputEmbedderMultimer,
32
+ RecyclingEmbedder,
33
+ TemplateEmbedder,
34
+ TemplateEmbedderMultimer,
35
+ ExtraMSAEmbedder,
36
+ PreembeddingEmbedder,
37
+ )
38
+ from .openfold.evoformer import EvoformerStack, ExtraMSAStack
39
+ from .openfold.heads import AuxiliaryHeads
40
+ from .openfold.structure_module import StructureModule
41
+ from .openfold.template import (
42
+ TemplatePairStack,
43
+ TemplatePointwiseAttention,
44
+ embed_templates_average,
45
+ embed_templates_offload,
46
+ )
47
+ import onescience.utils.openfold.np.residue_constants as residue_constants
48
+ from onescience.utils.openfold.feats import (
49
+ pseudo_beta_fn,
50
+ build_extra_msa_feat,
51
+ build_template_angle_feat,
52
+ build_template_pair_feat,
53
+ atom14_to_atom37,
54
+ )
55
+ from onescience.utils.openfold.loss import (
56
+ compute_plddt,
57
+ )
58
+ from onescience.utils.openfold.tensor_utils import (
59
+ add,
60
+ dict_multimap,
61
+ tensor_tree_map,
62
+ )
63
+
64
+
65
+ class AlphaFold(nn.Module):
66
+ """
67
+ Alphafold 2.
68
+
69
+ Implements Algorithm 2 (but with training).
70
+ """
71
+
72
+ def __init__(self, config):
73
+ """
74
+ Args:
75
+ config:
76
+ A dict-like config object (like the one in config.py)
77
+ """
78
+ super(AlphaFold, self).__init__()
79
+
80
+ self.globals = config.globals
81
+ self.config = config.model
82
+ self.template_config = self.config.template
83
+ self.extra_msa_config = self.config.extra_msa
84
+ self.seqemb_mode = config.globals.seqemb_mode_enabled
85
+
86
+ # Main trunk + structure module
87
+ if self.globals.is_multimer:
88
+ self.input_embedder = InputEmbedderMultimer(
89
+ **self.config["input_embedder"]
90
+ )
91
+ elif self.seqemb_mode:
92
+ # If using seqemb mode, embed the sequence embeddings passed
93
+ # to the model ("preembeddings") instead of embedding the sequence
94
+ self.input_embedder = PreembeddingEmbedder(
95
+ **self.config["preembedding_embedder"],
96
+ )
97
+ else:
98
+ self.input_embedder = InputEmbedder(
99
+ **self.config["input_embedder"],
100
+ )
101
+
102
+ self.recycling_embedder = RecyclingEmbedder(
103
+ **self.config["recycling_embedder"],
104
+ )
105
+
106
+ if self.template_config.enabled:
107
+ if self.globals.is_multimer:
108
+ self.template_embedder = TemplateEmbedderMultimer(
109
+ self.template_config,
110
+ )
111
+ else:
112
+ self.template_embedder = TemplateEmbedder(
113
+ self.template_config,
114
+ )
115
+
116
+ if self.extra_msa_config.enabled:
117
+ self.extra_msa_embedder = ExtraMSAEmbedder(
118
+ **self.extra_msa_config["extra_msa_embedder"],
119
+ )
120
+ self.extra_msa_stack = ExtraMSAStack(
121
+ **self.extra_msa_config["extra_msa_stack"],
122
+ )
123
+
124
+ self.evoformer = EvoformerStack(
125
+ **self.config["evoformer_stack"],
126
+ )
127
+
128
+ self.structure_module = StructureModule(
129
+ is_multimer=self.globals.is_multimer,
130
+ **self.config["structure_module"],
131
+ )
132
+ self.aux_heads = AuxiliaryHeads(
133
+ self.config["heads"],
134
+ )
135
+
136
+ def embed_templates(self, batch, feats, z, pair_mask, templ_dim, inplace_safe):
137
+ if self.globals.is_multimer:
138
+ asym_id = feats["asym_id"]
139
+ multichain_mask_2d = (
140
+ asym_id[..., None] == asym_id[..., None, :]
141
+ )
142
+ template_embeds = self.template_embedder(
143
+ batch,
144
+ z,
145
+ pair_mask.to(dtype=z.dtype),
146
+ templ_dim,
147
+ chunk_size=self.globals.chunk_size,
148
+ multichain_mask_2d=multichain_mask_2d,
149
+ use_deepspeed_evo_attention=self.globals.use_deepspeed_evo_attention,
150
+ use_lma=self.globals.use_lma,
151
+ inplace_safe=inplace_safe,
152
+ _mask_trans=self.config._mask_trans
153
+ )
154
+ feats["template_torsion_angles_mask"] = (
155
+ template_embeds["template_mask"]
156
+ )
157
+ else:
158
+ if self.template_config.offload_templates:
159
+ return embed_templates_offload(self,
160
+ batch, z, pair_mask, templ_dim, inplace_safe=inplace_safe,
161
+ )
162
+ elif self.template_config.average_templates:
163
+ return embed_templates_average(self,
164
+ batch, z, pair_mask, templ_dim, inplace_safe=inplace_safe,
165
+ )
166
+
167
+ template_embeds = self.template_embedder(
168
+ batch,
169
+ z,
170
+ pair_mask.to(dtype=z.dtype),
171
+ templ_dim,
172
+ chunk_size=self.globals.chunk_size,
173
+ use_deepspeed_evo_attention=self.globals.use_deepspeed_evo_attention,
174
+ use_lma=self.globals.use_lma,
175
+ inplace_safe=inplace_safe,
176
+ _mask_trans=self.config._mask_trans
177
+ )
178
+
179
+ return template_embeds
180
+
181
+ def tolerance_reached(self, prev_pos, next_pos, mask, eps=1e-8) -> bool:
182
+ """
183
+ Early stopping criteria based on criteria used in
184
+ AF2Complex: https://www.nature.com/articles/s41467-022-29394-2
185
+ Args:
186
+ prev_pos: Previous atom positions in atom37/14 representation
187
+ next_pos: Current atom positions in atom37/14 representation
188
+ mask: 1-D sequence mask
189
+ eps: Epsilon used in square root calculation
190
+ Returns:
191
+ Whether to stop recycling early based on the desired tolerance.
192
+ """
193
+
194
+ def distances(points):
195
+ """Compute all pairwise distances for a set of points."""
196
+ d = points[..., None, :] - points[..., None, :, :]
197
+ return torch.sqrt(torch.sum(d ** 2, dim=-1))
198
+
199
+ if self.config.recycle_early_stop_tolerance < 0:
200
+ return False
201
+
202
+ ca_idx = residue_constants.atom_order['CA']
203
+ sq_diff = (distances(prev_pos[..., ca_idx, :]) - distances(next_pos[..., ca_idx, :])) ** 2
204
+ mask = mask[..., None] * mask[..., None, :]
205
+ sq_diff = masked_mean(mask=mask, value=sq_diff, dim=list(range(len(mask.shape))))
206
+ diff = torch.sqrt(sq_diff + eps).item()
207
+ return diff <= self.config.recycle_early_stop_tolerance
208
+
209
+ def iteration(self, feats, prevs, _recycle=True):
210
+ # Primary output dictionary
211
+ outputs = {}
212
+
213
+ # This needs to be done manually for DeepSpeed's sake
214
+ dtype = next(self.parameters()).dtype
215
+ for k in feats:
216
+ if feats[k].dtype == torch.float32:
217
+ feats[k] = feats[k].to(dtype=dtype)
218
+
219
+ # Grab some data about the input
220
+ batch_dims = feats["target_feat"].shape[:-2]
221
+ no_batch_dims = len(batch_dims)
222
+ n = feats["target_feat"].shape[-2]
223
+ n_seq = feats["msa_feat"].shape[-3]
224
+ device = feats["target_feat"].device
225
+
226
+ # Controls whether the model uses in-place operations throughout
227
+ # The dual condition accounts for activation checkpoints
228
+ inplace_safe = not (self.training or torch.is_grad_enabled())
229
+
230
+ # Prep some features
231
+ seq_mask = feats["seq_mask"]
232
+ pair_mask = seq_mask[..., None] * seq_mask[..., None, :]
233
+ msa_mask = feats["msa_mask"]
234
+
235
+ if self.globals.is_multimer:
236
+ # Initialize the MSA and pair representations
237
+ # m: [*, S_c, N, C_m]
238
+ # z: [*, N, N, C_z]
239
+ m, z = self.input_embedder(feats)
240
+ elif self.seqemb_mode:
241
+ # Initialize the SingleSeq and pair representations
242
+ # m: [*, 1, N, C_m]
243
+ # z: [*, N, N, C_z]
244
+ m, z = self.input_embedder(
245
+ feats["target_feat"],
246
+ feats["residue_index"],
247
+ feats["seq_embedding"]
248
+ )
249
+ else:
250
+ # Initialize the MSA and pair representations
251
+ # m: [*, S_c, N, C_m]
252
+ # z: [*, N, N, C_z]
253
+ m, z = self.input_embedder(
254
+ feats["target_feat"],
255
+ feats["residue_index"],
256
+ feats["msa_feat"],
257
+ inplace_safe=inplace_safe,
258
+ )
259
+
260
+ # Unpack the recycling embeddings. Removing them from the list allows
261
+ # them to be freed further down in this function, saving memory
262
+ m_1_prev, z_prev, x_prev = reversed([prevs.pop() for _ in range(3)])
263
+
264
+ # Initialize the recycling embeddings, if needs be
265
+ if None in [m_1_prev, z_prev, x_prev]:
266
+ # [*, N, C_m]
267
+ m_1_prev = m.new_zeros(
268
+ (*batch_dims, n, self.config.input_embedder.c_m),
269
+ requires_grad=False,
270
+ )
271
+
272
+ # [*, N, N, C_z]
273
+ z_prev = z.new_zeros(
274
+ (*batch_dims, n, n, self.config.input_embedder.c_z),
275
+ requires_grad=False,
276
+ )
277
+
278
+ # [*, N, 3]
279
+ x_prev = z.new_zeros(
280
+ (*batch_dims, n, residue_constants.atom_type_num, 3),
281
+ requires_grad=False,
282
+ )
283
+
284
+ pseudo_beta_x_prev = pseudo_beta_fn(
285
+ feats["aatype"], x_prev, None
286
+ ).to(dtype=z.dtype)
287
+
288
+ # The recycling embedder is memory-intensive, so we offload first
289
+ if self.globals.offload_inference and inplace_safe:
290
+ m = m.cpu()
291
+ z = z.cpu()
292
+
293
+ # m_1_prev_emb: [*, N, C_m]
294
+ # z_prev_emb: [*, N, N, C_z]
295
+ m_1_prev_emb, z_prev_emb = self.recycling_embedder(
296
+ m_1_prev,
297
+ z_prev,
298
+ pseudo_beta_x_prev,
299
+ inplace_safe=inplace_safe,
300
+ )
301
+
302
+ del pseudo_beta_x_prev
303
+
304
+ if self.globals.offload_inference and inplace_safe:
305
+ m = m.to(m_1_prev_emb.device)
306
+ z = z.to(z_prev.device)
307
+
308
+ # [*, S_c, N, C_m]
309
+ m[..., 0, :, :] += m_1_prev_emb
310
+
311
+ # [*, N, N, C_z]
312
+ z = add(z, z_prev_emb, inplace=inplace_safe)
313
+
314
+ # Deletions like these become significant for inference with large N,
315
+ # where they free unused tensors and remove references to others such
316
+ # that they can be offloaded later
317
+ del m_1_prev, z_prev, m_1_prev_emb, z_prev_emb
318
+
319
+ # Embed the templates + merge with MSA/pair embeddings
320
+ if self.config.template.enabled:
321
+ template_feats = {
322
+ k: v for k, v in feats.items() if k.startswith("template_")
323
+ }
324
+
325
+ template_embeds = self.embed_templates(
326
+ template_feats,
327
+ feats,
328
+ z,
329
+ pair_mask.to(dtype=z.dtype),
330
+ no_batch_dims,
331
+ inplace_safe=inplace_safe,
332
+ )
333
+
334
+ # [*, N, N, C_z]
335
+ z = add(z,
336
+ template_embeds.pop("template_pair_embedding"),
337
+ inplace_safe,
338
+ )
339
+
340
+ if (
341
+ "template_single_embedding" in template_embeds
342
+ ):
343
+ # [*, S = S_c + S_t, N, C_m]
344
+ m = torch.cat(
345
+ [m, template_embeds["template_single_embedding"]],
346
+ dim=-3
347
+ )
348
+
349
+ # [*, S, N]
350
+ if not self.globals.is_multimer:
351
+ torsion_angles_mask = feats["template_torsion_angles_mask"]
352
+ msa_mask = torch.cat(
353
+ [feats["msa_mask"], torsion_angles_mask[..., 2]],
354
+ dim=-2
355
+ )
356
+ else:
357
+ msa_mask = torch.cat(
358
+ [feats["msa_mask"], template_embeds["template_mask"]],
359
+ dim=-2,
360
+ )
361
+
362
+ # Embed extra MSA features + merge with pairwise embeddings
363
+ if self.config.extra_msa.enabled:
364
+ if self.globals.is_multimer:
365
+ extra_msa_fn = data_transforms_multimer.build_extra_msa_feat
366
+ else:
367
+ extra_msa_fn = build_extra_msa_feat
368
+
369
+ # [*, S_e, N, C_e]
370
+ extra_msa_feat = extra_msa_fn(feats).to(dtype=z.dtype)
371
+ a = self.extra_msa_embedder(extra_msa_feat)
372
+
373
+ if self.globals.offload_inference:
374
+ # To allow the extra MSA stack (and later the evoformer) to
375
+ # offload its inputs, we remove all references to them here
376
+ input_tensors = [a, z]
377
+ del a, z
378
+
379
+ # [*, N, N, C_z]
380
+ z = self.extra_msa_stack._forward_offload(
381
+ input_tensors,
382
+ msa_mask=feats["extra_msa_mask"].to(dtype=m.dtype),
383
+ chunk_size=self.globals.chunk_size,
384
+ use_deepspeed_evo_attention=self.globals.use_deepspeed_evo_attention,
385
+ use_lma=self.globals.use_lma,
386
+ pair_mask=pair_mask.to(dtype=m.dtype),
387
+ _mask_trans=self.config._mask_trans,
388
+ )
389
+
390
+ del input_tensors
391
+ else:
392
+ # [*, N, N, C_z]
393
+ z = self.extra_msa_stack(
394
+ a, z,
395
+ msa_mask=feats["extra_msa_mask"].to(dtype=m.dtype),
396
+ chunk_size=self.globals.chunk_size,
397
+ use_deepspeed_evo_attention=self.globals.use_deepspeed_evo_attention,
398
+ use_lma=self.globals.use_lma,
399
+ pair_mask=pair_mask.to(dtype=m.dtype),
400
+ inplace_safe=inplace_safe,
401
+ _mask_trans=self.config._mask_trans,
402
+ )
403
+
404
+ # Run MSA + pair embeddings through the trunk of the network
405
+ # m: [*, S, N, C_m]
406
+ # z: [*, N, N, C_z]
407
+ # s: [*, N, C_s]
408
+ if self.globals.offload_inference:
409
+ input_tensors = [m, z]
410
+ del m, z
411
+ m, z, s = self.evoformer._forward_offload(
412
+ input_tensors,
413
+ msa_mask=msa_mask.to(dtype=input_tensors[0].dtype),
414
+ pair_mask=pair_mask.to(dtype=input_tensors[1].dtype),
415
+ chunk_size=self.globals.chunk_size,
416
+ use_deepspeed_evo_attention=self.globals.use_deepspeed_evo_attention,
417
+ use_lma=self.globals.use_lma,
418
+ _mask_trans=self.config._mask_trans,
419
+ )
420
+
421
+ del input_tensors
422
+ else:
423
+ m, z, s = self.evoformer(
424
+ m,
425
+ z,
426
+ msa_mask=msa_mask.to(dtype=m.dtype),
427
+ pair_mask=pair_mask.to(dtype=z.dtype),
428
+ chunk_size=self.globals.chunk_size,
429
+ use_deepspeed_evo_attention=self.globals.use_deepspeed_evo_attention,
430
+ use_lma=self.globals.use_lma,
431
+ use_flash=self.globals.use_flash,
432
+ inplace_safe=inplace_safe,
433
+ _mask_trans=self.config._mask_trans,
434
+ )
435
+
436
+ outputs["msa"] = m[..., :n_seq, :, :]
437
+ outputs["pair"] = z
438
+ outputs["single"] = s
439
+
440
+ del z
441
+
442
+ # Predict 3D structure
443
+ outputs["sm"] = self.structure_module(
444
+ outputs,
445
+ feats["aatype"],
446
+ mask=feats["seq_mask"].to(dtype=s.dtype),
447
+ inplace_safe=inplace_safe,
448
+ _offload_inference=self.globals.offload_inference,
449
+ )
450
+ outputs["final_atom_positions"] = atom14_to_atom37(
451
+ outputs["sm"]["positions"][-1], feats
452
+ )
453
+ outputs["final_atom_mask"] = feats["atom37_atom_exists"]
454
+ outputs["final_affine_tensor"] = outputs["sm"]["frames"][-1]
455
+
456
+ # Save embeddings for use during the next recycling iteration
457
+
458
+ # [*, N, C_m]
459
+ m_1_prev = m[..., 0, :, :]
460
+
461
+ # [*, N, N, C_z]
462
+ z_prev = outputs["pair"]
463
+
464
+ early_stop = False
465
+ if self.globals.is_multimer:
466
+ early_stop = self.tolerance_reached(x_prev, outputs["final_atom_positions"], seq_mask)
467
+
468
+ del x_prev
469
+
470
+ # [*, N, 3]
471
+ x_prev = outputs["final_atom_positions"]
472
+
473
+ return outputs, m_1_prev, z_prev, x_prev, early_stop
474
+
475
+ def _disable_activation_checkpointing(self):
476
+ self.template_embedder.template_pair_stack.blocks_per_ckpt = None
477
+ self.evoformer.blocks_per_ckpt = None
478
+
479
+ for b in self.extra_msa_stack.blocks:
480
+ b.ckpt = False
481
+
482
+ def _enable_activation_checkpointing(self):
483
+ self.template_embedder.template_pair_stack.blocks_per_ckpt = (
484
+ self.config.template.template_pair_stack.blocks_per_ckpt
485
+ )
486
+ self.evoformer.blocks_per_ckpt = (
487
+ self.config.evoformer_stack.blocks_per_ckpt
488
+ )
489
+
490
+ for b in self.extra_msa_stack.blocks:
491
+ b.ckpt = self.config.extra_msa.extra_msa_stack.ckpt
492
+
493
+ def forward(self, batch):
494
+ """
495
+ Args:
496
+ batch:
497
+ Dictionary of arguments outlined in Algorithm 2. Keys must
498
+ include the official names of the features in the
499
+ supplement subsection 1.2.9.
500
+
501
+ The final dimension of each input must have length equal to
502
+ the number of recycling iterations.
503
+
504
+ Features (without the recycling dimension):
505
+
506
+ "aatype" ([*, N_res]):
507
+ Contrary to the supplement, this tensor of residue
508
+ indices is not one-hot.
509
+ "target_feat" ([*, N_res, C_tf])
510
+ One-hot encoding of the target sequence. C_tf is
511
+ config.model.input_embedder.tf_dim.
512
+ "residue_index" ([*, N_res])
513
+ Tensor whose final dimension consists of
514
+ consecutive indices from 0 to N_res.
515
+ "msa_feat" ([*, N_seq, N_res, C_msa])
516
+ MSA features, constructed as in the supplement.
517
+ C_msa is config.model.input_embedder.msa_dim.
518
+ "seq_mask" ([*, N_res])
519
+ 1-D sequence mask
520
+ "msa_mask" ([*, N_seq, N_res])
521
+ MSA mask
522
+ "pair_mask" ([*, N_res, N_res])
523
+ 2-D pair mask
524
+ "extra_msa_mask" ([*, N_extra, N_res])
525
+ Extra MSA mask
526
+ "template_mask" ([*, N_templ])
527
+ Template mask (on the level of templates, not
528
+ residues)
529
+ "template_aatype" ([*, N_templ, N_res])
530
+ Tensor of template residue indices (indices greater
531
+ than 19 are clamped to 20 (Unknown))
532
+ "template_all_atom_positions"
533
+ ([*, N_templ, N_res, 37, 3])
534
+ Template atom coordinates in atom37 format
535
+ "template_all_atom_mask" ([*, N_templ, N_res, 37])
536
+ Template atom coordinate mask
537
+ "template_pseudo_beta" ([*, N_templ, N_res, 3])
538
+ Positions of template carbon "pseudo-beta" atoms
539
+ (i.e. C_beta for all residues but glycine, for
540
+ for which C_alpha is used instead)
541
+ "template_pseudo_beta_mask" ([*, N_templ, N_res])
542
+ Pseudo-beta mask
543
+ """
544
+ # Initialize recycling embeddings
545
+ m_1_prev, z_prev, x_prev = None, None, None
546
+ prevs = [m_1_prev, z_prev, x_prev]
547
+
548
+ is_grad_enabled = torch.is_grad_enabled()
549
+
550
+ # Main recycling loop
551
+ num_iters = batch["aatype"].shape[-1]
552
+ early_stop = False
553
+ num_recycles = 0
554
+ for cycle_no in range(num_iters):
555
+ # Select the features for the current recycling cycle
556
+ fetch_cur_batch = lambda t: t[..., cycle_no]
557
+ feats = tensor_tree_map(fetch_cur_batch, batch)
558
+
559
+ # Enable grad iff we're training and it's the final recycling layer
560
+ is_final_iter = cycle_no == (num_iters - 1) or early_stop
561
+ with torch.set_grad_enabled(is_grad_enabled and is_final_iter):
562
+ if is_final_iter:
563
+ # Sidestep AMP bug (PyTorch issue #65766)
564
+ if torch.is_autocast_enabled():
565
+ torch.clear_autocast_cache()
566
+
567
+ # Run the next iteration of the model
568
+ outputs, m_1_prev, z_prev, x_prev, early_stop = self.iteration(
569
+ feats,
570
+ prevs,
571
+ _recycle=(num_iters > 1)
572
+ )
573
+
574
+ num_recycles += 1
575
+
576
+ if not is_final_iter:
577
+ del outputs
578
+ prevs = [m_1_prev, z_prev, x_prev]
579
+ del m_1_prev, z_prev, x_prev
580
+ else:
581
+ break
582
+
583
+ outputs["num_recycles"] = torch.tensor(num_recycles, device=feats["aatype"].device)
584
+
585
+ if "asym_id" in batch:
586
+ outputs["asym_id"] = feats["asym_id"]
587
+
588
+ # Run auxiliary heads
589
+ outputs.update(self.aux_heads(outputs))
590
+
591
+ return outputs
models/openfold/msa.py ADDED
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from functools import partial
16
+ import math
17
+ import torch
18
+ import torch.nn as nn
19
+ from typing import Optional, List, Tuple
20
+
21
+ from .openfold.primitives import (
22
+ Linear,
23
+ LayerNorm,
24
+ Attention,
25
+ GlobalAttention,
26
+ _attention_chunked_trainable,
27
+ )
28
+ from onescience.utils.openfold.checkpointing import get_checkpoint_fn
29
+ from onescience.utils.openfold.chunk_utils import chunk_layer
30
+ from onescience.utils.openfold.tensor_utils import (
31
+ permute_final_dims,
32
+ flatten_final_dims,
33
+ )
34
+
35
+
36
+ class MSAAttention(nn.Module):
37
+ def __init__(
38
+ self,
39
+ c_in,
40
+ c_hidden,
41
+ no_heads,
42
+ pair_bias=False,
43
+ c_z=None,
44
+ inf=1e9,
45
+ ):
46
+ """
47
+ Args:
48
+ c_in:
49
+ Input channel dimension
50
+ c_hidden:
51
+ Per-head hidden channel dimension
52
+ no_heads:
53
+ Number of attention heads
54
+ pair_bias:
55
+ Whether to use pair embedding bias
56
+ c_z:
57
+ Pair embedding channel dimension. Ignored unless pair_bias
58
+ is true
59
+ inf:
60
+ A large number to be used in computing the attention mask
61
+ """
62
+ super(MSAAttention, self).__init__()
63
+
64
+ self.c_in = c_in
65
+ self.c_hidden = c_hidden
66
+ self.no_heads = no_heads
67
+ self.pair_bias = pair_bias
68
+ self.c_z = c_z
69
+ self.inf = inf
70
+
71
+ self.layer_norm_m = LayerNorm(self.c_in)
72
+
73
+ self.layer_norm_z = None
74
+ self.linear_z = None
75
+ if self.pair_bias:
76
+ self.layer_norm_z = LayerNorm(self.c_z)
77
+ self.linear_z = Linear(
78
+ self.c_z, self.no_heads, bias=False, init="normal"
79
+ )
80
+
81
+ self.mha = Attention(
82
+ self.c_in,
83
+ self.c_in,
84
+ self.c_in,
85
+ self.c_hidden,
86
+ self.no_heads,
87
+ )
88
+
89
+ @torch.jit.ignore
90
+ def _chunk(self,
91
+ m: torch.Tensor,
92
+ biases: Optional[List[torch.Tensor]],
93
+ chunk_size: int,
94
+ use_memory_efficient_kernel: bool,
95
+ use_deepspeed_evo_attention: bool,
96
+ use_lma: bool,
97
+ use_flash: bool,
98
+ flash_mask: Optional[torch.Tensor],
99
+ ) -> torch.Tensor:
100
+ def fn(m, biases, flash_mask):
101
+ m = self.layer_norm_m(m)
102
+ return self.mha(
103
+ q_x=m,
104
+ kv_x=m,
105
+ biases=biases,
106
+ use_memory_efficient_kernel=use_memory_efficient_kernel,
107
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
108
+ use_lma=use_lma,
109
+ use_flash=use_flash,
110
+ flash_mask=flash_mask,
111
+ )
112
+
113
+ inputs = {"m": m}
114
+ if(biases is not None):
115
+ inputs["biases"] = biases
116
+ else:
117
+ fn = partial(fn, biases=None)
118
+ if(use_flash and flash_mask is not None):
119
+ inputs["flash_mask"] = flash_mask
120
+ else:
121
+ fn = partial(fn, flash_mask=None)
122
+
123
+ return chunk_layer(
124
+ fn,
125
+ inputs,
126
+ chunk_size=chunk_size,
127
+ no_batch_dims=len(m.shape[:-2])
128
+ )
129
+
130
+ def _prep_inputs(self,
131
+ m: torch.Tensor,
132
+ z: Optional[torch.Tensor],
133
+ mask: Optional[torch.Tensor],
134
+ inplace_safe: bool = False,
135
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
136
+ n_seq, n_res = m.shape[-3:-1]
137
+ if mask is None:
138
+ # [*, N_seq, N_res]
139
+ mask = m.new_ones(
140
+ m.shape[:-3] + (n_seq, n_res),
141
+ )
142
+
143
+ # [*, N_seq, 1, 1, N_res]
144
+ mask_bias = (self.inf * (mask - 1))[..., :, None, None, :]
145
+
146
+ if (self.pair_bias and
147
+ z is not None and # For the
148
+ self.layer_norm_z is not None and # benefit of
149
+ self.linear_z is not None # TorchScript
150
+ ):
151
+ chunks = []
152
+
153
+ for i in range(0, z.shape[-3], 256):
154
+ z_chunk = z[..., i: i + 256, :, :]
155
+
156
+ # [*, N_res, N_res, C_z]
157
+ z_chunk = self.layer_norm_z(z_chunk)
158
+
159
+ # [*, N_res, N_res, no_heads]
160
+ z_chunk = self.linear_z(z_chunk)
161
+
162
+ chunks.append(z_chunk)
163
+
164
+ z = torch.cat(chunks, dim=-3)
165
+
166
+ # [*, 1, no_heads, N_res, N_res]
167
+ z = permute_final_dims(z, (2, 0, 1)).unsqueeze(-4)
168
+
169
+ return m, mask_bias, z
170
+
171
+ @torch.jit.ignore
172
+ def _chunked_msa_attn(self,
173
+ m: torch.Tensor,
174
+ z: Optional[torch.Tensor],
175
+ mask: Optional[torch.Tensor],
176
+ chunk_logits: int,
177
+ checkpoint: bool,
178
+ inplace_safe: bool = False
179
+ ) -> torch.Tensor:
180
+ """
181
+ MSA attention with training-time chunking of the softmax computation.
182
+ Saves memory in the extra MSA stack. Probably obviated by our fused
183
+ attention kernel, which is now used by default.
184
+ """
185
+ MSA_DIM = -4
186
+
187
+ def _get_qkv(m, z):
188
+ m, mask_bias, z = self._prep_inputs(
189
+ m, z, mask, inplace_safe=inplace_safe
190
+ )
191
+ m = self.layer_norm_m(m)
192
+ q, k, v = self.mha._prep_qkv(m, m)
193
+ return m, q, k, v, mask_bias, z
194
+
195
+ checkpoint_fn = get_checkpoint_fn()
196
+
197
+ if(torch.is_grad_enabled() and checkpoint):
198
+ m, q, k, v, mask_bias, z = checkpoint_fn(_get_qkv, m, z)
199
+ else:
200
+ m, q, k, v, mask_bias, z = _get_qkv(m, z)
201
+
202
+ o = _attention_chunked_trainable(
203
+ query=q,
204
+ key=k,
205
+ value=v,
206
+ biases=[mask_bias, z],
207
+ chunk_size=chunk_logits,
208
+ chunk_dim=MSA_DIM,
209
+ checkpoint=checkpoint,
210
+ )
211
+
212
+ if(torch.is_grad_enabled() and checkpoint):
213
+ # Storing an additional m here is far from ideal
214
+ m = checkpoint_fn(self.mha._wrap_up, o, m)
215
+ else:
216
+ m = self.mha._wrap_up(o, m)
217
+
218
+ return m
219
+
220
+ def forward(self,
221
+ m: torch.Tensor,
222
+ z: Optional[torch.Tensor] = None,
223
+ mask: Optional[torch.Tensor] = None,
224
+ chunk_size: Optional[int] = None,
225
+ use_memory_efficient_kernel: bool = False,
226
+ use_deepspeed_evo_attention: bool = False,
227
+ use_lma: bool = False,
228
+ use_flash: bool = False,
229
+ inplace_safe: bool = False,
230
+ _chunk_logits: Optional[int] = None,
231
+ _checkpoint_chunks: Optional[bool] = None,
232
+ ) -> torch.Tensor:
233
+ """
234
+ Args:
235
+ m:
236
+ [*, N_seq, N_res, C_m] MSA embedding
237
+ z:
238
+ [*, N_res, N_res, C_z] pair embedding. Required only if
239
+ pair_bias is True
240
+ mask:
241
+ [*, N_seq, N_res] MSA mask
242
+ chunk_size:
243
+ Size of chunks into which the inputs are split along their
244
+ batch dimensions. A low value decreases memory overhead at the
245
+ cost of slower execution. Chunking is not performed by default.
246
+
247
+ """
248
+ if(_chunk_logits is not None):
249
+ return self._chunked_msa_attn(
250
+ m=m, z=z, mask=mask,
251
+ chunk_logits=_chunk_logits,
252
+ checkpoint=_checkpoint_chunks,
253
+ inplace_safe=inplace_safe,
254
+ )
255
+
256
+ if(use_flash):
257
+ assert z is None
258
+ biases = None
259
+ else:
260
+ m, mask_bias, z = self._prep_inputs(
261
+ m, z, mask, inplace_safe=inplace_safe
262
+ )
263
+
264
+ biases = [mask_bias]
265
+ if(z is not None):
266
+ biases.append(z)
267
+
268
+ if chunk_size is not None:
269
+ m = self._chunk(
270
+ m,
271
+ biases,
272
+ chunk_size,
273
+ use_memory_efficient_kernel=use_memory_efficient_kernel,
274
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
275
+ use_lma=use_lma,
276
+ use_flash=use_flash,
277
+ flash_mask=mask,
278
+ )
279
+ else:
280
+ m = self.layer_norm_m(m)
281
+ m = self.mha(
282
+ q_x=m,
283
+ kv_x=m,
284
+ biases=biases,
285
+ use_memory_efficient_kernel=use_memory_efficient_kernel,
286
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
287
+ use_lma=use_lma,
288
+ use_flash=use_flash,
289
+ flash_mask=mask,
290
+ )
291
+
292
+ return m
293
+
294
+
295
+ class MSARowAttentionWithPairBias(MSAAttention):
296
+ """
297
+ Implements Algorithm 7.
298
+ """
299
+
300
+ def __init__(self, c_m, c_z, c_hidden, no_heads, inf=1e9):
301
+ """
302
+ Args:
303
+ c_m:
304
+ Input channel dimension
305
+ c_z:
306
+ Pair embedding channel dimension
307
+ c_hidden:
308
+ Per-head hidden channel dimension
309
+ no_heads:
310
+ Number of attention heads
311
+ inf:
312
+ Large number used to construct attention masks
313
+ """
314
+ super(MSARowAttentionWithPairBias, self).__init__(
315
+ c_m,
316
+ c_hidden,
317
+ no_heads,
318
+ pair_bias=True,
319
+ c_z=c_z,
320
+ inf=inf,
321
+ )
322
+
323
+
324
+ class MSAColumnAttention(nn.Module):
325
+ """
326
+ Implements Algorithm 8.
327
+
328
+ By rights, this should also be a subclass of MSAAttention. Alas,
329
+ most inheritance isn't supported by TorchScript.
330
+ """
331
+
332
+ def __init__(self, c_m, c_hidden, no_heads, inf=1e9):
333
+ """
334
+ Args:
335
+ c_m:
336
+ MSA channel dimension
337
+ c_hidden:
338
+ Per-head hidden channel dimension
339
+ no_heads:
340
+ Number of attention heads
341
+ inf:
342
+ Large number used to construct attention masks
343
+ """
344
+ super(MSAColumnAttention, self).__init__()
345
+
346
+ self.c_m = c_m
347
+ self.c_hidden = c_hidden
348
+ self.no_heads = no_heads
349
+ self.inf = inf
350
+
351
+ self._msa_att = MSAAttention(
352
+ c_in=c_m,
353
+ c_hidden=c_hidden,
354
+ no_heads=no_heads,
355
+ pair_bias=False,
356
+ c_z=None,
357
+ inf=inf,
358
+ )
359
+
360
+ def forward(self,
361
+ m: torch.Tensor,
362
+ mask: Optional[torch.Tensor] = None,
363
+ chunk_size: Optional[int] = None,
364
+ use_deepspeed_evo_attention: bool = False,
365
+ use_lma: bool = False,
366
+ use_flash: bool = False,
367
+ ) -> torch.Tensor:
368
+ """
369
+ Args:
370
+ m:
371
+ [*, N_seq, N_res, C_m] MSA embedding
372
+ mask:
373
+ [*, N_seq, N_res] MSA mask
374
+ chunk_size:
375
+ Size of chunks into which the inputs are split along their
376
+ batch dimensions. A low value decreases memory overhead at the
377
+ cost of slower execution. Chunking is not performed by default.
378
+ """
379
+ # [*, N_res, N_seq, C_in]
380
+ m = m.transpose(-2, -3)
381
+ if mask is not None:
382
+ mask = mask.transpose(-1, -2)
383
+
384
+ m = self._msa_att(
385
+ m,
386
+ mask=mask,
387
+ chunk_size=chunk_size,
388
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
389
+ use_lma=use_lma,
390
+ use_flash=use_flash,
391
+ )
392
+
393
+ # [*, N_seq, N_res, C_in]
394
+ m = m.transpose(-2, -3)
395
+ if mask is not None:
396
+ mask = mask.transpose(-1, -2)
397
+
398
+ return m
399
+
400
+
401
+ class MSAColumnGlobalAttention(nn.Module):
402
+ def __init__(
403
+ self, c_in, c_hidden, no_heads, inf=1e9, eps=1e-10,
404
+ ):
405
+ super(MSAColumnGlobalAttention, self).__init__()
406
+
407
+ self.c_in = c_in
408
+ self.c_hidden = c_hidden
409
+ self.no_heads = no_heads
410
+ self.inf = inf
411
+ self.eps = eps
412
+
413
+ self.layer_norm_m = nn.LayerNorm(c_in)
414
+
415
+ self.global_attention = GlobalAttention(
416
+ c_in=c_in,
417
+ c_hidden=c_hidden,
418
+ no_heads=no_heads,
419
+ inf=inf,
420
+ eps=eps,
421
+ )
422
+
423
+ @torch.jit.ignore
424
+ def _chunk(self,
425
+ m: torch.Tensor,
426
+ mask: torch.Tensor,
427
+ chunk_size: int,
428
+ use_lma: bool = False,
429
+ ) -> torch.Tensor:
430
+ mha_input = {
431
+ "m": m,
432
+ "mask": mask,
433
+ }
434
+
435
+ def fn(m, mask):
436
+ m = self.layer_norm_m(m)
437
+ return self.global_attention(m, mask, use_lma=use_lma)
438
+
439
+ return chunk_layer(
440
+ fn,
441
+ mha_input,
442
+ chunk_size=chunk_size,
443
+ no_batch_dims=len(m.shape[:-2]),
444
+ )
445
+
446
+ def forward(
447
+ self,
448
+ m: torch.Tensor,
449
+ mask: Optional[torch.Tensor] = None,
450
+ chunk_size: Optional[int] = None,
451
+ use_lma: bool = False,
452
+ ) -> torch.Tensor:
453
+ n_seq, n_res, c_in = m.shape[-3:]
454
+
455
+ if mask is None:
456
+ # [*, N_seq, N_res]
457
+ mask = torch.ones(
458
+ m.shape[:-1],
459
+ dtype=m.dtype,
460
+ device=m.device,
461
+ ).detach()
462
+
463
+ # [*, N_res, N_seq, C_in]
464
+ m = m.transpose(-2, -3)
465
+ mask = mask.transpose(-1, -2)
466
+
467
+ if chunk_size is not None:
468
+ m = self._chunk(m, mask, chunk_size, use_lma=use_lma)
469
+ else:
470
+ m = self.layer_norm_m(m)
471
+ m = self.global_attention(m=m, mask=mask, use_lma=use_lma)
472
+
473
+ # [*, N_seq, N_res, C_in]
474
+ m = m.transpose(-2, -3)
475
+
476
+ return m
models/openfold/outer_product_mean.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from functools import partial
17
+ from typing import Optional
18
+
19
+ import torch
20
+ import torch.nn as nn
21
+
22
+ from .openfold.primitives import Linear
23
+ from onescience.utils.openfold.chunk_utils import chunk_layer
24
+ from onescience.utils.openfold.precision_utils import is_fp16_enabled
25
+
26
+
27
+ class OuterProductMean(nn.Module):
28
+ """
29
+ Implements Algorithm 10.
30
+ """
31
+
32
+ def __init__(self, c_m, c_z, c_hidden, eps=1e-3, bias: bool=True):
33
+ """
34
+ Args:
35
+ c_m:
36
+ MSA embedding channel dimension
37
+ c_z:
38
+ Pair embedding channel dimension
39
+ c_hidden:
40
+ Hidden channel dimension
41
+ """
42
+ super(OuterProductMean, self).__init__()
43
+
44
+ self.c_m = c_m
45
+ self.c_z = c_z
46
+ self.c_hidden = c_hidden
47
+ self.eps = eps
48
+
49
+ self.layer_norm = nn.LayerNorm(c_m)
50
+ self.linear_1 = Linear(c_m, c_hidden, bias=bias)
51
+ self.linear_2 = Linear(c_m, c_hidden, bias=bias)
52
+ self.linear_out = Linear(c_hidden ** 2, c_z, init="final")
53
+
54
+ def _opm(self, a, b):
55
+ # [*, N_res, N_res, C, C]
56
+ outer = torch.einsum("...bac,...dae->...bdce", a, b)
57
+
58
+ # [*, N_res, N_res, C * C]
59
+ outer = outer.reshape(outer.shape[:-2] + (-1,))
60
+
61
+ # [*, N_res, N_res, C_z]
62
+ outer = self.linear_out(outer)
63
+
64
+ return outer
65
+
66
+ @torch.jit.ignore
67
+ def _chunk(self,
68
+ a: torch.Tensor,
69
+ b: torch.Tensor,
70
+ chunk_size: int
71
+ ) -> torch.Tensor:
72
+ # Since the "batch dim" in this case is not a true batch dimension
73
+ # (in that the shape of the output depends on it), we need to
74
+ # iterate over it ourselves
75
+ a_reshape = a.reshape((-1,) + a.shape[-3:])
76
+ b_reshape = b.reshape((-1,) + b.shape[-3:])
77
+ out = []
78
+ for a_prime, b_prime in zip(a_reshape, b_reshape):
79
+ outer = chunk_layer(
80
+ partial(self._opm, b=b_prime),
81
+ {"a": a_prime},
82
+ chunk_size=chunk_size,
83
+ no_batch_dims=1,
84
+ )
85
+ out.append(outer)
86
+
87
+ # For some cursed reason making this distinction saves memory
88
+ if(len(out) == 1):
89
+ outer = out[0].unsqueeze(0)
90
+ else:
91
+ outer = torch.stack(out, dim=0)
92
+
93
+ outer = outer.reshape(a.shape[:-3] + outer.shape[1:])
94
+
95
+ return outer
96
+
97
+ def _forward(self,
98
+ m: torch.Tensor,
99
+ mask: Optional[torch.Tensor] = None,
100
+ chunk_size: Optional[int] = None,
101
+ inplace_safe: bool = False,
102
+ ) -> torch.Tensor:
103
+ """
104
+ Args:
105
+ m:
106
+ [*, N_seq, N_res, C_m] MSA embedding
107
+ mask:
108
+ [*, N_seq, N_res] MSA mask
109
+ Returns:
110
+ [*, N_res, N_res, C_z] pair embedding update
111
+ """
112
+ if mask is None:
113
+ mask = m.new_ones(m.shape[:-1])
114
+
115
+ # [*, N_seq, N_res, C_m]
116
+ ln = self.layer_norm(m)
117
+
118
+ # [*, N_seq, N_res, C]
119
+ mask = mask.unsqueeze(-1)
120
+ a = self.linear_1(ln)
121
+ a = a * mask
122
+
123
+ b = self.linear_2(ln)
124
+ b = b * mask
125
+
126
+ del ln
127
+
128
+ a = a.transpose(-2, -3)
129
+ b = b.transpose(-2, -3)
130
+
131
+ if chunk_size is not None:
132
+ outer = self._chunk(a, b, chunk_size)
133
+ else:
134
+ outer = self._opm(a, b)
135
+
136
+ # [*, N_res, N_res, 1]
137
+ norm = torch.einsum("...abc,...adc->...bdc", mask, mask)
138
+ norm = norm + self.eps
139
+
140
+ # [*, N_res, N_res, C_z]
141
+ if(inplace_safe):
142
+ outer /= norm
143
+ else:
144
+ outer = outer / norm
145
+
146
+ return outer
147
+
148
+ def forward(self,
149
+ m: torch.Tensor,
150
+ mask: Optional[torch.Tensor] = None,
151
+ chunk_size: Optional[int] = None,
152
+ inplace_safe: bool = False,
153
+ ) -> torch.Tensor:
154
+ if(is_fp16_enabled()):
155
+ with torch.cuda.amp.autocast(enabled=False):
156
+ return self._forward(m.float(), mask, chunk_size, inplace_safe)
157
+ else:
158
+ return self._forward(m, mask, chunk_size, inplace_safe)
159
+
models/openfold/pair_transition.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from typing import Optional
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+
20
+ from .openfold.primitives import Linear, LayerNorm
21
+ from onescience.utils.openfold.chunk_utils import chunk_layer
22
+
23
+
24
+ class PairTransition(nn.Module):
25
+ """
26
+ Implements Algorithm 15.
27
+ """
28
+
29
+ def __init__(self, c_z, n):
30
+ """
31
+ Args:
32
+ c_z:
33
+ Pair transition channel dimension
34
+ n:
35
+ Factor by which c_z is multiplied to obtain hidden channel
36
+ dimension
37
+ """
38
+ super(PairTransition, self).__init__()
39
+
40
+ self.c_z = c_z
41
+ self.n = n
42
+
43
+ self.layer_norm = LayerNorm(self.c_z)
44
+ self.linear_1 = Linear(self.c_z, self.n * self.c_z, init="relu")
45
+ self.relu = nn.ReLU()
46
+ self.linear_2 = Linear(self.n * self.c_z, c_z, init="final")
47
+
48
+ def _transition(self, z, mask):
49
+ # [*, N_res, N_res, C_z]
50
+ z = self.layer_norm(z)
51
+
52
+ # [*, N_res, N_res, C_hidden]
53
+ z = self.linear_1(z)
54
+ z = self.relu(z)
55
+
56
+ # [*, N_res, N_res, C_z]
57
+ z = self.linear_2(z)
58
+ z = z * mask
59
+
60
+ return z
61
+
62
+ @torch.jit.ignore
63
+ def _chunk(self,
64
+ z: torch.Tensor,
65
+ mask: torch.Tensor,
66
+ chunk_size: int,
67
+ ) -> torch.Tensor:
68
+ return chunk_layer(
69
+ self._transition,
70
+ {"z": z, "mask": mask},
71
+ chunk_size=chunk_size,
72
+ no_batch_dims=len(z.shape[:-2]),
73
+ )
74
+
75
+ def forward(self,
76
+ z: torch.Tensor,
77
+ mask: Optional[torch.Tensor] = None,
78
+ chunk_size: Optional[int] = None,
79
+ ) -> torch.Tensor:
80
+ """
81
+ Args:
82
+ z:
83
+ [*, N_res, N_res, C_z] pair embedding
84
+ Returns:
85
+ [*, N_res, N_res, C_z] pair embedding update
86
+ """
87
+ # DISCREPANCY: DeepMind forgets to apply the mask in this module.
88
+ if mask is None:
89
+ mask = z.new_ones(z.shape[:-1])
90
+
91
+ # [*, N_res, N_res, 1]
92
+ mask = mask.unsqueeze(-1)
93
+
94
+ if chunk_size is not None:
95
+ z = self._chunk(z, mask, chunk_size)
96
+ else:
97
+ z = self._transition(z=z, mask=mask)
98
+
99
+ return z
models/openfold/primitives.py ADDED
@@ -0,0 +1,902 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import importlib
16
+ import math
17
+ from typing import Optional, Callable, List, Tuple
18
+ import numpy as np
19
+
20
+ deepspeed_is_installed = importlib.util.find_spec("deepspeed") is not None
21
+ ds4s_is_installed = deepspeed_is_installed and importlib.util.find_spec("deepspeed.ops.deepspeed4science") is not None
22
+ if deepspeed_is_installed:
23
+ import deepspeed
24
+
25
+ if ds4s_is_installed:
26
+ from deepspeed.ops.deepspeed4science import DS4Sci_EvoformerAttention
27
+
28
+ fa_is_installed = importlib.util.find_spec("flash_attn") is not None
29
+ if fa_is_installed:
30
+ from flash_attn.bert_padding import unpad_input
31
+ from flash_attn.flash_attn_interface import flash_attn_varlen_kvpacked_func
32
+
33
+ import torch
34
+ import torch.nn as nn
35
+ from scipy.stats import truncnorm
36
+
37
+ from onescience.utils.openfold.checkpointing import get_checkpoint_fn
38
+ from onescience.utils.openfold.kernel.attention_core import attention_core
39
+ from onescience.utils.openfold.precision_utils import is_fp16_enabled
40
+ from onescience.utils.openfold.tensor_utils import (
41
+ permute_final_dims,
42
+ flatten_final_dims,
43
+ )
44
+
45
+
46
+ DEFAULT_LMA_Q_CHUNK_SIZE = 1024
47
+ DEFAULT_LMA_KV_CHUNK_SIZE = 4096
48
+
49
+
50
+ def _prod(nums):
51
+ out = 1
52
+ for n in nums:
53
+ out = out * n
54
+ return out
55
+
56
+
57
+ def _calculate_fan(linear_weight_shape, fan="fan_in"):
58
+ fan_out, fan_in = linear_weight_shape
59
+
60
+ if fan == "fan_in":
61
+ f = fan_in
62
+ elif fan == "fan_out":
63
+ f = fan_out
64
+ elif fan == "fan_avg":
65
+ f = (fan_in + fan_out) / 2
66
+ else:
67
+ raise ValueError("Invalid fan option")
68
+
69
+ return f
70
+
71
+
72
+ def trunc_normal_init_(weights, scale=1.0, fan="fan_in"):
73
+ shape = weights.shape
74
+ f = _calculate_fan(shape, fan)
75
+ scale = scale / max(1, f)
76
+ a = -2
77
+ b = 2
78
+ std = math.sqrt(scale) / truncnorm.std(a=a, b=b, loc=0, scale=1)
79
+ size = _prod(shape)
80
+ samples = truncnorm.rvs(a=a, b=b, loc=0, scale=std, size=size)
81
+ samples = np.reshape(samples, shape)
82
+ with torch.no_grad():
83
+ weights.copy_(torch.tensor(samples, device=weights.device))
84
+
85
+
86
+ def lecun_normal_init_(weights):
87
+ trunc_normal_init_(weights, scale=1.0)
88
+
89
+
90
+ def he_normal_init_(weights):
91
+ trunc_normal_init_(weights, scale=2.0)
92
+
93
+
94
+ def glorot_uniform_init_(weights):
95
+ nn.init.xavier_uniform_(weights, gain=1)
96
+
97
+
98
+ def final_init_(weights):
99
+ with torch.no_grad():
100
+ weights.fill_(0.0)
101
+
102
+
103
+ def gating_init_(weights):
104
+ with torch.no_grad():
105
+ weights.fill_(0.0)
106
+
107
+
108
+ def normal_init_(weights):
109
+ torch.nn.init.kaiming_normal_(weights, nonlinearity="linear")
110
+
111
+
112
+ def ipa_point_weights_init_(weights):
113
+ with torch.no_grad():
114
+ softplus_inverse_1 = 0.541324854612918
115
+ weights.fill_(softplus_inverse_1)
116
+
117
+
118
+ class Linear(nn.Linear):
119
+ """
120
+ A Linear layer with built-in nonstandard initializations. Called just
121
+ like torch.nn.Linear.
122
+
123
+ Implements the initializers in 1.11.4, plus some additional ones found
124
+ in the code.
125
+ """
126
+
127
+ def __init__(
128
+ self,
129
+ in_dim: int,
130
+ out_dim: int,
131
+ bias: bool = True,
132
+ init: str = "default",
133
+ init_fn: Optional[Callable[[torch.Tensor, torch.Tensor], None]] = None,
134
+ precision=None
135
+ ):
136
+ """
137
+ Args:
138
+ in_dim:
139
+ The final dimension of inputs to the layer
140
+ out_dim:
141
+ The final dimension of layer outputs
142
+ bias:
143
+ Whether to learn an additive bias. True by default
144
+ init:
145
+ The initializer to use. Choose from:
146
+
147
+ "default": LeCun fan-in truncated normal initialization
148
+ "relu": He initialization w/ truncated normal distribution
149
+ "glorot": Fan-average Glorot uniform initialization
150
+ "gating": Weights=0, Bias=1
151
+ "normal": Normal initialization with std=1/sqrt(fan_in)
152
+ "final": Weights=0, Bias=0
153
+
154
+ Overridden by init_fn if the latter is not None.
155
+ init_fn:
156
+ A custom initializer taking weight and bias as inputs.
157
+ Overrides init if not None.
158
+ """
159
+ super(Linear, self).__init__(in_dim, out_dim, bias=bias)
160
+
161
+ if bias:
162
+ with torch.no_grad():
163
+ self.bias.fill_(0)
164
+
165
+ with torch.no_grad():
166
+ if init_fn is not None:
167
+ init_fn(self.weight, self.bias)
168
+ else:
169
+ if init == "default":
170
+ lecun_normal_init_(self.weight)
171
+ elif init == "relu":
172
+ he_normal_init_(self.weight)
173
+ elif init == "glorot":
174
+ glorot_uniform_init_(self.weight)
175
+ elif init == "gating":
176
+ gating_init_(self.weight)
177
+ if bias:
178
+ self.bias.fill_(1.0)
179
+ elif init == "normal":
180
+ normal_init_(self.weight)
181
+ elif init == "final":
182
+ final_init_(self.weight)
183
+ else:
184
+ raise ValueError("Invalid init string.")
185
+
186
+ self.precision = precision
187
+
188
+ def forward(self, input: torch.Tensor) -> torch.Tensor:
189
+ d = input.dtype
190
+ deepspeed_is_initialized = (
191
+ deepspeed_is_installed and
192
+ deepspeed.comm.comm.is_initialized()
193
+ )
194
+ if self.precision is not None:
195
+ with torch.cuda.amp.autocast(enabled=False):
196
+ bias = self.bias.to(dtype=self.precision) if self.bias is not None else None
197
+ return nn.functional.linear(input.to(dtype=self.precision),
198
+ self.weight.to(dtype=self.precision),
199
+ bias).to(dtype=d)
200
+
201
+ if d is torch.bfloat16 and not deepspeed_is_initialized:
202
+ with torch.cuda.amp.autocast(enabled=False):
203
+ bias = self.bias.to(dtype=d) if self.bias is not None else None
204
+ return nn.functional.linear(input, self.weight.to(dtype=d), bias)
205
+
206
+ return nn.functional.linear(input, self.weight, self.bias)
207
+
208
+
209
+ class LayerNorm(nn.Module):
210
+ def __init__(self, c_in, eps=1e-5):
211
+ super(LayerNorm, self).__init__()
212
+
213
+ self.c_in = (c_in,)
214
+ self.eps = eps
215
+
216
+ self.weight = nn.Parameter(torch.ones(c_in))
217
+ self.bias = nn.Parameter(torch.zeros(c_in))
218
+
219
+ def forward(self, x):
220
+ d = x.dtype
221
+ deepspeed_is_initialized = (
222
+ deepspeed_is_installed and
223
+ deepspeed.comm.comm.is_initialized()
224
+ )
225
+ if d is torch.bfloat16 and not deepspeed_is_initialized:
226
+ with torch.cuda.amp.autocast(enabled=False):
227
+ out = nn.functional.layer_norm(
228
+ x,
229
+ self.c_in,
230
+ self.weight.to(dtype=d),
231
+ self.bias.to(dtype=d),
232
+ self.eps
233
+ )
234
+ else:
235
+ out = nn.functional.layer_norm(
236
+ x,
237
+ self.c_in,
238
+ self.weight,
239
+ self.bias,
240
+ self.eps,
241
+ )
242
+
243
+ return out
244
+ import os
245
+ fastln_is_installed = os.getenv("LAYERNORM_TYPE", None) == "fast_layernorm"
246
+ if fastln_is_installed:
247
+ # LayerNorm is a time bottomneck, so we use a custom implementation.
248
+ from modules.models.protenix.layer_norm.layer_norm import FusedLayerNorm
249
+
250
+ class OpenFoldLayerNorm(nn.Module):
251
+ def __init__(
252
+ self,
253
+ c_in,
254
+ create_scale: bool = True,
255
+ create_offset: bool = True,
256
+ eps=1e-5,
257
+ ):
258
+ super(OpenFoldLayerNorm, self).__init__()
259
+
260
+ self.c_in = (c_in,)
261
+ self.create_scale = create_scale
262
+ self.create_offset = create_offset
263
+ self.eps = eps
264
+
265
+ if self.create_scale:
266
+ self.weight = nn.Parameter(torch.ones(c_in))
267
+ else:
268
+ self.weight = None
269
+ if self.create_offset:
270
+ self.bias = nn.Parameter(torch.zeros(c_in))
271
+ else:
272
+ self.bias = None
273
+
274
+ def forward(self, x):
275
+ d = x.dtype
276
+ deepspeed_is_initialized = (
277
+ deepspeed_is_installed and deepspeed.comm.comm.is_initialized()
278
+ )
279
+ if d is torch.bfloat16 and not deepspeed_is_initialized:
280
+ with torch.cuda.amp.autocast(enabled=False):
281
+ out = nn.functional.layer_norm(
282
+ x,
283
+ self.c_in,
284
+ self.weight.to(dtype=d) if self.weight is not None else None,
285
+ self.bias.to(dtype=d) if self.bias is not None else None,
286
+ self.eps,
287
+ )
288
+ else:
289
+ out = nn.functional.layer_norm(
290
+ x,
291
+ self.c_in,
292
+ self.weight,
293
+ self.bias,
294
+ self.eps,
295
+ )
296
+ return out
297
+
298
+
299
+ # Keep the function name for code simplicity
300
+ def ProtenixLayerNorm(
301
+ c_in,
302
+ create_scale: bool = True,
303
+ create_offset: bool = True,
304
+ eps: float = 1e-5,
305
+ ):
306
+ # if specify "fast_layernorm" and fastln_is_installed, use the FusedLayerNorm,
307
+ # Otherwise, OpenFoldLayerNorm is used!
308
+ if fastln_is_installed:
309
+ # print("use fast layernorm")
310
+ return FusedLayerNorm(
311
+ c_in, create_scale=create_scale, create_offset=create_offset, eps=eps
312
+ )
313
+ # print("use openfold layernorm")
314
+ return OpenFoldLayerNorm(c_in, create_scale, create_offset, eps)
315
+
316
+
317
+ @torch.jit.ignore
318
+ def softmax_no_cast(t: torch.Tensor, dim: int = -1) -> torch.Tensor:
319
+ """
320
+ Softmax, but without automatic casting to fp32 when the input is of
321
+ type bfloat16
322
+ """
323
+ d = t.dtype
324
+ deepspeed_is_initialized = (
325
+ deepspeed_is_installed and
326
+ deepspeed.comm.comm.is_initialized()
327
+ )
328
+ if d is torch.bfloat16 and not deepspeed_is_initialized:
329
+ with torch.cuda.amp.autocast(enabled=False):
330
+ s = torch.nn.functional.softmax(t, dim=dim)
331
+ else:
332
+ s = torch.nn.functional.softmax(t, dim=dim)
333
+
334
+ return s
335
+
336
+
337
+ #@torch.jit.script
338
+ def _attention(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, biases: List[torch.Tensor]) -> torch.Tensor:
339
+ # [*, H, C_hidden, K]
340
+ key = permute_final_dims(key, (1, 0))
341
+
342
+ # [*, H, Q, K]
343
+ a = torch.matmul(query, key)
344
+
345
+ for b in biases:
346
+ a += b
347
+
348
+ a = softmax_no_cast(a, -1)
349
+
350
+ # [*, H, Q, C_hidden]
351
+ a = torch.matmul(a, value)
352
+
353
+ return a
354
+
355
+
356
+ @torch.jit.ignore
357
+ def _attention_chunked_trainable(
358
+ query, key, value, biases, chunk_size, chunk_dim, checkpoint,
359
+ ):
360
+ if checkpoint and len(biases) > 2:
361
+ raise ValueError(
362
+ "Checkpointed version permits only permits two bias terms"
363
+ )
364
+
365
+ def _checkpointable_attention(q, k, v, b1, b2):
366
+ bs = [b for b in [b1, b2] if b is not None]
367
+ a = _attention(q, k, v, bs)
368
+ return a
369
+
370
+ o_chunks = []
371
+ checkpoint_fn = get_checkpoint_fn()
372
+ count = query.shape[chunk_dim]
373
+ for start in range(0, count, chunk_size):
374
+ end = start + chunk_size
375
+ idx = [slice(None)] * len(query.shape)
376
+ idx[chunk_dim] = slice(start, end)
377
+ idx_tup = tuple(idx)
378
+ q_chunk = query[idx_tup]
379
+ k_chunk = key[idx_tup]
380
+ v_chunk = value[idx_tup]
381
+
382
+ def _slice_bias(b):
383
+ idx[chunk_dim] = (
384
+ slice(start, end) if b.shape[chunk_dim] != 1 else slice(None)
385
+ )
386
+ return b[tuple(idx)]
387
+
388
+ if checkpoint:
389
+ bias_1_chunk, bias_2_chunk = [
390
+ _slice_bias(b) if b is not None else None
391
+ for b in (biases + [None, None])[:2]
392
+ ]
393
+
394
+ o_chunk = checkpoint_fn(_checkpointable_attention,
395
+ q_chunk, k_chunk, v_chunk, bias_1_chunk, bias_2_chunk
396
+ )
397
+ else:
398
+ bias_chunks = [
399
+ _slice_bias(b) for b in biases
400
+ ]
401
+
402
+ o_chunk = _attention(q_chunk, k_chunk, v_chunk, bias_chunks)
403
+
404
+ o_chunk = o_chunk.transpose(-2, -3)
405
+ o_chunks.append(o_chunk)
406
+
407
+ o = torch.cat(o_chunks, dim=chunk_dim)
408
+ return o
409
+
410
+
411
+ class Attention(nn.Module):
412
+ """
413
+ Standard multi-head attention using AlphaFold's default layer
414
+ initialization. Allows multiple bias vectors.
415
+ """
416
+ def __init__(
417
+ self,
418
+ c_q: int,
419
+ c_k: int,
420
+ c_v: int,
421
+ c_hidden: int,
422
+ no_heads: int,
423
+ gating: bool = True,
424
+ bias: bool = True
425
+ ):
426
+ """
427
+ Args:
428
+ c_q:
429
+ Input dimension of query data
430
+ c_k:
431
+ Input dimension of key data
432
+ c_v:
433
+ Input dimension of value data
434
+ c_hidden:
435
+ Per-head hidden dimension
436
+ no_heads:
437
+ Number of attention heads
438
+ gating:
439
+ Whether the output should be gated using query data
440
+ """
441
+ super(Attention, self).__init__()
442
+
443
+ self.c_q = c_q
444
+ self.c_k = c_k
445
+ self.c_v = c_v
446
+ self.c_hidden = c_hidden
447
+ self.no_heads = no_heads
448
+ self.gating = gating
449
+
450
+ # DISCREPANCY: c_hidden is not the per-head channel dimension, as
451
+ # stated in the supplement, but the overall channel dimension.
452
+
453
+ self.linear_q = Linear(
454
+ self.c_q, self.c_hidden * self.no_heads, bias=False, init="glorot"
455
+ )
456
+ self.linear_k = Linear(
457
+ self.c_k, self.c_hidden * self.no_heads, bias=False, init="glorot"
458
+ )
459
+ self.linear_v = Linear(
460
+ self.c_v, self.c_hidden * self.no_heads, bias=False, init="glorot"
461
+ )
462
+ self.linear_o = Linear(
463
+ self.c_hidden * self.no_heads, self.c_q, bias=bias, init="final"
464
+ )
465
+
466
+ self.linear_g = None
467
+ if self.gating:
468
+ self.linear_g = Linear(
469
+ self.c_q, self.c_hidden * self.no_heads, bias=bias, init="gating"
470
+ )
471
+
472
+ self.sigmoid = nn.Sigmoid()
473
+
474
+ def _prep_qkv(self,
475
+ q_x: torch.Tensor,
476
+ kv_x: torch.Tensor,
477
+ apply_scale: bool = True
478
+ ) -> Tuple[
479
+ torch.Tensor, torch.Tensor, torch.Tensor
480
+ ]:
481
+ # [*, Q/K/V, H * C_hidden]
482
+ q = self.linear_q(q_x)
483
+ k = self.linear_k(kv_x)
484
+ v = self.linear_v(kv_x)
485
+
486
+ # [*, Q/K, H, C_hidden]
487
+ q = q.view(q.shape[:-1] + (self.no_heads, -1))
488
+ k = k.view(k.shape[:-1] + (self.no_heads, -1))
489
+ v = v.view(v.shape[:-1] + (self.no_heads, -1))
490
+
491
+ # [*, H, Q/K, C_hidden]
492
+ q = q.transpose(-2, -3)
493
+ k = k.transpose(-2, -3)
494
+ v = v.transpose(-2, -3)
495
+
496
+ if apply_scale:
497
+ q /= math.sqrt(self.c_hidden)
498
+
499
+ return q, k, v
500
+
501
+ def _wrap_up(self,
502
+ o: torch.Tensor,
503
+ q_x: torch.Tensor
504
+ ) -> torch.Tensor:
505
+ if self.linear_g is not None:
506
+ g = self.sigmoid(self.linear_g(q_x))
507
+
508
+ # [*, Q, H, C_hidden]
509
+ g = g.view(g.shape[:-1] + (self.no_heads, -1))
510
+ o = o * g
511
+
512
+ # [*, Q, H * C_hidden]
513
+ o = flatten_final_dims(o, 2)
514
+
515
+ # [*, Q, C_q]
516
+ o = self.linear_o(o)
517
+
518
+ return o
519
+
520
+ def forward(
521
+ self,
522
+ q_x: torch.Tensor,
523
+ kv_x: torch.Tensor,
524
+ biases: Optional[List[torch.Tensor]] = None,
525
+ use_memory_efficient_kernel: bool = False,
526
+ use_deepspeed_evo_attention: bool = False,
527
+ use_lma: bool = False,
528
+ lma_q_chunk_size: int = DEFAULT_LMA_Q_CHUNK_SIZE,
529
+ lma_kv_chunk_size: int = DEFAULT_LMA_KV_CHUNK_SIZE,
530
+ use_flash: bool = False,
531
+ flash_mask: Optional[torch.Tensor] = None
532
+ ) -> torch.Tensor:
533
+ """
534
+ Args:
535
+ q_x:
536
+ [*, Q, C_q] query data
537
+ kv_x:
538
+ [*, K, C_k] key data
539
+ biases:
540
+ List of biases that broadcast to [*, H, Q, K]
541
+ use_memory_efficient_kernel:
542
+ Whether to use a custom memory-efficient attention kernel.
543
+ This should be the default choice for most. If none of the
544
+ "use_<...>" flags are True, a stock PyTorch implementation
545
+ is used instead
546
+ use_deepspeed_evo_attention:
547
+ Whether to use DeepSpeed memory-efficient attention kernel.
548
+ If none of the "use_<...>" flags are True, a stock PyTorch
549
+ implementation is used instead
550
+ use_lma:
551
+ Whether to use low-memory attention (Staats & Rabe 2021). If
552
+ none of the "use_<...>" flags are True, a stock PyTorch
553
+ implementation is used instead
554
+ lma_q_chunk_size:
555
+ Query chunk size (for LMA)
556
+ lma_kv_chunk_size:
557
+ Key/Value chunk size (for LMA)
558
+ Returns
559
+ [*, Q, C_q] attention update
560
+ """
561
+ if use_lma and (lma_q_chunk_size is None or lma_kv_chunk_size is None):
562
+ raise ValueError(
563
+ "If use_lma is specified, lma_q_chunk_size and "
564
+ "lma_kv_chunk_size must be provided"
565
+ )
566
+
567
+ if use_flash and biases is not None:
568
+ raise ValueError(
569
+ "use_flash is incompatible with the bias option. For masking, "
570
+ "use flash_mask instead"
571
+ )
572
+
573
+ attn_options = [use_memory_efficient_kernel, use_deepspeed_evo_attention, use_lma, use_flash]
574
+ if sum(attn_options) > 1:
575
+ raise ValueError(
576
+ "Choose at most one alternative attention algorithm"
577
+ )
578
+
579
+ if biases is None:
580
+ biases = []
581
+
582
+ # DeepSpeed attention kernel applies scaling internally
583
+ q, k, v = self._prep_qkv(q_x, kv_x,
584
+ apply_scale=not use_deepspeed_evo_attention)
585
+
586
+ if is_fp16_enabled():
587
+ use_memory_efficient_kernel = False
588
+
589
+ if use_memory_efficient_kernel:
590
+ if len(biases) > 2:
591
+ raise ValueError(
592
+ "If use_memory_efficient_kernel is True, you may only "
593
+ "provide up to two bias terms"
594
+ )
595
+ o = attention_core(q, k, v, *((biases + [None] * 2)[:2]))
596
+ o = o.transpose(-2, -3)
597
+ elif use_deepspeed_evo_attention:
598
+ if len(biases) > 2:
599
+ raise ValueError(
600
+ "If use_deepspeed_evo_attention is True, you may only "
601
+ "provide up to two bias terms"
602
+ )
603
+ o = _deepspeed_evo_attn(q, k, v, biases)
604
+ elif use_lma:
605
+ biases = [
606
+ b.expand(b.shape[:-2] + (q_x.shape[-2],) + (kv_x.shape[-2],))
607
+ for b in biases
608
+ ]
609
+ o = _lma(q, k, v, biases, lma_q_chunk_size, lma_kv_chunk_size)
610
+ o = o.transpose(-2, -3)
611
+ elif use_flash:
612
+ o = _flash_attn(q, k, v, flash_mask)
613
+ else:
614
+ o = _attention(q, k, v, biases)
615
+ o = o.transpose(-2, -3)
616
+
617
+ o = self._wrap_up(o, q_x)
618
+
619
+ return o
620
+
621
+
622
+ class GlobalAttention(nn.Module):
623
+ def __init__(self, c_in, c_hidden, no_heads, inf, eps):
624
+ super(GlobalAttention, self).__init__()
625
+
626
+ self.c_in = c_in
627
+ self.c_hidden = c_hidden
628
+ self.no_heads = no_heads
629
+ self.inf = inf
630
+ self.eps = eps
631
+
632
+ self.linear_q = Linear(
633
+ c_in, c_hidden * no_heads, bias=False, init="glorot"
634
+ )
635
+
636
+ self.linear_k = Linear(
637
+ c_in, c_hidden, bias=False, init="glorot",
638
+ )
639
+ self.linear_v = Linear(
640
+ c_in, c_hidden, bias=False, init="glorot",
641
+ )
642
+ self.linear_g = Linear(c_in, c_hidden * no_heads, init="gating")
643
+ self.linear_o = Linear(c_hidden * no_heads, c_in, init="final")
644
+
645
+ self.sigmoid = nn.Sigmoid()
646
+
647
+ def forward(self,
648
+ m: torch.Tensor,
649
+ mask: torch.Tensor,
650
+ use_lma: bool = False,
651
+ ) -> torch.Tensor:
652
+ # [*, N_res, C_in]
653
+ q = torch.sum(m * mask.unsqueeze(-1), dim=-2) / (
654
+ torch.sum(mask, dim=-1)[..., None] + self.eps
655
+ )
656
+
657
+ # [*, N_res, H * C_hidden]
658
+ q = self.linear_q(q)
659
+ q *= (self.c_hidden ** (-0.5))
660
+
661
+ # [*, N_res, H, C_hidden]
662
+ q = q.view(q.shape[:-1] + (self.no_heads, -1))
663
+
664
+ # [*, N_res, N_seq, C_hidden]
665
+ k = self.linear_k(m)
666
+ v = self.linear_v(m)
667
+
668
+ bias = (self.inf * (mask - 1))[..., :, None, :]
669
+ if not use_lma:
670
+ # [*, N_res, H, N_seq]
671
+ a = torch.matmul(
672
+ q,
673
+ k.transpose(-1, -2), # [*, N_res, C_hidden, N_seq]
674
+ )
675
+ a += bias
676
+ a = softmax_no_cast(a)
677
+
678
+ # [*, N_res, H, C_hidden]
679
+ o = torch.matmul(
680
+ a,
681
+ v,
682
+ )
683
+ else:
684
+ o = _lma(
685
+ q,
686
+ k,
687
+ v,
688
+ [bias],
689
+ DEFAULT_LMA_Q_CHUNK_SIZE,
690
+ DEFAULT_LMA_KV_CHUNK_SIZE
691
+ )
692
+
693
+ # [*, N_res, N_seq, C_hidden]
694
+ g = self.sigmoid(self.linear_g(m))
695
+
696
+ # [*, N_res, N_seq, H, C_hidden]
697
+ g = g.view(g.shape[:-1] + (self.no_heads, -1))
698
+
699
+ # [*, N_res, N_seq, H, C_hidden]
700
+ o = o.unsqueeze(-3) * g
701
+
702
+ # [*, N_res, N_seq, H * C_hidden]
703
+ o = o.reshape(o.shape[:-2] + (-1,))
704
+
705
+ # [*, N_res, N_seq, C_in]
706
+ m = self.linear_o(o)
707
+
708
+ return m
709
+
710
+
711
+ @torch.jit.ignore
712
+ def _deepspeed_evo_attn(
713
+ q: torch.Tensor,
714
+ k: torch.Tensor,
715
+ v: torch.Tensor,
716
+ biases: List[torch.Tensor],
717
+ ):
718
+ """""
719
+ Compute attention using the DeepSpeed DS4Sci_EvoformerAttention kernel.
720
+
721
+ Args:
722
+ q:
723
+ [*, H, Q, C_hidden] query data
724
+ k:
725
+ [*, H, K, C_hidden] key data
726
+ v:
727
+ [*, H, V, C_hidden] value data
728
+ biases:
729
+ List of biases that broadcast to [*, H, Q, K]
730
+ """
731
+
732
+ if not ds4s_is_installed:
733
+ raise ValueError(
734
+ "_deepspeed_evo_attn requires that DeepSpeed be installed "
735
+ "and that the deepspeed.ops.deepspeed4science package exists"
736
+ )
737
+
738
+ def reshape_dims(x):
739
+ no_batch_dims = len(x.shape[:-3])
740
+ if no_batch_dims < 2:
741
+ return x.reshape(*((1,) * (2 - no_batch_dims) + x.shape))
742
+ if no_batch_dims > 2:
743
+ return x.reshape(*((x.shape[0], -1) + x.shape[-3:]))
744
+ return x
745
+
746
+ # [*, Q/K, H, C_hidden]
747
+ q = q.transpose(-2, -3)
748
+ k = k.transpose(-2, -3)
749
+ v = v.transpose(-2, -3)
750
+
751
+ # Reshape tensors to match expected input shape [B, N, Q/K, H, C_hidden]
752
+ # for DS4Sci_EvoformerAttention() by adding or flattening batch dims as needed.
753
+ orig_shape = q.shape
754
+ if len(orig_shape[:-3]) != 2:
755
+ q = reshape_dims(q)
756
+ k = reshape_dims(k)
757
+ v = reshape_dims(v)
758
+ biases = [reshape_dims(b) for b in biases]
759
+
760
+ # DeepSpeed attn. kernel requires inputs to be type bf16 or fp16
761
+ # Cast to bf16 so kernel can be used during inference
762
+ orig_dtype = q.dtype
763
+ if orig_dtype not in [torch.bfloat16, torch.float16]:
764
+ o = DS4Sci_EvoformerAttention(q.to(dtype=torch.bfloat16),
765
+ k.to(dtype=torch.bfloat16),
766
+ v.to(dtype=torch.bfloat16),
767
+ [b.to(dtype=torch.bfloat16) for b in biases])
768
+
769
+ o = o.to(dtype=orig_dtype)
770
+ else:
771
+ o = DS4Sci_EvoformerAttention(q, k, v, biases)
772
+
773
+ o = o.reshape(orig_shape)
774
+ return o
775
+
776
+
777
+ def _lma(
778
+ q: torch.Tensor,
779
+ k: torch.Tensor,
780
+ v: torch.Tensor,
781
+ biases: List[torch.Tensor],
782
+ q_chunk_size: int,
783
+ kv_chunk_size: int,
784
+ ):
785
+ no_q, no_kv = q.shape[-2], k.shape[-2]
786
+
787
+ # [*, H, Q, C_hidden]
788
+ o = q.new_zeros(q.shape)
789
+ for q_s in range(0, no_q, q_chunk_size):
790
+ q_chunk = q[..., q_s: q_s + q_chunk_size, :]
791
+ large_bias_chunks = [
792
+ b[..., q_s: q_s + q_chunk_size, :] for b in biases
793
+ ]
794
+
795
+ maxes = []
796
+ weights = []
797
+ values = []
798
+ for kv_s in range(0, no_kv, kv_chunk_size):
799
+ k_chunk = k[..., kv_s: kv_s + kv_chunk_size, :]
800
+ v_chunk = v[..., kv_s: kv_s + kv_chunk_size, :]
801
+ small_bias_chunks = [
802
+ b[..., kv_s: kv_s + kv_chunk_size] for b in large_bias_chunks
803
+ ]
804
+
805
+ a = torch.einsum(
806
+ "...hqd,...hkd->...hqk", q_chunk, k_chunk,
807
+ )
808
+
809
+ for b in small_bias_chunks:
810
+ a += b
811
+
812
+ max_a = torch.max(a, dim=-1, keepdim=True)[0]
813
+ exp_a = torch.exp(a - max_a)
814
+ exp_v = torch.einsum("...hvf,...hqv->...hqf", v_chunk, exp_a)
815
+
816
+ maxes.append(max_a.detach().squeeze(-1))
817
+ weights.append(torch.sum(exp_a, dim=-1))
818
+ values.append(exp_v)
819
+
820
+ chunk_max = torch.stack(maxes, dim=-3)
821
+ chunk_weights = torch.stack(weights, dim=-3)
822
+ chunk_values = torch.stack(values, dim=-4)
823
+
824
+ global_max = torch.max(chunk_max, dim=-3, keepdim=True)[0]
825
+ max_diffs = torch.exp(chunk_max - global_max)
826
+ chunk_values = chunk_values * max_diffs.unsqueeze(-1)
827
+ chunk_weights = chunk_weights * max_diffs
828
+
829
+ all_values = torch.sum(chunk_values, dim=-4)
830
+ all_weights = torch.sum(chunk_weights.unsqueeze(-1), dim=-4)
831
+
832
+ q_chunk_out = all_values / all_weights
833
+
834
+ o[..., q_s: q_s + q_chunk_size, :] = q_chunk_out
835
+
836
+ return o
837
+
838
+
839
+ @torch.jit.ignore
840
+ def _flash_attn(q, k, v, kv_mask):
841
+ if not fa_is_installed:
842
+ raise ValueError(
843
+ "_flash_attn requires that FlashAttention be installed"
844
+ )
845
+
846
+ batch_dims = q.shape[:-3]
847
+ no_heads, n, c = q.shape[-3:]
848
+ dtype = q.dtype
849
+
850
+ q = q.half()
851
+ k = k.half()
852
+ v = v.half()
853
+ kv_mask = kv_mask.half()
854
+
855
+ # [*, B, N, H, C]
856
+ q = q.transpose(-2, -3)
857
+ k = k.transpose(-2, -3)
858
+ v = v.transpose(-2, -3)
859
+
860
+ # [B_flat, N, H, C]
861
+ q = q.reshape(-1, *q.shape[-3:])
862
+ k = k.reshape(-1, *k.shape[-3:])
863
+ v = v.reshape(-1, *v.shape[-3:])
864
+
865
+ # Flattened batch size
866
+ batch_size = q.shape[0]
867
+
868
+ # [B_flat * N, H, C]
869
+ q = q.reshape(-1, *q.shape[-2:])
870
+
871
+ q_max_s = n
872
+ q_cu_seqlens = torch.arange(
873
+ 0, (batch_size + 1) * n, step=n, dtype=torch.int32, device=q.device
874
+ )
875
+
876
+ # [B_flat, N, 2, H, C]
877
+ kv = torch.stack([k, v], dim=-3)
878
+ kv_shape = kv.shape
879
+
880
+ # [B_flat, N, 2 * H * C]
881
+ kv = kv.reshape(*kv.shape[:-3], -1)
882
+
883
+ kv_unpad, _, kv_cu_seqlens, kv_max_s, _ = unpad_input(kv, kv_mask)
884
+ kv_unpad = kv_unpad.reshape(-1, *kv_shape[-3:])
885
+
886
+ out = flash_attn_varlen_kvpacked_func(
887
+ q,
888
+ kv_unpad,
889
+ q_cu_seqlens,
890
+ kv_cu_seqlens,
891
+ q_max_s,
892
+ kv_max_s,
893
+ dropout_p=0.,
894
+ softmax_scale=1., # q has been scaled already
895
+ )
896
+
897
+ # [*, B, N, H, C]
898
+ out = out.reshape(*batch_dims, n, no_heads, c)
899
+
900
+ out = out.to(dtype=dtype)
901
+
902
+ return out
models/openfold/structure_module.py ADDED
@@ -0,0 +1,1252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from functools import reduce
16
+ import importlib
17
+ import math
18
+ import sys
19
+ from operator import mul
20
+
21
+ import torch
22
+ import torch.nn as nn
23
+ from typing import Optional, Tuple, Sequence, Union
24
+
25
+ from .openfold.primitives import Linear, LayerNorm, ipa_point_weights_init_
26
+ from onescience.utils.openfold.np.residue_constants import (
27
+ restype_rigid_group_default_frame,
28
+ restype_atom14_to_rigid_group,
29
+ restype_atom14_mask,
30
+ restype_atom14_rigid_group_positions,
31
+ )
32
+ from onescience.utils.openfold.geometry.quat_rigid import QuatRigid
33
+ from onescience.utils.openfold.geometry.rigid_matrix_vector import Rigid3Array
34
+ from onescience.utils.openfold.geometry.vector import Vec3Array, square_euclidean_distance
35
+ from onescience.utils.openfold.feats import (
36
+ frames_and_literature_positions_to_atom14_pos,
37
+ torsion_angles_to_frames,
38
+ )
39
+ from onescience.utils.openfold.precision_utils import is_fp16_enabled
40
+ from onescience.utils.openfold.rigid_utils import Rotation, Rigid
41
+ from onescience.utils.openfold.tensor_utils import (
42
+ dict_multimap,
43
+ permute_final_dims,
44
+ flatten_final_dims,
45
+ )
46
+
47
+ attn_core_inplace_cuda = importlib.import_module("attn_core_inplace_cuda")
48
+
49
+
50
+ class AngleResnetBlock(nn.Module):
51
+ def __init__(self, c_hidden):
52
+ """
53
+ Args:
54
+ c_hidden:
55
+ Hidden channel dimension
56
+ """
57
+ super(AngleResnetBlock, self).__init__()
58
+
59
+ self.c_hidden = c_hidden
60
+
61
+ self.linear_1 = Linear(self.c_hidden, self.c_hidden, init="relu")
62
+ self.linear_2 = Linear(self.c_hidden, self.c_hidden, init="final")
63
+
64
+ self.relu = nn.ReLU()
65
+
66
+ def forward(self, a: torch.Tensor) -> torch.Tensor:
67
+
68
+ s_initial = a
69
+
70
+ a = self.relu(a)
71
+ a = self.linear_1(a)
72
+ a = self.relu(a)
73
+ a = self.linear_2(a)
74
+
75
+ return a + s_initial
76
+
77
+
78
+ class AngleResnet(nn.Module):
79
+ """
80
+ Implements Algorithm 20, lines 11-14
81
+ """
82
+
83
+ def __init__(self, c_in, c_hidden, no_blocks, no_angles, epsilon):
84
+ """
85
+ Args:
86
+ c_in:
87
+ Input channel dimension
88
+ c_hidden:
89
+ Hidden channel dimension
90
+ no_blocks:
91
+ Number of resnet blocks
92
+ no_angles:
93
+ Number of torsion angles to generate
94
+ epsilon:
95
+ Small constant for normalization
96
+ """
97
+ super(AngleResnet, self).__init__()
98
+
99
+ self.c_in = c_in
100
+ self.c_hidden = c_hidden
101
+ self.no_blocks = no_blocks
102
+ self.no_angles = no_angles
103
+ self.eps = epsilon
104
+
105
+ self.linear_in = Linear(self.c_in, self.c_hidden)
106
+ self.linear_initial = Linear(self.c_in, self.c_hidden)
107
+
108
+ self.layers = nn.ModuleList()
109
+ for _ in range(self.no_blocks):
110
+ layer = AngleResnetBlock(c_hidden=self.c_hidden)
111
+ self.layers.append(layer)
112
+
113
+ self.linear_out = Linear(self.c_hidden, self.no_angles * 2)
114
+
115
+ self.relu = nn.ReLU()
116
+
117
+ def forward(
118
+ self, s: torch.Tensor, s_initial: torch.Tensor
119
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
120
+ """
121
+ Args:
122
+ s:
123
+ [*, C_hidden] single embedding
124
+ s_initial:
125
+ [*, C_hidden] single embedding as of the start of the
126
+ StructureModule
127
+ Returns:
128
+ [*, no_angles, 2] predicted angles
129
+ """
130
+ # NOTE: The ReLU's applied to the inputs are absent from the supplement
131
+ # pseudocode but present in the source. For maximal compatibility with
132
+ # the pretrained weights, I'm going with the source.
133
+
134
+ # [*, C_hidden]
135
+ s_initial = self.relu(s_initial)
136
+ s_initial = self.linear_initial(s_initial)
137
+ s = self.relu(s)
138
+ s = self.linear_in(s)
139
+ s = s + s_initial
140
+
141
+ for l in self.layers:
142
+ s = l(s)
143
+
144
+ s = self.relu(s)
145
+
146
+ # [*, no_angles * 2]
147
+ s = self.linear_out(s)
148
+
149
+ # [*, no_angles, 2]
150
+ s = s.view(s.shape[:-1] + (-1, 2))
151
+
152
+ unnormalized_s = s
153
+ norm_denom = torch.sqrt(
154
+ torch.clamp(
155
+ torch.sum(s ** 2, dim=-1, keepdim=True),
156
+ min=self.eps,
157
+ )
158
+ )
159
+ s = s / norm_denom
160
+
161
+ return unnormalized_s, s
162
+
163
+
164
+ class PointProjection(nn.Module):
165
+ def __init__(self,
166
+ c_hidden: int,
167
+ num_points: int,
168
+ no_heads: int,
169
+ is_multimer: bool,
170
+ return_local_points: bool = False,
171
+ ):
172
+ super().__init__()
173
+ self.return_local_points = return_local_points
174
+ self.no_heads = no_heads
175
+ self.num_points = num_points
176
+ self.is_multimer = is_multimer
177
+
178
+ # Multimer requires this to be run with fp32 precision during training
179
+ precision = torch.float32 if self.is_multimer else None
180
+ self.linear = Linear(c_hidden, no_heads * 3 * num_points, precision=precision)
181
+
182
+ def forward(self,
183
+ activations: torch.Tensor,
184
+ rigids: Union[Rigid, Rigid3Array],
185
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
186
+ # TODO: Needs to run in high precision during training
187
+ points_local = self.linear(activations)
188
+ out_shape = points_local.shape[:-1] + (self.no_heads, self.num_points, 3)
189
+
190
+ if self.is_multimer:
191
+ points_local = points_local.view(
192
+ points_local.shape[:-1] + (self.no_heads, -1)
193
+ )
194
+
195
+ points_local = torch.split(
196
+ points_local, points_local.shape[-1] // 3, dim=-1
197
+ )
198
+
199
+ points_local = torch.stack(points_local, dim=-1).view(out_shape)
200
+
201
+ points_global = rigids[..., None, None].apply(points_local)
202
+
203
+ if(self.return_local_points):
204
+ return points_global, points_local
205
+
206
+ return points_global
207
+
208
+
209
+ class InvariantPointAttention(nn.Module):
210
+ """
211
+ Implements Algorithm 22.
212
+ """
213
+ def __init__(
214
+ self,
215
+ c_s: int,
216
+ c_z: int,
217
+ c_hidden: int,
218
+ no_heads: int,
219
+ no_qk_points: int,
220
+ no_v_points: int,
221
+ inf: float = 1e5,
222
+ eps: float = 1e-8,
223
+ is_multimer: bool = False,
224
+ ):
225
+ """
226
+ Args:
227
+ c_s:
228
+ Single representation channel dimension
229
+ c_z:
230
+ Pair representation channel dimension
231
+ c_hidden:
232
+ Hidden channel dimension
233
+ no_heads:
234
+ Number of attention heads
235
+ no_qk_points:
236
+ Number of query/key points to generate
237
+ no_v_points:
238
+ Number of value points to generate
239
+ """
240
+ super(InvariantPointAttention, self).__init__()
241
+
242
+ self.c_s = c_s
243
+ self.c_z = c_z
244
+ self.c_hidden = c_hidden
245
+ self.no_heads = no_heads
246
+ self.no_qk_points = no_qk_points
247
+ self.no_v_points = no_v_points
248
+ self.inf = inf
249
+ self.eps = eps
250
+ self.is_multimer = is_multimer
251
+
252
+ # These linear layers differ from their specifications in the
253
+ # supplement. There, they lack bias and use Glorot initialization.
254
+ # Here as in the official source, they have bias and use the default
255
+ # Lecun initialization.
256
+ hc = self.c_hidden * self.no_heads
257
+ self.linear_q = Linear(self.c_s, hc, bias=(not is_multimer))
258
+
259
+ self.linear_q_points = PointProjection(
260
+ self.c_s,
261
+ self.no_qk_points,
262
+ self.no_heads,
263
+ self.is_multimer
264
+ )
265
+
266
+ if(is_multimer):
267
+ self.linear_k = Linear(self.c_s, hc, bias=False)
268
+ self.linear_v = Linear(self.c_s, hc, bias=False)
269
+ self.linear_k_points = PointProjection(
270
+ self.c_s,
271
+ self.no_qk_points,
272
+ self.no_heads,
273
+ self.is_multimer
274
+ )
275
+
276
+ self.linear_v_points = PointProjection(
277
+ self.c_s,
278
+ self.no_v_points,
279
+ self.no_heads,
280
+ self.is_multimer
281
+ )
282
+ else:
283
+ self.linear_kv = Linear(self.c_s, 2 * hc)
284
+ self.linear_kv_points = PointProjection(
285
+ self.c_s,
286
+ self.no_qk_points + self.no_v_points,
287
+ self.no_heads,
288
+ self.is_multimer
289
+ )
290
+
291
+ self.linear_b = Linear(self.c_z, self.no_heads)
292
+
293
+ self.head_weights = nn.Parameter(torch.zeros((no_heads)))
294
+ ipa_point_weights_init_(self.head_weights)
295
+
296
+ concat_out_dim = self.no_heads * (
297
+ self.c_z + self.c_hidden + self.no_v_points * 4
298
+ )
299
+ self.linear_out = Linear(concat_out_dim, self.c_s, init="final")
300
+
301
+ self.softmax = nn.Softmax(dim=-1)
302
+ self.softplus = nn.Softplus()
303
+
304
+ def forward(
305
+ self,
306
+ s: torch.Tensor,
307
+ z: torch.Tensor,
308
+ r: Union[Rigid, Rigid3Array],
309
+ mask: torch.Tensor,
310
+ inplace_safe: bool = False,
311
+ _offload_inference: bool = False,
312
+ _z_reference_list: Optional[Sequence[torch.Tensor]] = None,
313
+ ) -> torch.Tensor:
314
+ """
315
+ Args:
316
+ s:
317
+ [*, N_res, C_s] single representation
318
+ z:
319
+ [*, N_res, N_res, C_z] pair representation
320
+ r:
321
+ [*, N_res] transformation object
322
+ mask:
323
+ [*, N_res] mask
324
+ Returns:
325
+ [*, N_res, C_s] single representation update
326
+ """
327
+ if (_offload_inference and inplace_safe):
328
+ z = _z_reference_list
329
+ else:
330
+ z = [z]
331
+
332
+ #######################################
333
+ # Generate scalar and point activations
334
+ #######################################
335
+ # [*, N_res, H * C_hidden]
336
+ q = self.linear_q(s)
337
+
338
+ # [*, N_res, H, C_hidden]
339
+ q = q.view(q.shape[:-1] + (self.no_heads, -1))
340
+
341
+ # [*, N_res, H, P_qk]
342
+ q_pts = self.linear_q_points(s, r)
343
+
344
+ # The following two blocks are equivalent
345
+ # They're separated only to preserve compatibility with old AF weights
346
+ if(self.is_multimer):
347
+ # [*, N_res, H * C_hidden]
348
+ k = self.linear_k(s)
349
+ v = self.linear_v(s)
350
+
351
+ # [*, N_res, H, C_hidden]
352
+ k = k.view(k.shape[:-1] + (self.no_heads, -1))
353
+ v = v.view(v.shape[:-1] + (self.no_heads, -1))
354
+
355
+ # [*, N_res, H, P_qk, 3]
356
+ k_pts = self.linear_k_points(s, r)
357
+
358
+ # [*, N_res, H, P_v, 3]
359
+ v_pts = self.linear_v_points(s, r)
360
+ else:
361
+ # [*, N_res, H * 2 * C_hidden]
362
+ kv = self.linear_kv(s)
363
+
364
+ # [*, N_res, H, 2 * C_hidden]
365
+ kv = kv.view(kv.shape[:-1] + (self.no_heads, -1))
366
+
367
+ # [*, N_res, H, C_hidden]
368
+ k, v = torch.split(kv, self.c_hidden, dim=-1)
369
+
370
+ kv_pts = self.linear_kv_points(s, r)
371
+
372
+ # [*, N_res, H, P_q/P_v, 3]
373
+ k_pts, v_pts = torch.split(
374
+ kv_pts, [self.no_qk_points, self.no_v_points], dim=-2
375
+ )
376
+
377
+ ##########################
378
+ # Compute attention scores
379
+ ##########################
380
+ # [*, N_res, N_res, H]
381
+ b = self.linear_b(z[0])
382
+
383
+ if (_offload_inference):
384
+ assert (sys.getrefcount(z[0]) == 2)
385
+ z[0] = z[0].cpu()
386
+
387
+ # [*, H, N_res, N_res]
388
+ if (is_fp16_enabled()):
389
+ with torch.cuda.amp.autocast(enabled=False):
390
+ a = torch.matmul(
391
+ permute_final_dims(q.float(), (1, 0, 2)), # [*, H, N_res, C_hidden]
392
+ permute_final_dims(k.float(), (1, 2, 0)), # [*, H, C_hidden, N_res]
393
+ )
394
+ else:
395
+ a = torch.matmul(
396
+ permute_final_dims(q, (1, 0, 2)), # [*, H, N_res, C_hidden]
397
+ permute_final_dims(k, (1, 2, 0)), # [*, H, C_hidden, N_res]
398
+ )
399
+
400
+ a *= math.sqrt(1.0 / (3 * self.c_hidden))
401
+ a += (math.sqrt(1.0 / 3) * permute_final_dims(b, (2, 0, 1)))
402
+
403
+ # [*, N_res, N_res, H, P_q, 3]
404
+ pt_att = q_pts.unsqueeze(-4) - k_pts.unsqueeze(-5)
405
+
406
+ if (inplace_safe):
407
+ pt_att *= pt_att
408
+ else:
409
+ pt_att = pt_att ** 2
410
+
411
+ pt_att = sum(torch.unbind(pt_att, dim=-1))
412
+
413
+ head_weights = self.softplus(self.head_weights).view(
414
+ *((1,) * len(pt_att.shape[:-2]) + (-1, 1))
415
+ )
416
+ head_weights = head_weights * math.sqrt(
417
+ 1.0 / (3 * (self.no_qk_points * 9.0 / 2))
418
+ )
419
+
420
+ if (inplace_safe):
421
+ pt_att *= head_weights
422
+ else:
423
+ pt_att = pt_att * head_weights
424
+
425
+ # [*, N_res, N_res, H]
426
+ pt_att = torch.sum(pt_att, dim=-1) * (-0.5)
427
+
428
+ # [*, N_res, N_res]
429
+ square_mask = mask.unsqueeze(-1) * mask.unsqueeze(-2)
430
+ square_mask = self.inf * (square_mask - 1)
431
+
432
+ # [*, H, N_res, N_res]
433
+ pt_att = permute_final_dims(pt_att, (2, 0, 1))
434
+
435
+ if (inplace_safe):
436
+ a += pt_att
437
+ del pt_att
438
+ a += square_mask.unsqueeze(-3)
439
+ # in-place softmax
440
+ attn_core_inplace_cuda.forward_(
441
+ a,
442
+ reduce(mul, a.shape[:-1]),
443
+ a.shape[-1],
444
+ )
445
+ else:
446
+ a = a + pt_att
447
+ a = a + square_mask.unsqueeze(-3)
448
+ a = self.softmax(a)
449
+
450
+ ################
451
+ # Compute output
452
+ ################
453
+ # [*, N_res, H, C_hidden]
454
+ o = torch.matmul(
455
+ a, v.transpose(-2, -3).to(dtype=a.dtype)
456
+ ).transpose(-2, -3)
457
+
458
+ # [*, N_res, H * C_hidden]
459
+ o = flatten_final_dims(o, 2)
460
+
461
+ # [*, H, 3, N_res, P_v]
462
+ if (inplace_safe):
463
+ v_pts = permute_final_dims(v_pts, (1, 3, 0, 2))
464
+ o_pt = [
465
+ torch.matmul(a, v.to(a.dtype))
466
+ for v in torch.unbind(v_pts, dim=-3)
467
+ ]
468
+ o_pt = torch.stack(o_pt, dim=-3)
469
+ else:
470
+ o_pt = torch.sum(
471
+ (
472
+ a[..., None, :, :, None]
473
+ * permute_final_dims(v_pts, (1, 3, 0, 2))[..., None, :, :]
474
+ ),
475
+ dim=-2,
476
+ )
477
+
478
+ # [*, N_res, H, P_v, 3]
479
+ o_pt = permute_final_dims(o_pt, (2, 0, 3, 1))
480
+ o_pt = r[..., None, None].invert_apply(o_pt)
481
+
482
+ # [*, N_res, H * P_v]
483
+ o_pt_norm = flatten_final_dims(
484
+ torch.sqrt(torch.sum(o_pt ** 2, dim=-1) + self.eps), 2
485
+ )
486
+
487
+ # [*, N_res, H * P_v, 3]
488
+ o_pt = o_pt.reshape(*o_pt.shape[:-3], -1, 3)
489
+ o_pt = torch.unbind(o_pt, dim=-1)
490
+
491
+ if (_offload_inference):
492
+ z[0] = z[0].to(o_pt.device)
493
+
494
+ # [*, N_res, H, C_z]
495
+ o_pair = torch.matmul(a.transpose(-2, -3), z[0].to(dtype=a.dtype))
496
+
497
+ # [*, N_res, H * C_z]
498
+ o_pair = flatten_final_dims(o_pair, 2)
499
+
500
+ # [*, N_res, C_s]
501
+ s = self.linear_out(
502
+ torch.cat(
503
+ (o, *o_pt, o_pt_norm, o_pair), dim=-1
504
+ ).to(dtype=z[0].dtype)
505
+ )
506
+
507
+ return s
508
+
509
+
510
+ #TODO: This module follows the refactoring done in IPA for multimer. Running the regular IPA above
511
+ # in multimer mode should be equivalent, but tests do not pass unless using this version. Determine
512
+ # whether or not the increase in test error matters in practice.
513
+ class InvariantPointAttentionMultimer(nn.Module):
514
+ """
515
+ Implements Algorithm 22.
516
+ """
517
+ def __init__(
518
+ self,
519
+ c_s: int,
520
+ c_z: int,
521
+ c_hidden: int,
522
+ no_heads: int,
523
+ no_qk_points: int,
524
+ no_v_points: int,
525
+ inf: float = 1e5,
526
+ eps: float = 1e-8,
527
+ is_multimer: bool = True,
528
+ ):
529
+ """
530
+ Args:
531
+ c_s:
532
+ Single representation channel dimension
533
+ c_z:
534
+ Pair representation channel dimension
535
+ c_hidden:
536
+ Hidden channel dimension
537
+ no_heads:
538
+ Number of attention heads
539
+ no_qk_points:
540
+ Number of query/key points to generate
541
+ no_v_points:
542
+ Number of value points to generate
543
+ """
544
+ super(InvariantPointAttentionMultimer, self).__init__()
545
+
546
+ self.c_s = c_s
547
+ self.c_z = c_z
548
+ self.c_hidden = c_hidden
549
+ self.no_heads = no_heads
550
+ self.no_qk_points = no_qk_points
551
+ self.no_v_points = no_v_points
552
+ self.inf = inf
553
+ self.eps = eps
554
+
555
+ # These linear layers differ from their specifications in the
556
+ # supplement. There, they lack bias and use Glorot initialization.
557
+ # Here as in the official source, they have bias and use the default
558
+ # Lecun initialization.
559
+ hc = self.c_hidden * self.no_heads
560
+ self.linear_q = Linear(self.c_s, hc, bias=False)
561
+
562
+ self.linear_q_points = PointProjection(
563
+ self.c_s,
564
+ self.no_qk_points,
565
+ self.no_heads,
566
+ is_multimer=True
567
+ )
568
+
569
+ self.linear_k = Linear(self.c_s, hc, bias=False)
570
+ self.linear_v = Linear(self.c_s, hc, bias=False)
571
+ self.linear_k_points = PointProjection(
572
+ self.c_s,
573
+ self.no_qk_points,
574
+ self.no_heads,
575
+ is_multimer=True
576
+ )
577
+
578
+ self.linear_v_points = PointProjection(
579
+ self.c_s,
580
+ self.no_v_points,
581
+ self.no_heads,
582
+ is_multimer=True
583
+ )
584
+
585
+ self.linear_b = Linear(self.c_z, self.no_heads)
586
+
587
+ self.head_weights = nn.Parameter(torch.zeros((no_heads)))
588
+ ipa_point_weights_init_(self.head_weights)
589
+
590
+ concat_out_dim = self.no_heads * (
591
+ self.c_z + self.c_hidden + self.no_v_points * 4
592
+ )
593
+ self.linear_out = Linear(concat_out_dim, self.c_s, init="final")
594
+
595
+ self.softmax = nn.Softmax(dim=-2)
596
+
597
+ def forward(
598
+ self,
599
+ s: torch.Tensor,
600
+ z: Optional[torch.Tensor],
601
+ r: Union[Rigid, Rigid3Array],
602
+ mask: torch.Tensor,
603
+ inplace_safe: bool = False,
604
+ _offload_inference: bool = False,
605
+ _z_reference_list: Optional[Sequence[torch.Tensor]] = None,
606
+ ) -> torch.Tensor:
607
+ """
608
+ Args:
609
+ s:
610
+ [*, N_res, C_s] single representation
611
+ z:
612
+ [*, N_res, N_res, C_z] pair representation
613
+ r:
614
+ [*, N_res] transformation object
615
+ mask:
616
+ [*, N_res] mask
617
+ Returns:
618
+ [*, N_res, C_s] single representation update
619
+ """
620
+ if(_offload_inference and inplace_safe):
621
+ z = _z_reference_list
622
+ else:
623
+ z = [z]
624
+
625
+ a = 0.
626
+
627
+ point_variance = (max(self.no_qk_points, 1) * 9.0 / 2)
628
+ point_weights = math.sqrt(1.0 / point_variance)
629
+
630
+ softplus = lambda x: torch.logaddexp(x, torch.zeros_like(x))
631
+
632
+ head_weights = softplus(self.head_weights)
633
+ point_weights = point_weights * head_weights
634
+
635
+ #######################################
636
+ # Generate scalar and point activations
637
+ #######################################
638
+
639
+ # [*, N_res, H, P_qk]
640
+ q_pts = Vec3Array.from_array(self.linear_q_points(s, r))
641
+
642
+ # [*, N_res, H, P_qk, 3]
643
+ k_pts = Vec3Array.from_array(self.linear_k_points(s, r))
644
+
645
+ pt_att = square_euclidean_distance(q_pts.unsqueeze(-3), k_pts.unsqueeze(-4), epsilon=0.)
646
+ pt_att = torch.sum(pt_att * point_weights[..., None], dim=-1) * (-0.5)
647
+ pt_att = pt_att.to(dtype=s.dtype)
648
+ a = a + pt_att
649
+
650
+ scalar_variance = max(self.c_hidden, 1) * 1.
651
+ scalar_weights = math.sqrt(1.0 / scalar_variance)
652
+
653
+ # [*, N_res, H * C_hidden]
654
+ q = self.linear_q(s)
655
+ k = self.linear_k(s)
656
+
657
+ # [*, N_res, H, C_hidden]
658
+ q = q.view(q.shape[:-1] + (self.no_heads, -1))
659
+ k = k.view(k.shape[:-1] + (self.no_heads, -1))
660
+
661
+ q = q * scalar_weights
662
+ a = a + torch.einsum('...qhc,...khc->...qkh', q, k)
663
+
664
+ ##########################
665
+ # Compute attention scores
666
+ ##########################
667
+ # [*, N_res, N_res, H]
668
+ b = self.linear_b(z[0])
669
+
670
+ if (_offload_inference):
671
+ assert (sys.getrefcount(z[0]) == 2)
672
+ z[0] = z[0].cpu()
673
+
674
+ a = a + b
675
+
676
+ # [*, N_res, N_res]
677
+ square_mask = mask.unsqueeze(-1) * mask.unsqueeze(-2)
678
+ square_mask = self.inf * (square_mask - 1)
679
+
680
+ a = a + square_mask.unsqueeze(-1)
681
+ a = a * math.sqrt(1. / 3) # Normalize by number of logit terms (3)
682
+ a = self.softmax(a)
683
+
684
+ # [*, N_res, H * C_hidden]
685
+ v = self.linear_v(s)
686
+
687
+ # [*, N_res, H, C_hidden]
688
+ v = v.view(v.shape[:-1] + (self.no_heads, -1))
689
+
690
+ o = torch.einsum('...qkh, ...khc->...qhc', a, v)
691
+
692
+ # [*, N_res, H * C_hidden]
693
+ o = flatten_final_dims(o, 2)
694
+
695
+ # [*, N_res, H, P_v, 3]
696
+ v_pts = Vec3Array.from_array(self.linear_v_points(s, r))
697
+
698
+ # [*, N_res, H, P_v]
699
+ o_pt = v_pts[..., None, :, :, :] * a.unsqueeze(-1)
700
+ o_pt = o_pt.sum(dim=-3)
701
+ # o_pt = Vec3Array(
702
+ # torch.sum(a.unsqueeze(-1) * v_pts[..., None, :, :, :].x, dim=-3),
703
+ # torch.sum(a.unsqueeze(-1) * v_pts[..., None, :, :, :].y, dim=-3),
704
+ # torch.sum(a.unsqueeze(-1) * v_pts[..., None, :, :, :].z, dim=-3),
705
+ # )
706
+
707
+ # [*, N_res, H * P_v, 3]
708
+ o_pt = o_pt.reshape(o_pt.shape[:-2] + (-1,))
709
+
710
+ # [*, N_res, H, P_v]
711
+ o_pt = r[..., None].apply_inverse_to_point(o_pt)
712
+ o_pt_flat = [o_pt.x, o_pt.y, o_pt.z]
713
+ o_pt_flat = [x.to(dtype=a.dtype) for x in o_pt_flat]
714
+
715
+ # [*, N_res, H * P_v]
716
+ o_pt_norm = o_pt.norm(epsilon=1e-8)
717
+
718
+ if (_offload_inference):
719
+ z[0] = z[0].to(o_pt.x.device)
720
+
721
+ o_pair = torch.einsum('...ijh, ...ijc->...ihc', a, z[0].to(dtype=a.dtype))
722
+
723
+ # [*, N_res, H * C_z]
724
+ o_pair = flatten_final_dims(o_pair, 2)
725
+
726
+ # [*, N_res, C_s]
727
+ s = self.linear_out(
728
+ torch.cat(
729
+ (o, *o_pt_flat, o_pt_norm, o_pair), dim=-1
730
+ ).to(dtype=z[0].dtype)
731
+ )
732
+
733
+ return s
734
+
735
+
736
+ class BackboneUpdate(nn.Module):
737
+ """
738
+ Implements part of Algorithm 23.
739
+ """
740
+
741
+ def __init__(self, c_s):
742
+ """
743
+ Args:
744
+ c_s:
745
+ Single representation channel dimension
746
+ """
747
+ super(BackboneUpdate, self).__init__()
748
+
749
+ self.c_s = c_s
750
+
751
+ self.linear = Linear(self.c_s, 6, init="final")
752
+
753
+ def forward(self, s: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
754
+ """
755
+ Args:
756
+ [*, N_res, C_s] single representation
757
+ Returns:
758
+ [*, N_res, 6] update vector
759
+ """
760
+ # [*, 6]
761
+ update = self.linear(s)
762
+
763
+ return update
764
+
765
+
766
+ class StructureModuleTransitionLayer(nn.Module):
767
+ def __init__(self, c):
768
+ super(StructureModuleTransitionLayer, self).__init__()
769
+
770
+ self.c = c
771
+
772
+ self.linear_1 = Linear(self.c, self.c, init="relu")
773
+ self.linear_2 = Linear(self.c, self.c, init="relu")
774
+ self.linear_3 = Linear(self.c, self.c, init="final")
775
+
776
+ self.relu = nn.ReLU()
777
+
778
+ def forward(self, s):
779
+ s_initial = s
780
+ s = self.linear_1(s)
781
+ s = self.relu(s)
782
+ s = self.linear_2(s)
783
+ s = self.relu(s)
784
+ s = self.linear_3(s)
785
+
786
+ s = s + s_initial
787
+
788
+ return s
789
+
790
+
791
+ class StructureModuleTransition(nn.Module):
792
+ def __init__(self, c, num_layers, dropout_rate):
793
+ super(StructureModuleTransition, self).__init__()
794
+
795
+ self.c = c
796
+ self.num_layers = num_layers
797
+ self.dropout_rate = dropout_rate
798
+
799
+ self.layers = nn.ModuleList()
800
+ for _ in range(self.num_layers):
801
+ l = StructureModuleTransitionLayer(self.c)
802
+ self.layers.append(l)
803
+
804
+ self.dropout = nn.Dropout(self.dropout_rate)
805
+ self.layer_norm = LayerNorm(self.c)
806
+
807
+ def forward(self, s):
808
+ for l in self.layers:
809
+ s = l(s)
810
+
811
+ s = self.dropout(s)
812
+ s = self.layer_norm(s)
813
+
814
+ return s
815
+
816
+
817
+ class StructureModule(nn.Module):
818
+ def __init__(
819
+ self,
820
+ c_s,
821
+ c_z,
822
+ c_ipa,
823
+ c_resnet,
824
+ no_heads_ipa,
825
+ no_qk_points,
826
+ no_v_points,
827
+ dropout_rate,
828
+ no_blocks,
829
+ no_transition_layers,
830
+ no_resnet_blocks,
831
+ no_angles,
832
+ trans_scale_factor,
833
+ epsilon,
834
+ inf,
835
+ is_multimer=False,
836
+ **kwargs,
837
+ ):
838
+ """
839
+ Args:
840
+ c_s:
841
+ Single representation channel dimension
842
+ c_z:
843
+ Pair representation channel dimension
844
+ c_ipa:
845
+ IPA hidden channel dimension
846
+ c_resnet:
847
+ Angle resnet (Alg. 23 lines 11-14) hidden channel dimension
848
+ no_heads_ipa:
849
+ Number of IPA heads
850
+ no_qk_points:
851
+ Number of query/key points to generate during IPA
852
+ no_v_points:
853
+ Number of value points to generate during IPA
854
+ dropout_rate:
855
+ Dropout rate used throughout the layer
856
+ no_blocks:
857
+ Number of structure module blocks
858
+ no_transition_layers:
859
+ Number of layers in the single representation transition
860
+ (Alg. 23 lines 8-9)
861
+ no_resnet_blocks:
862
+ Number of blocks in the angle resnet
863
+ no_angles:
864
+ Number of angles to generate in the angle resnet
865
+ trans_scale_factor:
866
+ Scale of single representation transition hidden dimension
867
+ epsilon:
868
+ Small number used in angle resnet normalization
869
+ inf:
870
+ Large number used for attention masking
871
+ """
872
+ super(StructureModule, self).__init__()
873
+
874
+ self.c_s = c_s
875
+ self.c_z = c_z
876
+ self.c_ipa = c_ipa
877
+ self.c_resnet = c_resnet
878
+ self.no_heads_ipa = no_heads_ipa
879
+ self.no_qk_points = no_qk_points
880
+ self.no_v_points = no_v_points
881
+ self.dropout_rate = dropout_rate
882
+ self.no_blocks = no_blocks
883
+ self.no_transition_layers = no_transition_layers
884
+ self.no_resnet_blocks = no_resnet_blocks
885
+ self.no_angles = no_angles
886
+ self.trans_scale_factor = trans_scale_factor
887
+ self.epsilon = epsilon
888
+ self.inf = inf
889
+ self.is_multimer = is_multimer
890
+
891
+ # Buffers to be lazily initialized later
892
+ # self.default_frames
893
+ # self.group_idx
894
+ # self.atom_mask
895
+ # self.lit_positions
896
+
897
+ self.layer_norm_s = LayerNorm(self.c_s)
898
+ self.layer_norm_z = LayerNorm(self.c_z)
899
+
900
+ self.linear_in = Linear(self.c_s, self.c_s)
901
+
902
+ ipa = InvariantPointAttention if not self.is_multimer else InvariantPointAttentionMultimer
903
+ self.ipa = ipa(
904
+ self.c_s,
905
+ self.c_z,
906
+ self.c_ipa,
907
+ self.no_heads_ipa,
908
+ self.no_qk_points,
909
+ self.no_v_points,
910
+ inf=self.inf,
911
+ eps=self.epsilon,
912
+ is_multimer=self.is_multimer,
913
+ )
914
+
915
+ self.ipa_dropout = nn.Dropout(self.dropout_rate)
916
+ self.layer_norm_ipa = LayerNorm(self.c_s)
917
+
918
+ self.transition = StructureModuleTransition(
919
+ self.c_s,
920
+ self.no_transition_layers,
921
+ self.dropout_rate,
922
+ )
923
+
924
+ if self.is_multimer:
925
+ self.bb_update = QuatRigid(self.c_s, full_quat=False)
926
+ else:
927
+ self.bb_update = BackboneUpdate(self.c_s)
928
+
929
+ self.angle_resnet = AngleResnet(
930
+ self.c_s,
931
+ self.c_resnet,
932
+ self.no_resnet_blocks,
933
+ self.no_angles,
934
+ self.epsilon,
935
+ )
936
+
937
+ def _forward_monomer(
938
+ self,
939
+ evoformer_output_dict,
940
+ aatype,
941
+ mask=None,
942
+ inplace_safe=False,
943
+ _offload_inference=False,
944
+ ):
945
+ """
946
+ Args:
947
+ evoformer_output_dict:
948
+ Dictionary containing:
949
+ "single":
950
+ [*, N_res, C_s] single representation
951
+ "pair":
952
+ [*, N_res, N_res, C_z] pair representation
953
+ aatype:
954
+ [*, N_res] amino acid indices
955
+ mask:
956
+ Optional [*, N_res] sequence mask
957
+ Returns:
958
+ A dictionary of outputs
959
+ """
960
+ s = evoformer_output_dict["single"]
961
+
962
+ if mask is None:
963
+ # [*, N]
964
+ mask = s.new_ones(s.shape[:-1])
965
+
966
+ # [*, N, C_s]
967
+ s = self.layer_norm_s(s)
968
+
969
+ # [*, N, N, C_z]
970
+ z = self.layer_norm_z(evoformer_output_dict["pair"])
971
+
972
+ z_reference_list = None
973
+ if (_offload_inference):
974
+ assert (sys.getrefcount(evoformer_output_dict["pair"]) == 2)
975
+ evoformer_output_dict["pair"] = evoformer_output_dict["pair"].cpu()
976
+ z_reference_list = [z]
977
+ z = None
978
+
979
+ # [*, N, C_s]
980
+ s_initial = s
981
+ s = self.linear_in(s)
982
+
983
+ # [*, N]
984
+ rigids = Rigid.identity(
985
+ s.shape[:-1],
986
+ s.dtype,
987
+ s.device,
988
+ self.training,
989
+ fmt="quat",
990
+ )
991
+ outputs = []
992
+ for i in range(self.no_blocks):
993
+ # [*, N, C_s]
994
+ s = s + self.ipa(
995
+ s,
996
+ z,
997
+ rigids,
998
+ mask,
999
+ inplace_safe=inplace_safe,
1000
+ _offload_inference=_offload_inference,
1001
+ _z_reference_list=z_reference_list
1002
+ )
1003
+ s = self.ipa_dropout(s)
1004
+ s = self.layer_norm_ipa(s)
1005
+ s = self.transition(s)
1006
+
1007
+ # [*, N]
1008
+ rigids = rigids.compose_q_update_vec(self.bb_update(s))
1009
+
1010
+ # To hew as closely as possible to AlphaFold, we convert our
1011
+ # quaternion-based transformations to rotation-matrix ones
1012
+ # here
1013
+ backb_to_global = Rigid(
1014
+ Rotation(
1015
+ rot_mats=rigids.get_rots().get_rot_mats(),
1016
+ quats=None
1017
+ ),
1018
+ rigids.get_trans(),
1019
+ )
1020
+
1021
+ backb_to_global = backb_to_global.scale_translation(
1022
+ self.trans_scale_factor
1023
+ )
1024
+
1025
+ # [*, N, 7, 2]
1026
+ unnormalized_angles, angles = self.angle_resnet(s, s_initial)
1027
+
1028
+ all_frames_to_global = self.torsion_angles_to_frames(
1029
+ backb_to_global,
1030
+ angles,
1031
+ aatype,
1032
+ )
1033
+
1034
+ pred_xyz = self.frames_and_literature_positions_to_atom14_pos(
1035
+ all_frames_to_global,
1036
+ aatype,
1037
+ )
1038
+
1039
+ scaled_rigids = rigids.scale_translation(self.trans_scale_factor)
1040
+
1041
+ preds = {
1042
+ "frames": scaled_rigids.to_tensor_7(),
1043
+ "sidechain_frames": all_frames_to_global.to_tensor_4x4(),
1044
+ "unnormalized_angles": unnormalized_angles,
1045
+ "angles": angles,
1046
+ "positions": pred_xyz,
1047
+ "states": s,
1048
+ }
1049
+
1050
+ outputs.append(preds)
1051
+
1052
+ rigids = rigids.stop_rot_gradient()
1053
+
1054
+ del z, z_reference_list
1055
+
1056
+ if (_offload_inference):
1057
+ evoformer_output_dict["pair"] = (
1058
+ evoformer_output_dict["pair"].to(s.device)
1059
+ )
1060
+
1061
+ outputs = dict_multimap(torch.stack, outputs)
1062
+ outputs["single"] = s
1063
+
1064
+ return outputs
1065
+
1066
+ def _forward_multimer(
1067
+ self,
1068
+ evoformer_output_dict,
1069
+ aatype,
1070
+ mask=None,
1071
+ inplace_safe=False,
1072
+ _offload_inference=False,
1073
+ ):
1074
+ s = evoformer_output_dict["single"]
1075
+
1076
+ if mask is None:
1077
+ # [*, N]
1078
+ mask = s.new_ones(s.shape[:-1])
1079
+
1080
+ # [*, N, C_s]
1081
+ s = self.layer_norm_s(s)
1082
+
1083
+ # [*, N, N, C_z]
1084
+ z = self.layer_norm_z(evoformer_output_dict["pair"])
1085
+
1086
+ z_reference_list = None
1087
+ if (_offload_inference):
1088
+ assert (sys.getrefcount(evoformer_output_dict["pair"]) == 2)
1089
+ evoformer_output_dict["pair"] = evoformer_output_dict["pair"].cpu()
1090
+ z_reference_list = [z]
1091
+ z = None
1092
+
1093
+ # [*, N, C_s]
1094
+ s_initial = s
1095
+ s = self.linear_in(s)
1096
+
1097
+ # [*, N]
1098
+ rigids = Rigid3Array.identity(
1099
+ s.shape[:-1],
1100
+ s.device,
1101
+ )
1102
+ outputs = []
1103
+ for i in range(self.no_blocks):
1104
+ # [*, N, C_s]
1105
+ s = s + self.ipa(
1106
+ s,
1107
+ z,
1108
+ rigids,
1109
+ mask,
1110
+ inplace_safe=inplace_safe,
1111
+ _offload_inference=_offload_inference,
1112
+ _z_reference_list=z_reference_list
1113
+ )
1114
+ s = self.ipa_dropout(s)
1115
+ s = self.layer_norm_ipa(s)
1116
+ s = self.transition(s)
1117
+
1118
+ # [*, N]
1119
+ rigids = rigids @ self.bb_update(s)
1120
+
1121
+ # [*, N, 7, 2]
1122
+ unnormalized_angles, angles = self.angle_resnet(s, s_initial)
1123
+
1124
+ all_frames_to_global = self.torsion_angles_to_frames(
1125
+ rigids.scale_translation(self.trans_scale_factor),
1126
+ angles,
1127
+ aatype,
1128
+ )
1129
+
1130
+ pred_xyz = self.frames_and_literature_positions_to_atom14_pos(
1131
+ all_frames_to_global,
1132
+ aatype,
1133
+ )
1134
+
1135
+ preds = {
1136
+ "frames": rigids.scale_translation(self.trans_scale_factor).to_tensor(),
1137
+ "sidechain_frames": all_frames_to_global.to_tensor_4x4(),
1138
+ "unnormalized_angles": unnormalized_angles,
1139
+ "angles": angles,
1140
+ "positions": pred_xyz,
1141
+ }
1142
+
1143
+ preds = {k: v.to(dtype=s.dtype) for k, v in preds.items()}
1144
+
1145
+ outputs.append(preds)
1146
+
1147
+ rigids = rigids.stop_rot_gradient()
1148
+
1149
+ del z, z_reference_list
1150
+
1151
+ if (_offload_inference):
1152
+ evoformer_output_dict["pair"] = (
1153
+ evoformer_output_dict["pair"].to(s.device)
1154
+ )
1155
+
1156
+ outputs = dict_multimap(torch.stack, outputs)
1157
+ outputs["single"] = s
1158
+
1159
+ return outputs
1160
+
1161
+ def forward(
1162
+ self,
1163
+ evoformer_output_dict,
1164
+ aatype,
1165
+ mask=None,
1166
+ inplace_safe=False,
1167
+ _offload_inference=False,
1168
+ ):
1169
+ """
1170
+ Args:
1171
+ s:
1172
+ [*, N_res, C_s] single representation
1173
+ z:
1174
+ [*, N_res, N_res, C_z] pair representation
1175
+ aatype:
1176
+ [*, N_res] amino acid indices
1177
+ mask:
1178
+ Optional [*, N_res] sequence mask
1179
+ Returns:
1180
+ A dictionary of outputs
1181
+ """
1182
+ if(self.is_multimer):
1183
+ outputs = self._forward_multimer(evoformer_output_dict, aatype, mask, inplace_safe, _offload_inference)
1184
+ else:
1185
+ outputs = self._forward_monomer(evoformer_output_dict, aatype, mask, inplace_safe, _offload_inference)
1186
+
1187
+ return outputs
1188
+
1189
+ def _init_residue_constants(self, float_dtype, device):
1190
+ if not hasattr(self, "default_frames"):
1191
+ self.register_buffer(
1192
+ "default_frames",
1193
+ torch.tensor(
1194
+ restype_rigid_group_default_frame,
1195
+ dtype=float_dtype,
1196
+ device=device,
1197
+ requires_grad=False,
1198
+ ),
1199
+ persistent=False,
1200
+ )
1201
+ if not hasattr(self, "group_idx"):
1202
+ self.register_buffer(
1203
+ "group_idx",
1204
+ torch.tensor(
1205
+ restype_atom14_to_rigid_group,
1206
+ device=device,
1207
+ requires_grad=False,
1208
+ ),
1209
+ persistent=False,
1210
+ )
1211
+ if not hasattr(self, "atom_mask"):
1212
+ self.register_buffer(
1213
+ "atom_mask",
1214
+ torch.tensor(
1215
+ restype_atom14_mask,
1216
+ dtype=float_dtype,
1217
+ device=device,
1218
+ requires_grad=False,
1219
+ ),
1220
+ persistent=False,
1221
+ )
1222
+ if not hasattr(self, "lit_positions"):
1223
+ self.register_buffer(
1224
+ "lit_positions",
1225
+ torch.tensor(
1226
+ restype_atom14_rigid_group_positions,
1227
+ dtype=float_dtype,
1228
+ device=device,
1229
+ requires_grad=False,
1230
+ ),
1231
+ persistent=False,
1232
+ )
1233
+
1234
+ def torsion_angles_to_frames(self, r, alpha, f):
1235
+ # Lazily initialize the residue constants on the correct device
1236
+ self._init_residue_constants(alpha.dtype, alpha.device)
1237
+ # Separated purely to make testing less annoying
1238
+ return torsion_angles_to_frames(r, alpha, f, self.default_frames)
1239
+
1240
+ def frames_and_literature_positions_to_atom14_pos(
1241
+ self, r, f # [*, N, 8] # [*, N]
1242
+ ):
1243
+ # Lazily initialize the residue constants on the correct device
1244
+ self._init_residue_constants(r.dtype, r.device)
1245
+ return frames_and_literature_positions_to_atom14_pos(
1246
+ r,
1247
+ f,
1248
+ self.default_frames,
1249
+ self.group_idx,
1250
+ self.atom_mask,
1251
+ self.lit_positions,
1252
+ )
models/openfold/template.py ADDED
@@ -0,0 +1,693 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from functools import partial
16
+ import math
17
+ import sys
18
+ from typing import Optional, List
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+
23
+ from .openfold.primitives import LayerNorm, Attention
24
+ from .openfold.dropout import (
25
+ DropoutRowwise,
26
+ DropoutColumnwise,
27
+ )
28
+ from .openfold.pair_transition import PairTransition
29
+ from .openfold.triangular_attention import (
30
+ TriangleAttentionStartingNode,
31
+ TriangleAttentionEndingNode,
32
+ )
33
+ from .openfold.triangular_multiplicative_update import (
34
+ TriangleMultiplicationOutgoing,
35
+ TriangleMultiplicationIncoming,
36
+ FusedTriangleMultiplicationOutgoing,
37
+ FusedTriangleMultiplicationIncoming
38
+ )
39
+ from onescience.utils.openfold.checkpointing import checkpoint_blocks
40
+ from onescience.utils.openfold.chunk_utils import (
41
+ chunk_layer,
42
+ ChunkSizeTuner,
43
+ )
44
+ from onescience.utils.openfold.feats import (
45
+ build_template_angle_feat,
46
+ build_template_pair_feat,
47
+ )
48
+ from onescience.utils.openfold.tensor_utils import (
49
+ add,
50
+ permute_final_dims,
51
+ tensor_tree_map,
52
+ )
53
+
54
+
55
+ class TemplatePointwiseAttention(nn.Module):
56
+ """
57
+ Implements Algorithm 17.
58
+ """
59
+
60
+ def __init__(self, c_t, c_z, c_hidden, no_heads, inf, **kwargs):
61
+ """
62
+ Args:
63
+ c_t:
64
+ Template embedding channel dimension
65
+ c_z:
66
+ Pair embedding channel dimension
67
+ c_hidden:
68
+ Hidden channel dimension
69
+ """
70
+ super(TemplatePointwiseAttention, self).__init__()
71
+
72
+ self.c_t = c_t
73
+ self.c_z = c_z
74
+ self.c_hidden = c_hidden
75
+ self.no_heads = no_heads
76
+ self.inf = inf
77
+
78
+ self.mha = Attention(
79
+ self.c_z,
80
+ self.c_t,
81
+ self.c_t,
82
+ self.c_hidden,
83
+ self.no_heads,
84
+ gating=False,
85
+ )
86
+
87
+ def _chunk(self,
88
+ z: torch.Tensor,
89
+ t: torch.Tensor,
90
+ biases: List[torch.Tensor],
91
+ chunk_size: int,
92
+ use_lma: bool = False,
93
+ ) -> torch.Tensor:
94
+ mha_inputs = {
95
+ "q_x": z,
96
+ "kv_x": t,
97
+ "biases": biases,
98
+ }
99
+ return chunk_layer(
100
+ partial(self.mha, use_lma=use_lma),
101
+ mha_inputs,
102
+ chunk_size=chunk_size,
103
+ no_batch_dims=len(z.shape[:-2]),
104
+ )
105
+
106
+ def forward(self,
107
+ t: torch.Tensor,
108
+ z: torch.Tensor,
109
+ template_mask: Optional[torch.Tensor] = None,
110
+ # This module suffers greatly from a small chunk size
111
+ chunk_size: Optional[int] = 256,
112
+ use_lma: bool = False,
113
+ ) -> torch.Tensor:
114
+ """
115
+ Args:
116
+ t:
117
+ [*, N_templ, N_res, N_res, C_t] template embedding
118
+ z:
119
+ [*, N_res, N_res, C_t] pair embedding
120
+ template_mask:
121
+ [*, N_templ] template mask
122
+ Returns:
123
+ [*, N_res, N_res, C_z] pair embedding update
124
+ """
125
+ if template_mask is None:
126
+ template_mask = t.new_ones(t.shape[:-3])
127
+
128
+ bias = self.inf * (template_mask[..., None, None, None, None, :] - 1)
129
+
130
+ # [*, N_res, N_res, 1, C_z]
131
+ z = z.unsqueeze(-2)
132
+
133
+ # [*, N_res, N_res, N_temp, C_t]
134
+ t = permute_final_dims(t, (1, 2, 0, 3))
135
+
136
+ # [*, N_res, N_res, 1, C_z]
137
+ biases = [bias]
138
+ if chunk_size is not None and not self.training:
139
+ z = self._chunk(z, t, biases, chunk_size, use_lma=use_lma)
140
+ else:
141
+ z = self.mha(q_x=z, kv_x=t, biases=biases, use_lma=use_lma)
142
+
143
+ # [*, N_res, N_res, C_z]
144
+ z = z.squeeze(-2)
145
+
146
+ return z
147
+
148
+
149
+ class TemplatePairStackBlock(nn.Module):
150
+ def __init__(
151
+ self,
152
+ c_t: int,
153
+ c_hidden_tri_att: int,
154
+ c_hidden_tri_mul: int,
155
+ no_heads: int,
156
+ pair_transition_n: int,
157
+ dropout_rate: float,
158
+ tri_mul_first: bool,
159
+ fuse_projection_weights: bool,
160
+ inf: float,
161
+ **kwargs,
162
+ ):
163
+ super(TemplatePairStackBlock, self).__init__()
164
+
165
+ self.c_t = c_t
166
+ self.c_hidden_tri_att = c_hidden_tri_att
167
+ self.c_hidden_tri_mul = c_hidden_tri_mul
168
+ self.no_heads = no_heads
169
+ self.pair_transition_n = pair_transition_n
170
+ self.dropout_rate = dropout_rate
171
+ self.inf = inf
172
+ self.tri_mul_first = tri_mul_first
173
+
174
+ self.dropout_row = DropoutRowwise(self.dropout_rate)
175
+ self.dropout_col = DropoutColumnwise(self.dropout_rate)
176
+
177
+ self.tri_att_start = TriangleAttentionStartingNode(
178
+ self.c_t,
179
+ self.c_hidden_tri_att,
180
+ self.no_heads,
181
+ inf=inf,
182
+ )
183
+ self.tri_att_end = TriangleAttentionEndingNode(
184
+ self.c_t,
185
+ self.c_hidden_tri_att,
186
+ self.no_heads,
187
+ inf=inf,
188
+ )
189
+
190
+ if fuse_projection_weights:
191
+ self.tri_mul_out = FusedTriangleMultiplicationOutgoing(
192
+ self.c_t,
193
+ self.c_hidden_tri_mul,
194
+ )
195
+ self.tri_mul_in = FusedTriangleMultiplicationIncoming(
196
+ self.c_t,
197
+ self.c_hidden_tri_mul,
198
+ )
199
+ else:
200
+ self.tri_mul_out = TriangleMultiplicationOutgoing(
201
+ self.c_t,
202
+ self.c_hidden_tri_mul,
203
+ )
204
+ self.tri_mul_in = TriangleMultiplicationIncoming(
205
+ self.c_t,
206
+ self.c_hidden_tri_mul,
207
+ )
208
+
209
+ self.pair_transition = PairTransition(
210
+ self.c_t,
211
+ self.pair_transition_n,
212
+ )
213
+
214
+ def tri_att_start_end(self,
215
+ single: torch.Tensor,
216
+ _attn_chunk_size: Optional[int],
217
+ single_mask: torch.Tensor,
218
+ use_deepspeed_evo_attention: bool,
219
+ use_lma: bool,
220
+ inplace_safe: bool):
221
+ single = add(single,
222
+ self.dropout_row(
223
+ self.tri_att_start(
224
+ single,
225
+ chunk_size=_attn_chunk_size,
226
+ mask=single_mask,
227
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
228
+ use_lma=use_lma,
229
+ inplace_safe=inplace_safe,
230
+ )
231
+ ),
232
+ inplace_safe,
233
+ )
234
+
235
+ single = add(single,
236
+ self.dropout_col(
237
+ self.tri_att_end(
238
+ single,
239
+ chunk_size=_attn_chunk_size,
240
+ mask=single_mask,
241
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
242
+ use_lma=use_lma,
243
+ inplace_safe=inplace_safe,
244
+ )
245
+ ),
246
+ inplace_safe,
247
+ )
248
+
249
+ return single
250
+
251
+ def tri_mul_out_in(self,
252
+ single: torch.Tensor,
253
+ single_mask: torch.Tensor,
254
+ inplace_safe: bool):
255
+ tmu_update = self.tri_mul_out(
256
+ single,
257
+ mask=single_mask,
258
+ inplace_safe=inplace_safe,
259
+ _add_with_inplace=True,
260
+ )
261
+ if not inplace_safe:
262
+ single = single + self.dropout_row(tmu_update)
263
+ else:
264
+ single = tmu_update
265
+
266
+ del tmu_update
267
+
268
+ tmu_update = self.tri_mul_in(
269
+ single,
270
+ mask=single_mask,
271
+ inplace_safe=inplace_safe,
272
+ _add_with_inplace=True,
273
+ )
274
+ if not inplace_safe:
275
+ single = single + self.dropout_row(tmu_update)
276
+ else:
277
+ single = tmu_update
278
+
279
+ del tmu_update
280
+
281
+ return single
282
+
283
+ def forward(self,
284
+ z: torch.Tensor,
285
+ mask: torch.Tensor,
286
+ chunk_size: Optional[int] = None,
287
+ use_deepspeed_evo_attention: bool = False,
288
+ use_lma: bool = False,
289
+ inplace_safe: bool = False,
290
+ _mask_trans: bool = True,
291
+ _attn_chunk_size: Optional[int] = None,
292
+ ):
293
+ if _attn_chunk_size is None:
294
+ _attn_chunk_size = chunk_size
295
+
296
+ single_templates = [
297
+ t.unsqueeze(-4) for t in torch.unbind(z, dim=-4)
298
+ ]
299
+ single_templates_masks = [
300
+ m.unsqueeze(-3) for m in torch.unbind(mask, dim=-3)
301
+ ]
302
+
303
+ for i in range(len(single_templates)):
304
+ single = single_templates[i]
305
+ single_mask = single_templates_masks[i]
306
+
307
+ if self.tri_mul_first:
308
+ single = self.tri_att_start_end(single=self.tri_mul_out_in(single=single,
309
+ single_mask=single_mask,
310
+ inplace_safe=inplace_safe),
311
+ _attn_chunk_size=_attn_chunk_size,
312
+ single_mask=single_mask,
313
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
314
+ use_lma=use_lma,
315
+ inplace_safe=inplace_safe)
316
+ else:
317
+ single = self.tri_mul_out_in(
318
+ single=self.tri_att_start_end(single=single,
319
+ _attn_chunk_size=_attn_chunk_size,
320
+ single_mask=single_mask,
321
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
322
+ use_lma=use_lma,
323
+ inplace_safe=inplace_safe),
324
+ single_mask=single_mask,
325
+ inplace_safe=inplace_safe)
326
+
327
+ single = add(single,
328
+ self.pair_transition(
329
+ single,
330
+ mask=single_mask if _mask_trans else None,
331
+ chunk_size=chunk_size,
332
+ ),
333
+ inplace_safe,
334
+ )
335
+
336
+ if not inplace_safe:
337
+ single_templates[i] = single
338
+
339
+ if not inplace_safe:
340
+ z = torch.cat(single_templates, dim=-4)
341
+
342
+ return z
343
+
344
+
345
+ class TemplatePairStack(nn.Module):
346
+ """
347
+ Implements Algorithm 16.
348
+ """
349
+
350
+ def __init__(
351
+ self,
352
+ c_t,
353
+ c_hidden_tri_att,
354
+ c_hidden_tri_mul,
355
+ no_blocks,
356
+ no_heads,
357
+ pair_transition_n,
358
+ dropout_rate,
359
+ tri_mul_first,
360
+ fuse_projection_weights,
361
+ blocks_per_ckpt,
362
+ tune_chunk_size: bool = False,
363
+ inf=1e9,
364
+ **kwargs,
365
+ ):
366
+ """
367
+ Args:
368
+ c_t:
369
+ Template embedding channel dimension
370
+ c_hidden_tri_att:
371
+ Per-head hidden dimension for triangular attention
372
+ c_hidden_tri_att:
373
+ Hidden dimension for triangular multiplication
374
+ no_blocks:
375
+ Number of blocks in the stack
376
+ pair_transition_n:
377
+ Scale of pair transition (Alg. 15) hidden dimension
378
+ dropout_rate:
379
+ Dropout rate used throughout the stack
380
+ blocks_per_ckpt:
381
+ Number of blocks per activation checkpoint. None disables
382
+ activation checkpointing
383
+ """
384
+ super(TemplatePairStack, self).__init__()
385
+
386
+ self.blocks_per_ckpt = blocks_per_ckpt
387
+
388
+ self.blocks = nn.ModuleList()
389
+ for _ in range(no_blocks):
390
+ block = TemplatePairStackBlock(
391
+ c_t=c_t,
392
+ c_hidden_tri_att=c_hidden_tri_att,
393
+ c_hidden_tri_mul=c_hidden_tri_mul,
394
+ no_heads=no_heads,
395
+ pair_transition_n=pair_transition_n,
396
+ dropout_rate=dropout_rate,
397
+ tri_mul_first=tri_mul_first,
398
+ fuse_projection_weights=fuse_projection_weights,
399
+ inf=inf,
400
+ )
401
+ self.blocks.append(block)
402
+
403
+ self.layer_norm = LayerNorm(c_t)
404
+
405
+ self.tune_chunk_size = tune_chunk_size
406
+ self.chunk_size_tuner = None
407
+ if tune_chunk_size:
408
+ self.chunk_size_tuner = ChunkSizeTuner()
409
+
410
+ def forward(
411
+ self,
412
+ t: torch.tensor,
413
+ mask: torch.tensor,
414
+ chunk_size: int,
415
+ use_deepspeed_evo_attention: bool = False,
416
+ use_lma: bool = False,
417
+ inplace_safe: bool = False,
418
+ _mask_trans: bool = True,
419
+ ):
420
+ """
421
+ Args:
422
+ t:
423
+ [*, N_templ, N_res, N_res, C_t] template embedding
424
+ mask:
425
+ [*, N_templ, N_res, N_res] mask
426
+ Returns:
427
+ [*, N_templ, N_res, N_res, C_t] template embedding update
428
+ """
429
+ if mask.shape[-3] == 1:
430
+ expand_idx = list(mask.shape)
431
+ expand_idx[-3] = t.shape[-4]
432
+ mask = mask.expand(*expand_idx)
433
+
434
+ blocks = [
435
+ partial(
436
+ b,
437
+ mask=mask,
438
+ chunk_size=chunk_size,
439
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
440
+ use_lma=use_lma,
441
+ inplace_safe=inplace_safe,
442
+ _mask_trans=_mask_trans,
443
+ )
444
+ for b in self.blocks
445
+ ]
446
+
447
+ if chunk_size is not None and self.chunk_size_tuner is not None:
448
+ assert (not self.training)
449
+ tuned_chunk_size = self.chunk_size_tuner.tune_chunk_size(
450
+ representative_fn=blocks[0],
451
+ args=(t.clone(),),
452
+ min_chunk_size=chunk_size,
453
+ )
454
+ blocks = [
455
+ partial(b,
456
+ chunk_size=tuned_chunk_size,
457
+ _attn_chunk_size=max(chunk_size, tuned_chunk_size // 4),
458
+ ) for b in blocks
459
+ ]
460
+
461
+ t, = checkpoint_blocks(
462
+ blocks=blocks,
463
+ args=(t,),
464
+ blocks_per_ckpt=self.blocks_per_ckpt if self.training else None,
465
+ )
466
+
467
+ t = self.layer_norm(t)
468
+
469
+ return t
470
+
471
+
472
+ def embed_templates_offload(
473
+ model,
474
+ batch,
475
+ z,
476
+ pair_mask,
477
+ templ_dim,
478
+ template_chunk_size=256,
479
+ inplace_safe=False,
480
+ ):
481
+ """
482
+ Args:
483
+ model:
484
+ An AlphaFold model object
485
+ batch:
486
+ An AlphaFold input batch. See documentation of AlphaFold.
487
+ z:
488
+ A [*, N, N, C_z] pair embedding
489
+ pair_mask:
490
+ A [*, N, N] pair mask
491
+ templ_dim:
492
+ The template dimension of the template tensors in batch
493
+ template_chunk_size:
494
+ Integer value controlling how quickly the offloaded pair embedding
495
+ tensor is brought back into GPU memory. In dire straits, can be
496
+ lowered to reduce memory consumption of this function even more.
497
+ Returns:
498
+ A dictionary of template pair and angle embeddings.
499
+
500
+ A version of the "embed_templates" method of the AlphaFold class that
501
+ offloads the large template pair tensor to CPU. Slower but more frugal
502
+ with GPU memory than the original. Useful for long-sequence inference.
503
+ """
504
+ # Embed the templates one at a time (with a poor man's vmap)
505
+ pair_embeds_cpu = []
506
+ n = z.shape[-2]
507
+ n_templ = batch["template_aatype"].shape[templ_dim]
508
+ for i in range(n_templ):
509
+ idx = batch["template_aatype"].new_tensor(i)
510
+ single_template_feats = tensor_tree_map(
511
+ lambda t: torch.index_select(t, templ_dim, idx).squeeze(templ_dim),
512
+ batch,
513
+ )
514
+
515
+ # [*, N, N, C_t]
516
+ t = build_template_pair_feat(
517
+ single_template_feats,
518
+ use_unit_vector=model.config.template.use_unit_vector,
519
+ inf=model.config.template.inf,
520
+ eps=model.config.template.eps,
521
+ **model.config.template.distogram,
522
+ ).to(z.dtype)
523
+ t = model.template_pair_embedder(t)
524
+
525
+ # [*, 1, N, N, C_z]
526
+ t = model.template_pair_stack(
527
+ t.unsqueeze(templ_dim),
528
+ pair_mask.unsqueeze(-3).to(dtype=z.dtype),
529
+ chunk_size=model.globals.chunk_size,
530
+ use_deepspeed_evo_attention=model.globals.use_deepspeed_evo_attention,
531
+ use_lma=model.globals.use_lma,
532
+ inplace_safe=inplace_safe,
533
+ _mask_trans=model.config._mask_trans,
534
+ )
535
+
536
+ assert (sys.getrefcount(t) == 2)
537
+
538
+ pair_embeds_cpu.append(t.cpu())
539
+
540
+ del t
541
+
542
+ # Preallocate the output tensor
543
+ t = z.new_zeros(z.shape)
544
+
545
+ for i in range(0, n, template_chunk_size):
546
+ pair_chunks = [
547
+ p[..., i: i + template_chunk_size, :, :] for p in pair_embeds_cpu
548
+ ]
549
+ pair_chunk = torch.cat(pair_chunks, dim=templ_dim).to(device=z.device)
550
+ z_chunk = z[..., i: i + template_chunk_size, :, :]
551
+ att_chunk = model.template_pointwise_att(
552
+ pair_chunk,
553
+ z_chunk,
554
+ template_mask=batch["template_mask"].to(dtype=z.dtype),
555
+ use_lma=model.globals.use_lma,
556
+ )
557
+
558
+ t[..., i: i + template_chunk_size, :, :] = att_chunk
559
+
560
+ del pair_chunks
561
+
562
+ if inplace_safe:
563
+ t = t * (torch.sum(batch["template_mask"], dim=-1) > 0)
564
+ else:
565
+ t *= (torch.sum(batch["template_mask"], dim=-1) > 0)
566
+
567
+ ret = {}
568
+ if model.config.template.embed_angles:
569
+ template_angle_feat = build_template_angle_feat(
570
+ batch,
571
+ )
572
+
573
+ # [*, N, C_m]
574
+ a = model.template_single_embedder(template_angle_feat)
575
+
576
+ ret["template_single_embedding"] = a
577
+
578
+ ret.update({"template_pair_embedding": t})
579
+
580
+ return ret
581
+
582
+
583
+ def embed_templates_average(
584
+ model,
585
+ batch,
586
+ z,
587
+ pair_mask,
588
+ templ_dim,
589
+ templ_group_size=2,
590
+ inplace_safe=False,
591
+ ):
592
+ """
593
+ Args:
594
+ model:
595
+ An AlphaFold model object
596
+ batch:
597
+ An AlphaFold input batch. See documentation of AlphaFold.
598
+ z:
599
+ A [*, N, N, C_z] pair embedding
600
+ pair_mask:
601
+ A [*, N, N] pair mask
602
+ templ_dim:
603
+ The template dimension of the template tensors in batch
604
+ templ_group_size:
605
+ Granularity of the approximation. Larger values trade memory for
606
+ greater proximity to the original function
607
+ Returns:
608
+ A dictionary of template pair and angle embeddings.
609
+
610
+ A memory-efficient approximation of the "embed_templates" method of the
611
+ AlphaFold class. Instead of running pointwise attention over pair
612
+ embeddings for all of the templates at the same time, it splits templates
613
+ into groups of size templ_group_size, computes embeddings for each group
614
+ normally, and then averages the group embeddings. In our experiments, this
615
+ approximation has a minimal effect on the quality of the resulting
616
+ embedding, while its low memory footprint allows the number of templates
617
+ to scale almost indefinitely.
618
+ """
619
+ # Embed the templates one at a time (with a poor man's vmap)
620
+ n = z.shape[-2]
621
+ n_templ = batch["template_aatype"].shape[templ_dim]
622
+ out_tensor = z.new_zeros(z.shape)
623
+ for i in range(0, n_templ, templ_group_size):
624
+ def slice_template_tensor(t):
625
+ s = [slice(None) for _ in t.shape]
626
+ s[templ_dim] = slice(i, i + templ_group_size)
627
+ return t[s]
628
+
629
+ template_feats = tensor_tree_map(
630
+ slice_template_tensor,
631
+ batch,
632
+ )
633
+
634
+ # [*, N, N, C_t]
635
+ t = build_template_pair_feat(
636
+ template_feats,
637
+ use_unit_vector=model.config.template.use_unit_vector,
638
+ inf=model.config.template.inf,
639
+ eps=model.config.template.eps,
640
+ **model.config.template.distogram,
641
+ ).to(z.dtype)
642
+
643
+ # [*, S_t, N, N, C_z]
644
+ t = model.template_pair_embedder(t)
645
+ t = model.template_pair_stack(
646
+ t,
647
+ pair_mask.unsqueeze(-3).to(dtype=z.dtype),
648
+ chunk_size=model.globals.chunk_size,
649
+ use_deepspeed_evo_attention=model.globals.use_deepspeed_evo_attention,
650
+ use_lma=model.globals.use_lma,
651
+ inplace_safe=inplace_safe,
652
+ _mask_trans=model.config._mask_trans,
653
+ )
654
+
655
+ t = model.template_pointwise_att(
656
+ t,
657
+ z,
658
+ template_mask=template_feats["template_mask"].to(dtype=z.dtype),
659
+ use_lma=model.globals.use_lma,
660
+ )
661
+
662
+ denom = math.ceil(n_templ / templ_group_size)
663
+ if inplace_safe:
664
+ t /= denom
665
+ else:
666
+ t = t / denom
667
+
668
+ if inplace_safe:
669
+ out_tensor += t
670
+ else:
671
+ out_tensor = out_tensor + t
672
+
673
+ del t
674
+
675
+ if inplace_safe:
676
+ out_tensor *= (torch.sum(batch["template_mask"], dim=-1) > 0)
677
+ else:
678
+ out_tensor = out_tensor * (torch.sum(batch["template_mask"], dim=-1) > 0)
679
+
680
+ ret = {}
681
+ if model.config.template.embed_angles:
682
+ template_angle_feat = build_template_angle_feat(
683
+ batch,
684
+ )
685
+
686
+ # [*, N, C_m]
687
+ a = model.template_single_embedder(template_angle_feat)
688
+
689
+ ret["template_single_embedding"] = a
690
+
691
+ ret.update({"template_pair_embedding": out_tensor})
692
+
693
+ return ret
models/openfold/torchscript.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Optional, Sequence, Tuple
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+
20
+ from .openfold.dropout import (
21
+ DropoutRowwise,
22
+ DropoutColumnwise,
23
+ )
24
+ from .openfold.evoformer import (
25
+ EvoformerBlock,
26
+ EvoformerStack,
27
+ )
28
+ from .openfold.outer_product_mean import OuterProductMean
29
+ from .openfold.msa import (
30
+ MSARowAttentionWithPairBias,
31
+ MSAColumnAttention,
32
+ MSAColumnGlobalAttention,
33
+ )
34
+ from .openfold.pair_transition import PairTransition
35
+ from .openfold.primitives import Attention, GlobalAttention
36
+ from .openfold.structure_module import (
37
+ InvariantPointAttention,
38
+ BackboneUpdate,
39
+ )
40
+ from .openfold.template import TemplatePairStackBlock
41
+ from .openfold.triangular_attention import (
42
+ TriangleAttentionStartingNode,
43
+ TriangleAttentionEndingNode,
44
+ )
45
+ from .openfold.triangular_multiplicative_update import (
46
+ TriangleMultiplicationOutgoing,
47
+ TriangleMultiplicationIncoming,
48
+ )
49
+
50
+
51
+ def script_preset_(model: torch.nn.Module):
52
+ """
53
+ TorchScript a handful of low-level but frequently used submodule types
54
+ that are known to be scriptable.
55
+
56
+ Args:
57
+ model:
58
+ A torch.nn.Module. It should contain at least some modules from
59
+ this repository, or this function won't do anything.
60
+ """
61
+ script_submodules_(
62
+ model,
63
+ [
64
+ nn.Dropout,
65
+ Attention,
66
+ GlobalAttention,
67
+ EvoformerBlock,
68
+ #TemplatePairStackBlock,
69
+ ],
70
+ attempt_trace=False,
71
+ batch_dims=None,
72
+ )
73
+
74
+
75
+ def _get_module_device(module: torch.nn.Module) -> torch.device:
76
+ """
77
+ Fetches the device of a module, assuming that all of the module's
78
+ parameters reside on a single device
79
+
80
+ Args:
81
+ module: A torch.nn.Module
82
+ Returns:
83
+ The module's device
84
+ """
85
+ return next(module.parameters()).device
86
+
87
+
88
+ def _trace_module(module, batch_dims=None):
89
+ if(batch_dims is None):
90
+ batch_dims = ()
91
+
92
+ # Stand-in values
93
+ n_seq = 10
94
+ n_res = 10
95
+
96
+ device = _get_module_device(module)
97
+
98
+ def msa(channel_dim):
99
+ return torch.rand(
100
+ (*batch_dims, n_seq, n_res, channel_dim),
101
+ device=device,
102
+ )
103
+
104
+ def pair(channel_dim):
105
+ return torch.rand(
106
+ (*batch_dims, n_res, n_res, channel_dim),
107
+ device=device,
108
+ )
109
+
110
+ if(isinstance(module, MSARowAttentionWithPairBias)):
111
+ inputs = {
112
+ "forward": (
113
+ msa(module.c_in), # m
114
+ pair(module.c_z), # z
115
+ torch.randint(
116
+ 0, 2,
117
+ (*batch_dims, n_seq, n_res)
118
+ ), # mask
119
+ ),
120
+ }
121
+ elif(isinstance(module, MSAColumnAttention)):
122
+ inputs = {
123
+ "forward": (
124
+ msa(module.c_in), # m
125
+ torch.randint(
126
+ 0, 2,
127
+ (*batch_dims, n_seq, n_res)
128
+ ), # mask
129
+ ),
130
+ }
131
+ elif(isinstance(module, OuterProductMean)):
132
+ inputs = {
133
+ "forward": (
134
+ msa(module.c_m),
135
+ torch.randint(
136
+ 0, 2,
137
+ (*batch_dims, n_seq, n_res)
138
+ )
139
+ )
140
+ }
141
+ else:
142
+ raise TypeError(
143
+ f"tracing is not supported for modules of type {type(module)}"
144
+ )
145
+
146
+ return torch.jit.trace_module(module, inputs)
147
+
148
+
149
+ def _script_submodules_helper_(
150
+ model,
151
+ types,
152
+ attempt_trace,
153
+ to_trace,
154
+ ):
155
+ for name, child in model.named_children():
156
+ if(types is None or any(isinstance(child, t) for t in types)):
157
+ try:
158
+ scripted = torch.jit.script(child)
159
+ setattr(model, name, scripted)
160
+ continue
161
+ except (RuntimeError, torch.jit.frontend.NotSupportedError) as e:
162
+ if(attempt_trace):
163
+ to_trace.add(type(child))
164
+ else:
165
+ raise e
166
+
167
+ _script_submodules_helper_(child, types, attempt_trace, to_trace)
168
+
169
+
170
+ def _trace_submodules_(
171
+ model,
172
+ types,
173
+ batch_dims=None,
174
+ ):
175
+ for name, child in model.named_children():
176
+ if(any(isinstance(child, t) for t in types)):
177
+ traced = _trace_module(child, batch_dims=batch_dims)
178
+ setattr(model, name, traced)
179
+ else:
180
+ _trace_submodules_(child, types, batch_dims=batch_dims)
181
+
182
+
183
+ def script_submodules_(
184
+ model: nn.Module,
185
+ types: Optional[Sequence[type]] = None,
186
+ attempt_trace: Optional[bool] = True,
187
+ batch_dims: Optional[Tuple[int]] = None,
188
+ ):
189
+ """
190
+ Convert all submodules whose types match one of those in the input
191
+ list to recursively scripted equivalents in place. To script the entire
192
+ model, just call torch.jit.script on it directly.
193
+
194
+ When types is None, all submodules are scripted.
195
+
196
+ Args:
197
+ model:
198
+ A torch.nn.Module
199
+ types:
200
+ A list of types of submodules to script
201
+ attempt_trace:
202
+ Whether to attempt to trace specified modules if scripting
203
+ fails. Recall that tracing eliminates all conditional
204
+ logic---with great tracing comes the mild responsibility of
205
+ having to remember to ensure that the modules in question
206
+ perform the same computations no matter what.
207
+ """
208
+ to_trace = set()
209
+
210
+ # Aggressively script as much as possible first...
211
+ _script_submodules_helper_(model, types, attempt_trace, to_trace)
212
+
213
+ # ... and then trace stragglers.
214
+ if(attempt_trace and len(to_trace) > 0):
215
+ _trace_submodules_(model, to_trace, batch_dims=batch_dims)
models/openfold/triangular_attention.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from functools import partialmethod, partial
17
+ import math
18
+ from typing import Optional, List
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+
23
+ from .openfold.primitives import Linear, LayerNorm, Attention
24
+ from onescience.utils.openfold.chunk_utils import chunk_layer
25
+ from onescience.utils.openfold.tensor_utils import (
26
+ permute_final_dims,
27
+ flatten_final_dims,
28
+ )
29
+
30
+
31
+ class TriangleAttention(nn.Module):
32
+ def __init__(
33
+ self, c_in, c_hidden, no_heads, starting=True, inf=1e9, bias: bool=True
34
+ ):
35
+ """
36
+ Args:
37
+ c_in:
38
+ Input channel dimension
39
+ c_hidden:
40
+ Overall hidden channel dimension (not per-head)
41
+ no_heads:
42
+ Number of attention heads
43
+ """
44
+ super(TriangleAttention, self).__init__()
45
+
46
+ self.c_in = c_in
47
+ self.c_hidden = c_hidden
48
+ self.no_heads = no_heads
49
+ self.starting = starting
50
+ self.inf = inf
51
+
52
+ self.layer_norm = LayerNorm(self.c_in)
53
+
54
+ self.linear = Linear(c_in, self.no_heads, bias=False, init="normal")
55
+ if bias==False:
56
+ self.mha = Attention(
57
+ self.c_in, self.c_in, self.c_in, self.c_hidden, self.no_heads, bias=False
58
+ )
59
+ else:
60
+ self.mha = Attention(
61
+ self.c_in, self.c_in, self.c_in, self.c_hidden, self.no_heads
62
+ )
63
+
64
+ @torch.jit.ignore
65
+ def _chunk(self,
66
+ x: torch.Tensor,
67
+ biases: List[torch.Tensor],
68
+ chunk_size: int,
69
+ use_memory_efficient_kernel: bool = False,
70
+ use_deepspeed_evo_attention: bool = False,
71
+ use_lma: bool = False,
72
+ inplace_safe: bool = False,
73
+ ) -> torch.Tensor:
74
+ "triangle! triangle!"
75
+ mha_inputs = {
76
+ "q_x": x,
77
+ "kv_x": x,
78
+ "biases": biases,
79
+ }
80
+
81
+ return chunk_layer(
82
+ partial(
83
+ self.mha,
84
+ use_memory_efficient_kernel=use_memory_efficient_kernel,
85
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
86
+ use_lma=use_lma
87
+ ),
88
+ mha_inputs,
89
+ chunk_size=chunk_size,
90
+ no_batch_dims=len(x.shape[:-2]),
91
+ _out=x if inplace_safe else None,
92
+ )
93
+
94
+ def forward(self,
95
+ x: torch.Tensor,
96
+ mask: Optional[torch.Tensor] = None,
97
+ chunk_size: Optional[int] = None,
98
+ use_memory_efficient_kernel: bool = False,
99
+ use_deepspeed_evo_attention: bool = False,
100
+ use_lma: bool = False,
101
+ inplace_safe: bool = False,
102
+ ) -> torch.Tensor:
103
+ """
104
+ Args:
105
+ x:
106
+ [*, I, J, C_in] input tensor (e.g. the pair representation)
107
+ Returns:
108
+ [*, I, J, C_in] output tensor
109
+ """
110
+ if mask is None:
111
+ # [*, I, J]
112
+ mask = x.new_ones(
113
+ x.shape[:-1],
114
+ )
115
+
116
+ if(not self.starting):
117
+ x = x.transpose(-2, -3)
118
+ mask = mask.transpose(-1, -2)
119
+
120
+ # [*, I, J, C_in]
121
+ x = self.layer_norm(x)
122
+
123
+ # [*, I, 1, 1, J]
124
+ mask_bias = (self.inf * (mask - 1))[..., :, None, None, :]
125
+
126
+ # [*, H, I, J]
127
+ triangle_bias = permute_final_dims(self.linear(x), (2, 0, 1))
128
+
129
+ # [*, 1, H, I, J]
130
+ triangle_bias = triangle_bias.unsqueeze(-4)
131
+
132
+ biases = [mask_bias, triangle_bias]
133
+
134
+ if chunk_size is not None:
135
+ x = self._chunk(
136
+ x,
137
+ biases,
138
+ chunk_size,
139
+ use_memory_efficient_kernel=use_memory_efficient_kernel,
140
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
141
+ use_lma=use_lma,
142
+ inplace_safe=inplace_safe,
143
+ )
144
+ else:
145
+ x = self.mha(
146
+ q_x=x,
147
+ kv_x=x,
148
+ biases=biases,
149
+ use_memory_efficient_kernel=use_memory_efficient_kernel,
150
+ use_deepspeed_evo_attention=use_deepspeed_evo_attention,
151
+ use_lma=use_lma
152
+ )
153
+
154
+ if(not self.starting):
155
+ x = x.transpose(-2, -3)
156
+
157
+ return x
158
+
159
+
160
+ # Implements Algorithm 13
161
+ TriangleAttentionStartingNode = TriangleAttention
162
+
163
+
164
+ class TriangleAttentionEndingNode(TriangleAttention):
165
+ """
166
+ Implements Algorithm 14.
167
+ """
168
+ __init__ = partialmethod(TriangleAttention.__init__, starting=False)
models/openfold/triangular_multiplicative_update.py ADDED
@@ -0,0 +1,647 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2021 AlQuraishi Laboratory
2
+ # Copyright 2021 DeepMind Technologies Limited
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from functools import partialmethod
17
+ from typing import Optional
18
+ from abc import ABC, abstractmethod
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+
23
+ from .openfold.primitives import Linear, LayerNorm
24
+ from onescience.utils.openfold.chunk_utils import chunk_layer
25
+ from onescience.utils.openfold.precision_utils import is_fp16_enabled
26
+ from onescience.utils.openfold.tensor_utils import add, permute_final_dims
27
+
28
+
29
+ class BaseTriangleMultiplicativeUpdate(nn.Module, ABC):
30
+ """
31
+ Implements Algorithms 11 and 12.
32
+ """
33
+ @abstractmethod
34
+ def __init__(self, c_z, c_hidden, _outgoing, bias=True):
35
+ """
36
+ Args:
37
+ c_z:
38
+ Input channel dimension
39
+ c:
40
+ Hidden channel dimension
41
+ """
42
+ super(BaseTriangleMultiplicativeUpdate, self).__init__()
43
+ self.c_z = c_z
44
+ self.c_hidden = c_hidden
45
+ self._outgoing = _outgoing
46
+ self.bias = bias
47
+
48
+ self.linear_g = Linear(self.c_z, self.c_z, bias=bias, init="gating")
49
+ self.linear_z = Linear(self.c_hidden, self.c_z, bias=bias, init="final")
50
+
51
+ self.layer_norm_in = LayerNorm(self.c_z)
52
+ self.layer_norm_out = LayerNorm(self.c_hidden)
53
+
54
+ self.sigmoid = nn.Sigmoid()
55
+
56
+ def _combine_projections(self,
57
+ a: torch.Tensor,
58
+ b: torch.Tensor,
59
+ _inplace_chunk_size: Optional[int] = None
60
+ ) -> torch.Tensor:
61
+ if(self._outgoing):
62
+ a = permute_final_dims(a, (2, 0, 1))
63
+ b = permute_final_dims(b, (2, 1, 0))
64
+ else:
65
+ a = permute_final_dims(a, (2, 1, 0))
66
+ b = permute_final_dims(b, (2, 0, 1))
67
+
68
+ if(_inplace_chunk_size is not None):
69
+ # To be replaced by torch vmap
70
+ for i in range(0, a.shape[-3], _inplace_chunk_size):
71
+ a_chunk = a[..., i: i + _inplace_chunk_size, :, :]
72
+ b_chunk = b[..., i: i + _inplace_chunk_size, :, :]
73
+ a[..., i: i + _inplace_chunk_size, :, :] = (
74
+ torch.matmul(
75
+ a_chunk,
76
+ b_chunk,
77
+ )
78
+ )
79
+
80
+ p = a
81
+ else:
82
+ p = torch.matmul(a, b)
83
+
84
+ return permute_final_dims(p, (1, 2, 0))
85
+
86
+ @abstractmethod
87
+ def forward(self,
88
+ z: torch.Tensor,
89
+ mask: Optional[torch.Tensor] = None,
90
+ inplace_safe: bool = False,
91
+ _add_with_inplace: bool = False
92
+ ) -> torch.Tensor:
93
+ """
94
+ Args:
95
+ x:
96
+ [*, N_res, N_res, C_z] input tensor
97
+ mask:
98
+ [*, N_res, N_res] input mask
99
+ Returns:
100
+ [*, N_res, N_res, C_z] output tensor
101
+ """
102
+ pass
103
+
104
+
105
+ class TriangleMultiplicativeUpdate(BaseTriangleMultiplicativeUpdate):
106
+ """
107
+ Implements Algorithms 11 and 12.
108
+ """
109
+ def __init__(self, c_z, c_hidden, _outgoing=True, bias: bool=True):
110
+ """
111
+ Args:
112
+ c_z:
113
+ Input channel dimension
114
+ c:
115
+ Hidden channel dimension
116
+ """
117
+ super(TriangleMultiplicativeUpdate, self).__init__(c_z=c_z,
118
+ c_hidden=c_hidden,
119
+ _outgoing=_outgoing,
120
+ bias=bias
121
+ )
122
+
123
+ self.linear_a_p = Linear(self.c_z, self.c_hidden, bias=bias)
124
+ self.linear_a_g = Linear(self.c_z, self.c_hidden, bias=bias, init="gating")
125
+ self.linear_b_p = Linear(self.c_z, self.c_hidden, bias=bias,)
126
+ self.linear_b_g = Linear(self.c_z, self.c_hidden, bias=bias, init="gating")
127
+
128
+ def _inference_forward(self,
129
+ z: torch.Tensor,
130
+ mask: Optional[torch.Tensor] = None,
131
+ inplace_chunk_size: Optional[int] = None,
132
+ with_add: bool = True,
133
+ ):
134
+ """
135
+ Args:
136
+ z:
137
+ A [*, N, N, C_z] pair representation
138
+ mask:
139
+ A [*, N, N] pair mask
140
+ inplace_chunk_size:
141
+ Size of chunks used in the main computation. Increase to trade
142
+ memory for speed.
143
+ with_add:
144
+ If True, z is overwritten with (z + update). Otherwise, it is
145
+ overwritten with (update).
146
+ Returns:
147
+ A reference to the overwritten z
148
+
149
+ More memory-efficient, inference-only version of the forward function.
150
+ Uses in-place operations, fusion of the addition that happens after
151
+ this module in the Evoformer, a smidge of recomputation, and
152
+ a cache of overwritten values to lower peak memory consumption of this
153
+ module from 5x the size of the input tensor z to 2.5x its size. Useful
154
+ for inference on extremely long sequences.
155
+
156
+ It works as follows. We will make reference to variables used in the
157
+ default forward implementation below. Naively, triangle multiplication
158
+ attention requires the manifestation of 5 tensors the size of z:
159
+ 1) z, the "square" input tensor, 2) a, the first projection of z,
160
+ 3) b, the second projection of b, 4) g, a z-sized mask, and 5) a
161
+ z-sized tensor for intermediate computations. For large N, this is
162
+ prohibitively expensive; for N=4000, for example, z is more than 8GB
163
+ alone. To avoid this problem, we compute b, g, and all intermediate
164
+ tensors in small chunks, noting that the chunks required to compute a
165
+ chunk of the output depend only on the tensor a and corresponding
166
+ vertical and horizontal chunks of z. This suggests an algorithm that
167
+ loops over pairs of chunks of z: hereafter "columns" and "rows" of
168
+ z, even though each "column" and "row" in fact contains
169
+ inplace_chunk_size contiguous true columns and rows of z. Writing
170
+ output chunks to a new tensor would bring total memory consumption
171
+ down to 3x the size of z. However, more memory can be saved by writing
172
+ output chunks directly to z in-place. WLOG, we choose to write output
173
+ chunks vertically, overwriting the ith "column" of z at the end of
174
+ the ith iteration of the main loop. Despite this overwriting, the
175
+ ith column is always one column ahead of previously overwritten columns
176
+ and can be recovered directly from z. After the first iteration,
177
+ however, the ith row of z is always at least partially overwritten. For
178
+ this reason, we introduce the z-cache, a tensor one-half the size of
179
+ z. The z-cache initially contains the left half (2nd and 3rd quadrants)
180
+ of z. For 0 < i < N/2, the missing left part of the ith row of z is
181
+ recovered from this cache at the beginning of the ith iteration. Once i
182
+ exceeds n/2, the cache is "reoriented" to encompass the 3rd and 4th
183
+ quadrants of z instead. Though the 3rd quadrant of the original z is
184
+ entirely overwritten at this point, it can be recovered from the z-cache
185
+ itself. Thereafter, the ith row of z can be recovered in its entirety
186
+ from the reoriented z-cache. After the final iteration, z has been
187
+ completely overwritten and contains the triangular multiplicative
188
+ update. If with_add is True, it instead contains the sum of z and the
189
+ triangular multiplicative update. In either case, peak memory
190
+ consumption is just 2.5x the size of z, disregarding memory used for
191
+ chunks and other small variables.
192
+ """
193
+ if mask is None:
194
+ mask = z.new_ones(z.shape[:-1])
195
+
196
+ mask = mask.unsqueeze(-1)
197
+
198
+ def compute_projection_helper(pair, mask, a=True):
199
+ if(a):
200
+ linear_g = self.linear_a_g
201
+ linear_p = self.linear_a_p
202
+ else:
203
+ linear_g = self.linear_b_g
204
+ linear_p = self.linear_b_p
205
+
206
+ pair = self.layer_norm_in(pair)
207
+ p = linear_g(pair)
208
+ p.sigmoid_()
209
+ p *= linear_p(pair)
210
+ p *= mask
211
+ p = permute_final_dims(p, (2, 0, 1))
212
+ return p
213
+
214
+ def compute_projection(pair, mask, a=True, chunked=True):
215
+ need_transpose = self._outgoing ^ a
216
+ if(not chunked):
217
+ p = compute_projection_helper(pair, mask, a)
218
+ if(need_transpose):
219
+ p = p.transpose(-1, -2)
220
+ else:
221
+ # This computation is chunked so as not to exceed our 2.5x
222
+ # budget with a large intermediate tensor
223
+ linear_g = self.linear_a_g if a else self.linear_b_g
224
+ #c = linear_g.bias.shape[-1]
225
+ if self.bias:
226
+ c = linear_g.bias.shape[-1]
227
+ else:
228
+ c = linear_g.weight.shape[0]
229
+ out_shape = pair.shape[:-3] + (c,) + pair.shape[-3:-1]
230
+ p = pair.new_zeros(out_shape)
231
+ for i in range(0, pair.shape[-3], inplace_chunk_size):
232
+ pair_chunk = pair[..., i: i + inplace_chunk_size, :, :]
233
+ mask_chunk = mask[..., i: i + inplace_chunk_size, :, :]
234
+ pair_chunk = compute_projection_helper(
235
+ pair[..., i: i + inplace_chunk_size, :, :],
236
+ mask[..., i: i + inplace_chunk_size, :, :],
237
+ a,
238
+ )
239
+ if(need_transpose):
240
+ pair_chunk = pair_chunk.transpose(-1, -2)
241
+ p[..., i: i + inplace_chunk_size] = pair_chunk
242
+ else:
243
+ p[..., i: i + inplace_chunk_size, :] = pair_chunk
244
+
245
+ del pair_chunk
246
+
247
+ return p
248
+
249
+ # We start by fully manifesting a. In addition to the input, this
250
+ # brings total memory consumption to 2x z (disregarding size of chunks)
251
+ # [*, N, N, c]
252
+ a = compute_projection(z, mask, True, chunked=True)
253
+ #if bias==True:
254
+ # a = compute_projection(z, mask, True, True, chunked=True)
255
+ #else:
256
+ # a = compute_projection(z, mask, False, True, chunked=True)
257
+
258
+ if(inplace_chunk_size is not None):
259
+ n = a.shape[-1]
260
+ half_n = n // 2 + n % 2
261
+ row_dim = -3
262
+ col_dim = -2
263
+ b_chunk_dim = row_dim if self._outgoing else col_dim
264
+
265
+ def empty_slicer(t):
266
+ return [slice(None) for _ in t.shape]
267
+
268
+ def slice_tensor(t, start, end, dim):
269
+ # Slices start:end from the dim dimension of t
270
+ s = empty_slicer(t)
271
+ s[dim] = slice(start, end)
272
+ return t[s]
273
+
274
+ def flip_z_cache_(z_cache, z):
275
+ # "Reorient" the z_cache (see below), filling it with quadrants
276
+ # 3---recovered from the z_cache---and 4---recovered from z---
277
+ # of the input tensor z.
278
+ quadrant_3 = slice_tensor(
279
+ z_cache, half_n, None, row_dim
280
+ )
281
+ z_cache = z_cache.transpose(row_dim, col_dim)
282
+
283
+ # If n is odd, we need to shrink the z_cache by one row
284
+ z_cache = z_cache[..., :(n // 2), :, :]
285
+
286
+ # Move the 3rd quadrant of z into the
287
+ first_half_slicer = empty_slicer(z_cache)
288
+ first_half_slicer[col_dim] = slice(0, half_n)
289
+ z_cache[first_half_slicer] = quadrant_3
290
+
291
+ # Get the fourth quadrant of z
292
+ quadrant_4 = slice_tensor(z, half_n, None, row_dim)
293
+ quadrant_4 = slice_tensor(
294
+ quadrant_4, half_n, None, col_dim
295
+ )
296
+
297
+ # Insert said quadrant into the rotated z-cache
298
+ quadrant_3_slicer = empty_slicer(z_cache)
299
+ quadrant_3_slicer[col_dim] = slice(half_n, None)
300
+
301
+ z_cache[quadrant_3_slicer] = quadrant_4
302
+
303
+ return z_cache
304
+
305
+ # Initialize the z cache to the left half of z.
306
+ z_cache_shape = list(z.shape)
307
+ z_cache_shape[col_dim] = half_n
308
+ z_cache = z.new_zeros(z_cache_shape)
309
+ z_cache_slicer = empty_slicer(z_cache)
310
+ z_cache_slicer[col_dim] = slice(0, half_n)
311
+ z_cache.copy_(z[z_cache_slicer])
312
+ z_cache_rotated = False
313
+
314
+ # We need to reorient the z-cache at the halfway point, and we
315
+ # don't want a single chunk to straddle that point. We contract one
316
+ # of the chunks in the middle to address that problem.
317
+ i_range = list(range(0, half_n, inplace_chunk_size))
318
+ initial_offsets = [
319
+ i_2 - i_1 for i_1, i_2 in zip(i_range, i_range[1:] + [half_n])
320
+ ]
321
+ after_half = list(range(half_n, n, inplace_chunk_size))
322
+ after_half_offsets = [inplace_chunk_size for _ in after_half]
323
+ combined_range_with_offsets = zip(
324
+ i_range + after_half, initial_offsets + after_half_offsets
325
+ )
326
+ for i, offset in combined_range_with_offsets:
327
+ if(not z_cache_rotated and i >= half_n):
328
+ z_cache = flip_z_cache_(z_cache, z)
329
+ z_cache_rotated = True
330
+
331
+ z_chunk_b = slice_tensor(
332
+ z, i, i + offset, b_chunk_dim,
333
+ )
334
+ mask_chunk = slice_tensor(
335
+ mask, i, i + offset, b_chunk_dim,
336
+ )
337
+
338
+ z_chunk_b = z_chunk_b.clone()
339
+ if(b_chunk_dim == col_dim):
340
+ z_chunk_b = slice_tensor(
341
+ z, i, i + offset, col_dim
342
+ )
343
+ else: # b_chunk_dim == row_dim
344
+ # In this case, the b-dimension (b_chunk_dim) is partially
345
+ # overwritten at the end of each iteration. We need to
346
+ # restore the missing component from the z-cache.
347
+ if(not z_cache_rotated):
348
+ z_chunk_slicer = empty_slicer(z_chunk_b)
349
+ z_chunk_slicer[col_dim] = slice(0, half_n)
350
+ z_chunk_b[z_chunk_slicer] = slice_tensor(
351
+ z_cache, i, i + offset, row_dim,
352
+ )
353
+ else:
354
+ z_cache_offset = i - half_n
355
+ z_chunk_b = slice_tensor(
356
+ z_cache,
357
+ z_cache_offset, z_cache_offset + offset,
358
+ row_dim
359
+ )
360
+ b_chunk = compute_projection(z_chunk_b, mask_chunk, a=False, chunked=False)
361
+ #if bias==True:
362
+ # b_chunk = compute_projection(
363
+ # z_chunk_b, mask_chunk, True, a=False, chunked=False
364
+ # )
365
+ #else:
366
+ # b_chunk = compute_projection(z_chunk_b, mask_chunk, False, a=False, chunked=False)
367
+ del z_chunk_b
368
+
369
+ x_chunk = torch.matmul(
370
+ a,
371
+ b_chunk,
372
+ )
373
+ x_chunk = permute_final_dims(x_chunk, (1, 2, 0))
374
+ x_chunk = self.layer_norm_out(x_chunk)
375
+ x_chunk = self.linear_z(x_chunk)
376
+
377
+ # The g dimension (col_dim) is parallel to and ahead of the
378
+ # overwrites in z. We can extract the g chunk normally.
379
+ z_chunk_g = slice_tensor(
380
+ z, i, i + offset, col_dim
381
+ )
382
+ g_chunk = self.linear_g(self.layer_norm_in(z_chunk_g))
383
+ g_chunk.sigmoid_()
384
+ del z_chunk_g
385
+
386
+ x_chunk *= g_chunk
387
+
388
+ # Write the columns into z in-place
389
+ z_slicer = empty_slicer(z)
390
+ z_slicer[col_dim] = slice(i, i + offset)
391
+ if(with_add):
392
+ z[z_slicer] += x_chunk
393
+ else:
394
+ z[z_slicer] = x_chunk
395
+ else:
396
+ b = compute_projection(z, mask, False, False)
397
+ #if bias==True:
398
+ # b = compute_projection(z, mask, True, False, False)
399
+ #else:
400
+ # b = compute_projection(z, mask, False, False, False)
401
+ x = torch.matmul(a, b)
402
+ x = self.layer_norm_out(x)
403
+ x = self.linear_z(x)
404
+ g = self.linear_g(z)
405
+ g.sigmoid_()
406
+ x *= g
407
+ if(with_add):
408
+ z += x
409
+ else:
410
+ z = x
411
+
412
+ return z
413
+
414
+ def forward(self,
415
+ z: torch.Tensor,
416
+ mask: Optional[torch.Tensor] = None,
417
+ inplace_safe: bool = False,
418
+ _add_with_inplace: bool = False,
419
+ _inplace_chunk_size: Optional[int] = 256,
420
+ ) -> torch.Tensor:
421
+ """
422
+ Args:
423
+ x:
424
+ [*, N_res, N_res, C_z] input tensor
425
+ mask:
426
+ [*, N_res, N_res] input mask
427
+ Returns:
428
+ [*, N_res, N_res, C_z] output tensor
429
+ """
430
+ if(inplace_safe):
431
+ x = self._inference_forward(
432
+ z,
433
+ mask,
434
+ inplace_chunk_size=_inplace_chunk_size,
435
+ with_add=_add_with_inplace,
436
+ )
437
+ return x
438
+
439
+ if mask is None:
440
+ mask = z.new_ones(z.shape[:-1])
441
+
442
+ mask = mask.unsqueeze(-1)
443
+
444
+ z = self.layer_norm_in(z)
445
+ a = mask
446
+ a = a * self.sigmoid(self.linear_a_g(z))
447
+ a = a * self.linear_a_p(z)
448
+ b = mask
449
+ b = b * self.sigmoid(self.linear_b_g(z))
450
+ b = b * self.linear_b_p(z)
451
+
452
+ # Prevents overflow of torch.matmul in combine projections in
453
+ # reduced-precision modes
454
+ a_std = a.std()
455
+ b_std = b.std()
456
+ if(is_fp16_enabled() and a_std != 0. and b_std != 0.):
457
+ a = a / a.std()
458
+ b = b / b.std()
459
+
460
+ if(is_fp16_enabled()):
461
+ with torch.cuda.amp.autocast(enabled=False):
462
+ x = self._combine_projections(a.float(), b.float())
463
+ else:
464
+ x = self._combine_projections(a, b)
465
+
466
+ del a, b
467
+ x = self.layer_norm_out(x)
468
+ x = self.linear_z(x)
469
+ g = self.sigmoid(self.linear_g(z))
470
+ x = x * g
471
+
472
+ return x
473
+
474
+
475
+ class TriangleMultiplicationOutgoing(TriangleMultiplicativeUpdate):
476
+ """
477
+ Implements Algorithm 11.
478
+ """
479
+ __init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=True)
480
+
481
+
482
+ class TriangleMultiplicationIncoming(TriangleMultiplicativeUpdate):
483
+ """
484
+ Implements Algorithm 12.
485
+ """
486
+ __init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=False)
487
+
488
+ class ProtenixTriangleMultiplicationOutgoing(TriangleMultiplicativeUpdate):
489
+ """
490
+ Implements Algorithm 11.
491
+ """
492
+ __init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=True, bias=False)
493
+
494
+ class ProtenixTriangleMultiplicationIncoming(TriangleMultiplicativeUpdate):
495
+ """
496
+ Implements Algorithm 12.
497
+ """
498
+ __init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=False, bias=False)
499
+
500
+
501
+ class FusedTriangleMultiplicativeUpdate(BaseTriangleMultiplicativeUpdate):
502
+ """
503
+ Implements Algorithms 11 and 12.
504
+ """
505
+
506
+ def __init__(self, c_z, c_hidden, _outgoing=True):
507
+ """
508
+ Args:
509
+ c_z:
510
+ Input channel dimension
511
+ c:
512
+ Hidden channel dimension
513
+ """
514
+ super(FusedTriangleMultiplicativeUpdate, self).__init__(c_z=c_z,
515
+ c_hidden=c_hidden,
516
+ _outgoing=_outgoing)
517
+
518
+ self.linear_ab_p = Linear(self.c_z, self.c_hidden * 2)
519
+ self.linear_ab_g = Linear(self.c_z, self.c_hidden * 2, init="gating")
520
+
521
+ def _inference_forward(self,
522
+ z: torch.Tensor,
523
+ mask: Optional[torch.Tensor] = None,
524
+ _inplace_chunk_size: Optional[int] = None,
525
+ with_add: bool = True,
526
+ ):
527
+ """
528
+ Args:
529
+ z:
530
+ A [*, N, N, C_z] pair representation
531
+ mask:
532
+ A [*, N, N] pair mask
533
+ with_add:
534
+ If True, z is overwritten with (z + update). Otherwise, it is
535
+ overwritten with (update).
536
+ Returns:
537
+ A reference to the overwritten z
538
+ """
539
+ if mask is None:
540
+ mask = z.new_ones(z.shape[:-1])
541
+
542
+ mask = mask.unsqueeze(-1)
543
+
544
+ def compute_projection_helper(pair, mask):
545
+ p = self.linear_ab_g(pair)
546
+ p.sigmoid_()
547
+ p *= self.linear_ab_p(pair)
548
+ p *= mask
549
+
550
+ return p
551
+
552
+ def compute_projection(pair, mask):
553
+ p = compute_projection_helper(pair, mask)
554
+ left = p[..., :self.c_hidden]
555
+ right = p[..., self.c_hidden:]
556
+
557
+ return left, right
558
+
559
+ z_norm_in = self.layer_norm_in(z)
560
+ a, b = compute_projection(z_norm_in, mask)
561
+ x = self._combine_projections(a, b, _inplace_chunk_size=_inplace_chunk_size)
562
+ x = self.layer_norm_out(x)
563
+ x = self.linear_z(x)
564
+ g = self.linear_g(z_norm_in)
565
+ g.sigmoid_()
566
+ x *= g
567
+ if (with_add):
568
+ z += x
569
+ else:
570
+ z = x
571
+
572
+ return z
573
+
574
+ def forward(self,
575
+ z: torch.Tensor,
576
+ mask: Optional[torch.Tensor] = None,
577
+ inplace_safe: bool = False,
578
+ _add_with_inplace: bool = False,
579
+ _inplace_chunk_size: Optional[int] = 256
580
+ ) -> torch.Tensor:
581
+ """
582
+ Args:
583
+ x:
584
+ [*, N_res, N_res, C_z] input tensor
585
+ mask:
586
+ [*, N_res, N_res] input mask
587
+ Returns:
588
+ [*, N_res, N_res, C_z] output tensor
589
+ """
590
+ if (inplace_safe):
591
+ x = self._inference_forward(
592
+ z,
593
+ mask,
594
+ _inplace_chunk_size=_inplace_chunk_size,
595
+ with_add=_add_with_inplace,
596
+ )
597
+ return x
598
+
599
+ if mask is None:
600
+ mask = z.new_ones(z.shape[:-1])
601
+
602
+ mask = mask.unsqueeze(-1)
603
+
604
+ z = self.layer_norm_in(z)
605
+ ab = mask
606
+ ab = ab * self.sigmoid(self.linear_ab_g(z))
607
+ ab = ab * self.linear_ab_p(z)
608
+
609
+ a = ab[..., :self.c_hidden]
610
+ b = ab[..., self.c_hidden:]
611
+
612
+ # Prevents overflow of torch.matmul in combine projections in
613
+ # reduced-precision modes
614
+ a_std = a.std()
615
+ b_std = b.std()
616
+ if (is_fp16_enabled() and a_std != 0. and b_std != 0.):
617
+ a = a / a.std()
618
+ b = b / b.std()
619
+
620
+ if (is_fp16_enabled()):
621
+ with torch.cuda.amp.autocast(enabled=False):
622
+ x = self._combine_projections(a.float(), b.float())
623
+ else:
624
+ x = self._combine_projections(a, b)
625
+
626
+ del a, b
627
+ x = self.layer_norm_out(x)
628
+ x = self.linear_z(x)
629
+ g = self.sigmoid(self.linear_g(z))
630
+ x = x * g
631
+
632
+ return x
633
+
634
+
635
+ class FusedTriangleMultiplicationOutgoing(FusedTriangleMultiplicativeUpdate):
636
+ """
637
+ Implements Algorithm 11.
638
+ """
639
+ __init__ = partialmethod(FusedTriangleMultiplicativeUpdate.__init__, _outgoing=True)
640
+
641
+
642
+ class FusedTriangleMultiplicationIncoming(FusedTriangleMultiplicativeUpdate):
643
+ """
644
+ Implements Algorithm 12.
645
+ """
646
+ __init__ = partialmethod(FusedTriangleMultiplicativeUpdate.__init__, _outgoing=False)
647
+
weight/.gitkeep ADDED
File without changes
weight/esm_models/README.md ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ This directory should contain the local ESM-2 files used by SimpleFold:
2
+
3
+ - esm2_t36_3B_UR50D.pt
4
+ - esm2_t36_3B_UR50D-contact-regression.pt
5
+
6
+ Remote ESM download is disabled for this packaged runtime.