OneScience commited on
Commit
d149fb1
·
verified ·
1 Parent(s): a0cb272

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +39 -9
  2. LICENSE.txt +202 -0
  3. README.md +203 -0
  4. config/__init__.py +1 -0
  5. config/config.py +956 -0
  6. config/config.yaml +25 -0
  7. config/deepspeed_config.json +27 -0
  8. configuration.json +16 -0
  9. data/demo/monomer/alignments/6KWC_1/bfd_uniref_hits.a3m +0 -0
  10. data/demo/monomer/alignments/6KWC_1/hhsearch_output.hhr +0 -0
  11. data/demo/monomer/alignments/6KWC_1/mgnify_hits.sto +0 -0
  12. data/demo/monomer/alignments/6KWC_1/uniref90_hits.sto +0 -0
  13. data/demo/monomer/fasta_dir/6kwc.fasta +2 -0
  14. data/demo/monomer/inference.sh +20 -0
  15. model/openfold/__init__.py +1 -0
  16. model/openfold/dropout.py +78 -0
  17. model/openfold/embedders.py +984 -0
  18. model/openfold/evoformer.py +1219 -0
  19. model/openfold/heads.py +267 -0
  20. model/openfold/model.py +591 -0
  21. model/openfold/msa.py +476 -0
  22. model/openfold/outer_product_mean.py +159 -0
  23. model/openfold/pair_transition.py +99 -0
  24. model/openfold/primitives.py +902 -0
  25. model/openfold/structure_module.py +1252 -0
  26. model/openfold/template.py +693 -0
  27. model/openfold/torchscript.py +215 -0
  28. model/openfold/triangular_attention.py +168 -0
  29. model/openfold/triangular_multiplicative_update.py +647 -0
  30. scripts/__init__.py +0 -0
  31. scripts/activate_conda_env.sh +6 -0
  32. scripts/alignment_data_to_fasta.py +144 -0
  33. scripts/alignment_db_scripts/add_non_unique_to_alignment_db.py +82 -0
  34. scripts/alignment_db_scripts/create_alignment_db.py +47 -0
  35. scripts/alignment_db_scripts/create_alignment_db_sharded.py +244 -0
  36. scripts/alignment_db_scripts/unify_alignment_db_indices.py +37 -0
  37. scripts/build_deepspeed_config.py +315 -0
  38. scripts/colabfold_search.sh +75 -0
  39. scripts/convert_of_weights_to_jax.py +104 -0
  40. scripts/convert_v1_to_v2_weights.py +97 -0
  41. scripts/data_dir_to_fasta.py +89 -0
  42. scripts/deactivate_conda_env.sh +3 -0
  43. scripts/deepspeed_inference_test.py +54 -0
  44. scripts/download_alphafold_dbs.sh +71 -0
  45. scripts/download_alphafold_params.sh +41 -0
  46. scripts/download_bfd.sh +43 -0
  47. scripts/download_cameo.py +103 -0
  48. scripts/download_colabfold_envdb.sh +38 -0
  49. scripts/download_mgnify.sh +41 -0
  50. scripts/download_mmseqs_dbs.sh +48 -0
.gitattributes CHANGED
@@ -1,35 +1,65 @@
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
  *.pt filter=lfs diff=lfs merge=lfs -text
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
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
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
+ *.pkl filter=lfs diff=lfs merge=lfs -text
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
+ *.wav filter=lfs diff=lfs merge=lfs -text
49
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
50
+ *.flac filter=lfs diff=lfs merge=lfs -text
51
+ *.ogg filter=lfs diff=lfs merge=lfs -text
52
+ *.m4a filter=lfs diff=lfs merge=lfs -text
53
+ *.aac filter=lfs diff=lfs merge=lfs -text
54
+ *.opus filter=lfs diff=lfs merge=lfs -text
55
+ *.mp4 filter=lfs diff=lfs merge=lfs -text
56
+ *.mov filter=lfs diff=lfs merge=lfs -text
57
+ *.avi filter=lfs diff=lfs merge=lfs -text
58
+ *.mkv filter=lfs diff=lfs merge=lfs -text
59
+ *.webm filter=lfs diff=lfs merge=lfs -text
60
+ *.obj filter=lfs diff=lfs merge=lfs -text
61
+ *.glb filter=lfs diff=lfs merge=lfs -text
62
+ *.gltf filter=lfs diff=lfs merge=lfs -text
63
+ *.ply filter=lfs diff=lfs merge=lfs -text
64
+ *.stl filter=lfs diff=lfs merge=lfs -text
65
+ *.fbx filter=lfs diff=lfs merge=lfs -text
LICENSE.txt ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2024 AlQuraishi Laboratory
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
README.md ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ - zh
6
+ tags:
7
+ - OneScience
8
+ - life-science
9
+ - protein-structure-prediction
10
+ frameworks: PyTorch
11
+ ---
12
+ <p align="center">
13
+ <strong>
14
+ <span style="font-size: 30px;">OpenFold</span>
15
+ </strong>
16
+ </p>
17
+
18
+ # Model Introduction
19
+
20
+ OpenFold is an open-source framework for predicting three-dimensional protein structures from protein sequences, multiple sequence alignments (MSAs), and template information. As a trainable reproduction of AlphaFold2, its core network architecture includes Evoformer, Structure Module, template modules, MSA modules, and other components, supporting a complete structural prediction experiment workflow that includes training, inference, and weight conversion.
21
+
22
+ Paper: OpenFold: Retraining AlphaFold2 yields new insights into its learning mechanisms and capacity for generalization
23
+ https://www.biorxiv.org/content/10.1101/2022.11.20.517210v2
24
+
25
+ # Model Description
26
+
27
+ OpenFold is based on the Evoformer + Structure Module architecture and is trained with PDB and UniRef data. It is designed for protein three-dimensional structure prediction, model fine-tuning, and research on architectural improvements. The core network contains 48 Evoformer Blocks and 8 Structure Module layers. Through triangle attention and invariant point attention mechanisms, it enables bidirectional fusion of evolutionary information and spatial geometry. Compared with the original AlphaFold2, OpenFold adds memory-efficient attention, mixed-precision training support, and seamless weight conversion capabilities, significantly reducing GPU memory usage and fixing trainability issues.
28
+
29
+ # Applicable Scenarios
30
+
31
+ | Scenario | Description |
32
+ | --- | --- |
33
+ | Local OpenFold invocation | Users can call OpenFold training or inference through the scripts in this repository. |
34
+ | Reuse of the OneScience base | Continue using shared OneScience modules such as datapipes, utils, loss, and np. |
35
+ | ModelScope/OneCode release | Expose only scripts, models, configurations, and weight directories to reduce the release package size. |
36
+ | Weight conversion and data preparation | Use the tools under `scripts/` to download databases, preprocess alignments, and convert weights. |
37
+
38
+
39
+ # Usage
40
+
41
+ ## 1. Using OneCode
42
+
43
+ You can try intelligent one-click AI4S programming through the OneCode online environment:
44
+
45
+ [Try intelligent one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
46
+
47
+ ## 2. Manual Installation and Usage
48
+
49
+ **Hardware Requirements**
50
+
51
+ - Running on a GPU or DCU is recommended.
52
+ - A CPU can be used for import checks and small-configuration connectivity validation, but full training and inference will be slow.
53
+ - DCU users need to install DTK in advance. DTK 25.04.2 or later is recommended, or the OneScience-recommended version that matches the current cluster.
54
+
55
+
56
+ ## 3. Quick Start
57
+
58
+ ### Download the Model Package
59
+
60
+ ```bash
61
+ modelscope download --model OneScience/OpenFold --local_dir ./OpenFold
62
+ cd OpenFold
63
+ ```
64
+
65
+ ### Install the Runtime Environment
66
+
67
+ #### DCU Environment
68
+ ```bash
69
+ # Activate DTK and CONDA first
70
+ conda create -n onescience311 python=3.11 -y
71
+ conda activate onescience311
72
+ # uv installation is supported
73
+ pip install onescience[bio] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
74
+ ```
75
+
76
+ ## Quick Verification
77
+
78
+ ```bash
79
+ PYTHONPATH=$(pwd)/model python -c "from openfold.model import AlphaFold; from config.config import model_config; import onescience; print('openfold wrapper ok')"
80
+ ```
81
+
82
+ If dependencies such as `torch`, `ml_collections`, or `deepspeed` are reported as missing, install the OneScience-recommended runtime environment first, or install the corresponding dependencies for your DCU/GPU version.
83
+
84
+ ## Example Data
85
+
86
+ The `data/` directory is used to store the example inputs required by OpenFold and user-provided data. The current repository includes a monomer inference example at `data/demo/monomer`, making it easy to quickly check the inference script, model import, and alignment loading workflow.
87
+
88
+ Directory structure:
89
+
90
+ | Path | Description |
91
+ | --- | --- |
92
+ | `data/demo/monomer/fasta_dir/6kwc.fasta` | Example protein sequence. The FASTA header is `6KWC_1`. |
93
+ | `data/demo/monomer/alignments/6KWC_1/` | Precomputed MSA/template search results corresponding to the FASTA header. |
94
+ | `data/demo/monomer/alignments/6KWC_1/bfd_uniref_hits.a3m` | BFD/UniRef alignment results. |
95
+ | `data/demo/monomer/alignments/6KWC_1/uniref90_hits.sto` | UniRef90 alignment results. |
96
+ | `data/demo/monomer/alignments/6KWC_1/mgnify_hits.sto` | MGnify alignment results. |
97
+ | `data/demo/monomer/alignments/6KWC_1/hhsearch_output.hhr` | HHsearch template search results. |
98
+ | `data/demo/monomer/inference.sh` | Monomer inference example script using the FASTA and precomputed alignments above. |
99
+
100
+ The example data does not include OpenFold weights or the PDB mmCIF template library. Before running the demo, prepare:
101
+
102
+ - `weight/openfold.pt`, or specify the weights with `CHECKPOINT_PATH=/path/to/openfold.pt`.
103
+ - The PDB mmCIF template directory. By default, `data/databases/pdb_mmcif/mmcif_files` is read; it can also be specified with `TEMPLATE_MMCIF_DIR=/path/to/mmcif_files`.
104
+
105
+ Run the example:
106
+
107
+ ```bash
108
+ bash data/demo/monomer/inference.sh
109
+ ```
110
+
111
+ To use another device, set `MODEL_DEVICE`:
112
+
113
+ ```bash
114
+ MODEL_DEVICE=cuda:0 bash data/demo/monomer/inference.sh
115
+ ```
116
+
117
+ ## Inference Example
118
+
119
+ ```bash
120
+ python scripts/inference.py /path/to/fasta_dir /path/to/template_mmcif_dir \
121
+ --output_dir ./outputs/inference \
122
+ --config_preset model_1_ptm \
123
+ --model_device cuda:0 \
124
+ --use_precomputed_alignments /path/to/alignments \
125
+ --openfold_checkpoint_path ./weight/openfold.pt
126
+ ```
127
+
128
+ If using AlphaFold JAX parameters:
129
+
130
+ ```bash
131
+ python scripts/inference.py /path/to/fasta_dir /path/to/template_mmcif_dir \
132
+ --output_dir ./outputs/inference \
133
+ --config_preset model_1_ptm \
134
+ --model_device cuda:0 \
135
+ --use_precomputed_alignments /path/to/alignments \
136
+ --jax_param_path ./weight/params_model_1_ptm.npz
137
+ ```
138
+
139
+ ## Training Example
140
+
141
+ ```bash
142
+ python scripts/train.py \
143
+ /path/to/train_mmcif \
144
+ /path/to/train_alignments \
145
+ /path/to/template_mmcif \
146
+ ./outputs/train \
147
+ 2021-10-10 \
148
+ --config_preset initial_training \
149
+ --max_epochs 1 \
150
+ --train_epoch_len 1 \
151
+ --gpus 1
152
+ ```
153
+
154
+ Multi-GPU training can be launched with `torchrun`; adjust the specific parameters according to your runtime environment.
155
+
156
+ ## Data and Weight Downloads
157
+
158
+ OpenFold weights:
159
+
160
+ ```bash
161
+ bash scripts/download_openfold_params.sh ./weights
162
+ ```
163
+
164
+ Hugging Face weights:
165
+
166
+ ```bash
167
+ bash scripts/download_openfold_params_huggingface.sh ./weights
168
+ ```
169
+
170
+ PDB mmCIF template library:
171
+
172
+ ```bash
173
+ bash scripts/download_pdb_mmcif.sh /path/to/database_dir
174
+ ```
175
+
176
+ Complete AlphaFold/OpenFold databases:
177
+
178
+ ```bash
179
+ bash scripts/download_alphafold_dbs.sh /path/to/database_dir full_dbs
180
+ ```
181
+
182
+ ## Official OneScience Information
183
+
184
+ | Platform | OneScience Main Repository | Skills Repository |
185
+ | --- | --- | --- |
186
+ | Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
187
+ | GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
188
+
189
+ ## Citations and License
190
+
191
+ - Original OpenFold paper: [OpenFold: Retraining AlphaFold2 yields new insights into its learning mechanisms and capacity for generalization](https://www.biorxiv.org/content/10.1101/2022.11.20.517210).
192
+
193
+ - Original AlphaFold2 paper: [Highly accurate protein structure prediction with AlphaFold](https://www.nature.com/articles/s41586-021-03819-2).
194
+
195
+ - If you use OpenFold's multimer prediction functionality, you should also cite the original AlphaFold-Multimer paper: [Protein complex prediction with AlphaFold-Multimer](https://www.biorxiv.org/content/10.1101/2021.10.04.463034v1).
196
+
197
+ - If you use OpenProteinSet training data, you should also cite: [OpenProteinSet: Training data for structural biology at scale](https://arxiv.org/abs/2308.05326).
198
+
199
+ - OpenFold-related source code uses the Apache License 2.0. See `LICENSE` in the repository root for details. The terms of use for model weights and data should follow the instructions provided by the corresponding publishers.
200
+
201
+ - The OpenProteinSet dataset uses the CC BY 4.0 License. When using this dataset, you should acknowledge the data source and cite the OpenProteinSet paper as required by the dataset page.
202
+
203
+ - If you use OpenFold in research, it is recommended to cite the original OpenFold and AlphaFold2 papers as well as relevant OneScience project information. When using multimer functionality, also cite AlphaFold-Multimer. When using OpenProteinSet or other downstream data resources, also include citations for the corresponding datasets, databases, and original papers.
config/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ 
config/config.py ADDED
@@ -0,0 +1,956 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import copy
3
+ import importlib
4
+ import ml_collections as mlc
5
+
6
+
7
+ def set_inf(c, inf):
8
+ for k, v in c.items():
9
+ if isinstance(v, mlc.ConfigDict):
10
+ set_inf(v, inf)
11
+ elif k == "inf":
12
+ c[k] = inf
13
+
14
+
15
+ def enforce_config_constraints(config):
16
+ def string_to_setting(s):
17
+ path = s.split('.')
18
+ setting = config
19
+ for p in path:
20
+ setting = setting.get(p)
21
+
22
+ return setting
23
+
24
+ mutually_exclusive_bools = [
25
+ (
26
+ "model.template.average_templates",
27
+ "model.template.offload_templates"
28
+ ),
29
+ (
30
+ "globals.use_lma",
31
+ "globals.use_flash",
32
+ "globals.use_deepspeed_evo_attention"
33
+ ),
34
+ ]
35
+
36
+ for options in mutually_exclusive_bools:
37
+ option_settings = [string_to_setting(o) for o in options]
38
+ if sum(option_settings) > 1:
39
+ raise ValueError(f"Only one of {', '.join(options)} may be set at a time")
40
+
41
+ fa_is_installed = importlib.util.find_spec("flash_attn") is not None
42
+ if config.globals.use_flash and not fa_is_installed:
43
+ raise ValueError("use_flash requires that FlashAttention is installed")
44
+
45
+ deepspeed_is_installed = importlib.util.find_spec("deepspeed") is not None
46
+ ds4s_is_installed = deepspeed_is_installed and importlib.util.find_spec(
47
+ "deepspeed.ops.deepspeed4science") is not None
48
+ if config.globals.use_deepspeed_evo_attention and not ds4s_is_installed:
49
+ raise ValueError(
50
+ "use_deepspeed_evo_attention requires that DeepSpeed be installed "
51
+ "and that the deepspeed.ops.deepspeed4science package exists"
52
+ )
53
+
54
+ if(
55
+ config.globals.offload_inference and
56
+ not config.model.template.average_templates
57
+ ):
58
+ config.model.template.offload_templates = True
59
+
60
+
61
+ def model_config(
62
+ name,
63
+ train=False,
64
+ low_prec=False,
65
+ long_sequence_inference=False,
66
+ use_deepspeed_evoformer_attention=False,
67
+ ):
68
+ c = copy.deepcopy(config)
69
+ # TRAINING PRESETS
70
+ if name == "initial_training":
71
+ # AF2 Suppl. Table 4, "initial training" setting
72
+ pass
73
+ elif name == "finetuning":
74
+ # AF2 Suppl. Table 4, "finetuning" setting
75
+ c.data.train.crop_size = 384
76
+ c.data.train.max_extra_msa = 5120
77
+ c.data.train.max_msa_clusters = 512
78
+ c.loss.violation.weight = 1.
79
+ c.loss.experimentally_resolved.weight = 0.01
80
+ elif name == "finetuning_ptm":
81
+ c.data.train.max_extra_msa = 5120
82
+ c.data.train.crop_size = 384
83
+ c.data.train.max_msa_clusters = 512
84
+ c.loss.violation.weight = 1.
85
+ c.loss.experimentally_resolved.weight = 0.01
86
+ c.model.heads.tm.enabled = True
87
+ c.loss.tm.weight = 0.1
88
+ elif name == "finetuning_no_templ":
89
+ # AF2 Suppl. Table 4, "finetuning" setting
90
+ c.data.train.crop_size = 384
91
+ c.data.train.max_extra_msa = 5120
92
+ c.data.train.max_msa_clusters = 512
93
+ c.model.template.enabled = False
94
+ c.loss.violation.weight = 1.
95
+ c.loss.experimentally_resolved.weight = 0.01
96
+ elif name == "finetuning_no_templ_ptm":
97
+ # AF2 Suppl. Table 4, "finetuning" setting
98
+ c.data.train.crop_size = 384
99
+ c.data.train.max_extra_msa = 5120
100
+ c.data.train.max_msa_clusters = 512
101
+ c.model.template.enabled = False
102
+ c.loss.violation.weight = 1.
103
+ c.loss.experimentally_resolved.weight = 0.01
104
+ c.model.heads.tm.enabled = True
105
+ c.loss.tm.weight = 0.1
106
+ # INFERENCE PRESETS
107
+ elif name == "model_1":
108
+ # AF2 Suppl. Table 5, Model 1.1.1
109
+ c.data.train.max_extra_msa = 5120
110
+ c.data.predict.max_extra_msa = 5120
111
+ c.data.common.reduce_max_clusters_by_max_templates = True
112
+ c.data.common.use_templates = True
113
+ c.data.common.use_template_torsion_angles = True
114
+ c.model.template.enabled = True
115
+ elif name == "model_2":
116
+ # AF2 Suppl. Table 5, Model 1.1.2
117
+ c.data.common.reduce_max_clusters_by_max_templates = True
118
+ c.data.common.use_templates = True
119
+ c.data.common.use_template_torsion_angles = True
120
+ c.model.template.enabled = True
121
+ elif name == "model_3":
122
+ # AF2 Suppl. Table 5, Model 1.2.1
123
+ c.data.train.max_extra_msa = 5120
124
+ c.data.predict.max_extra_msa = 5120
125
+ c.model.template.enabled = False
126
+ elif name == "model_4":
127
+ # AF2 Suppl. Table 5, Model 1.2.2
128
+ c.data.train.max_extra_msa = 5120
129
+ c.data.predict.max_extra_msa = 5120
130
+ c.model.template.enabled = False
131
+ elif name == "model_5":
132
+ # AF2 Suppl. Table 5, Model 1.2.3
133
+ c.model.template.enabled = False
134
+ elif name == "model_1_ptm":
135
+ c.data.train.max_extra_msa = 5120
136
+ c.data.predict.max_extra_msa = 5120
137
+ c.data.common.reduce_max_clusters_by_max_templates = True
138
+ c.data.common.use_templates = True
139
+ c.data.common.use_template_torsion_angles = True
140
+ c.model.template.enabled = True
141
+ c.model.heads.tm.enabled = True
142
+ c.loss.tm.weight = 0.1
143
+ elif name == "model_2_ptm":
144
+ c.data.common.reduce_max_clusters_by_max_templates = True
145
+ c.data.common.use_templates = True
146
+ c.data.common.use_template_torsion_angles = True
147
+ c.model.template.enabled = True
148
+ c.model.heads.tm.enabled = True
149
+ c.loss.tm.weight = 0.1
150
+ elif name == "model_3_ptm":
151
+ c.data.train.max_extra_msa = 5120
152
+ c.data.predict.max_extra_msa = 5120
153
+ c.model.template.enabled = False
154
+ c.model.heads.tm.enabled = True
155
+ c.loss.tm.weight = 0.1
156
+ elif name == "model_4_ptm":
157
+ c.data.train.max_extra_msa = 5120
158
+ c.data.predict.max_extra_msa = 5120
159
+ c.model.template.enabled = False
160
+ c.model.heads.tm.enabled = True
161
+ c.loss.tm.weight = 0.1
162
+ elif name == "model_5_ptm":
163
+ c.model.template.enabled = False
164
+ c.model.heads.tm.enabled = True
165
+ c.loss.tm.weight = 0.1
166
+ elif name.startswith("seq"): # SINGLE SEQUENCE EMBEDDING PRESETS
167
+ c.update(seq_mode_config.copy_and_resolve_references())
168
+ if name == "seqemb_initial_training":
169
+ c.data.train.max_msa_clusters = 1
170
+ c.data.eval.max_msa_clusters = 1
171
+ c.data.train.block_delete_msa = False
172
+ c.data.train.max_distillation_msa_clusters = 1
173
+ elif name == "seqemb_finetuning":
174
+ c.data.train.max_msa_clusters = 1
175
+ c.data.eval.max_msa_clusters = 1
176
+ c.data.train.block_delete_msa = False
177
+ c.data.train.max_distillation_msa_clusters = 1
178
+ c.data.train.crop_size = 384
179
+ c.loss.violation.weight = 1.
180
+ c.loss.experimentally_resolved.weight = 0.01
181
+ elif name == "seq_model_esm1b":
182
+ c.data.common.use_templates = True
183
+ c.data.common.use_template_torsion_angles = True
184
+ c.model.template.enabled = True
185
+ c.data.predict.max_msa_clusters = 1
186
+ elif name == "seq_model_esm1b_ptm":
187
+ c.data.common.use_templates = True
188
+ c.data.common.use_template_torsion_angles = True
189
+ c.model.template.enabled = True
190
+ c.data.predict.max_msa_clusters = 1
191
+ c.model.heads.tm.enabled = True
192
+ c.loss.tm.weight = 0.1
193
+ elif "multimer" in name: # MULTIMER PRESETS
194
+ c.update(multimer_config_update.copy_and_resolve_references())
195
+
196
+ # Not used in multimer
197
+ del c.model.template.template_pointwise_attention
198
+ del c.loss.fape.backbone
199
+
200
+ # TODO: Change max_msa_clusters and max_extra_msa to multimer feats within model
201
+ if re.fullmatch("^model_[1-5]_multimer(_v2)?$", name):
202
+ #c.model.input_embedder.num_msa = 252
203
+ #c.model.extra_msa.extra_msa_embedder.num_extra_msa = 1152
204
+ c.data.train.crop_size = 384
205
+
206
+ c.data.train.max_msa_clusters = 252
207
+ c.data.eval.max_msa_clusters = 252
208
+ c.data.predict.max_msa_clusters = 252
209
+
210
+ c.data.train.max_extra_msa = 1152
211
+ c.data.eval.max_extra_msa = 1152
212
+ c.data.predict.max_extra_msa = 1152
213
+
214
+ c.model.evoformer_stack.fuse_projection_weights = False
215
+ c.model.extra_msa.extra_msa_stack.fuse_projection_weights = False
216
+ c.model.template.template_pair_stack.fuse_projection_weights = False
217
+ elif name == 'model_4_multimer_v3':
218
+ #c.model.extra_msa.extra_msa_embedder.num_extra_msa = 1152
219
+ c.data.train.max_extra_msa = 1152
220
+ c.data.eval.max_extra_msa = 1152
221
+ c.data.predict.max_extra_msa = 1152
222
+ elif name == 'model_5_multimer_v3':
223
+ #c.model.extra_msa.extra_msa_embedder.num_extra_msa = 1152
224
+ c.data.train.max_extra_msa = 1152
225
+ c.data.eval.max_extra_msa = 1152
226
+ c.data.predict.max_extra_msa = 1152
227
+ else:
228
+ raise ValueError("Invalid model name")
229
+
230
+ if long_sequence_inference:
231
+ assert(not train)
232
+ c.globals.offload_inference = True
233
+ # Default to DeepSpeed memory-efficient attention kernel unless use_lma is explicitly set
234
+ c.globals.use_deepspeed_evo_attention = True if not c.globals.use_lma else False
235
+ c.globals.use_flash = False
236
+ c.model.template.offload_inference = True
237
+ c.model.template.template_pair_stack.tune_chunk_size = False
238
+ c.model.extra_msa.extra_msa_stack.tune_chunk_size = False
239
+ c.model.evoformer_stack.tune_chunk_size = False
240
+
241
+ if use_deepspeed_evoformer_attention:
242
+ c.globals.use_deepspeed_evo_attention = True
243
+
244
+ if train:
245
+ c.globals.blocks_per_ckpt = 1
246
+ c.globals.chunk_size = None
247
+ c.globals.use_lma = False
248
+ c.globals.offload_inference = False
249
+ c.model.template.average_templates = False
250
+ c.model.template.offload_templates = False
251
+
252
+ if low_prec:
253
+ c.globals.eps = 1e-4
254
+ # If we want exact numerical parity with the original, inf can't be
255
+ # a global constant
256
+ set_inf(c, 1e4)
257
+
258
+ enforce_config_constraints(c)
259
+
260
+ return c
261
+
262
+
263
+ c_z = mlc.FieldReference(128, field_type=int)
264
+ c_m = mlc.FieldReference(256, field_type=int)
265
+ c_t = mlc.FieldReference(64, field_type=int)
266
+ c_e = mlc.FieldReference(64, field_type=int)
267
+ c_s = mlc.FieldReference(384, field_type=int)
268
+
269
+ # For seqemb mode, dimension size of the per-residue sequence embedding passed to the model
270
+ # In current model, the dimension size is the ESM-1b dimension size i.e. 1280.
271
+ preemb_dim_size = mlc.FieldReference(1280, field_type=int)
272
+
273
+ blocks_per_ckpt = mlc.FieldReference(None, field_type=int)
274
+ chunk_size = mlc.FieldReference(4, field_type=int)
275
+ aux_distogram_bins = mlc.FieldReference(64, field_type=int)
276
+ tm_enabled = mlc.FieldReference(False, field_type=bool)
277
+ eps = mlc.FieldReference(1e-8, field_type=float)
278
+ templates_enabled = mlc.FieldReference(True, field_type=bool)
279
+ embed_template_torsion_angles = mlc.FieldReference(True, field_type=bool)
280
+ tune_chunk_size = mlc.FieldReference(True, field_type=bool)
281
+
282
+ NUM_RES = "num residues placeholder"
283
+ NUM_MSA_SEQ = "msa placeholder"
284
+ NUM_EXTRA_SEQ = "extra msa placeholder"
285
+ NUM_TEMPLATES = "num templates placeholder"
286
+
287
+ config = mlc.ConfigDict(
288
+ {
289
+ "data": {
290
+ "common": {
291
+ "feat": {
292
+ "aatype": [NUM_RES],
293
+ "all_atom_mask": [NUM_RES, None],
294
+ "all_atom_positions": [NUM_RES, None, None],
295
+ "alt_chi_angles": [NUM_RES, None],
296
+ "atom14_alt_gt_exists": [NUM_RES, None],
297
+ "atom14_alt_gt_positions": [NUM_RES, None, None],
298
+ "atom14_atom_exists": [NUM_RES, None],
299
+ "atom14_atom_is_ambiguous": [NUM_RES, None],
300
+ "atom14_gt_exists": [NUM_RES, None],
301
+ "atom14_gt_positions": [NUM_RES, None, None],
302
+ "atom37_atom_exists": [NUM_RES, None],
303
+ "backbone_rigid_mask": [NUM_RES],
304
+ "backbone_rigid_tensor": [NUM_RES, None, None],
305
+ "bert_mask": [NUM_MSA_SEQ, NUM_RES],
306
+ "chi_angles_sin_cos": [NUM_RES, None, None],
307
+ "chi_mask": [NUM_RES, None],
308
+ "extra_deletion_value": [NUM_EXTRA_SEQ, NUM_RES],
309
+ "extra_has_deletion": [NUM_EXTRA_SEQ, NUM_RES],
310
+ "extra_msa": [NUM_EXTRA_SEQ, NUM_RES],
311
+ "extra_msa_mask": [NUM_EXTRA_SEQ, NUM_RES],
312
+ "extra_msa_row_mask": [NUM_EXTRA_SEQ],
313
+ "is_distillation": [],
314
+ "msa_feat": [NUM_MSA_SEQ, NUM_RES, None],
315
+ "msa_mask": [NUM_MSA_SEQ, NUM_RES],
316
+ "msa_row_mask": [NUM_MSA_SEQ],
317
+ "no_recycling_iters": [],
318
+ "pseudo_beta": [NUM_RES, None],
319
+ "pseudo_beta_mask": [NUM_RES],
320
+ "residue_index": [NUM_RES],
321
+ "residx_atom14_to_atom37": [NUM_RES, None],
322
+ "residx_atom37_to_atom14": [NUM_RES, None],
323
+ "resolution": [],
324
+ "rigidgroups_alt_gt_frames": [NUM_RES, None, None, None],
325
+ "rigidgroups_group_exists": [NUM_RES, None],
326
+ "rigidgroups_group_is_ambiguous": [NUM_RES, None],
327
+ "rigidgroups_gt_exists": [NUM_RES, None],
328
+ "rigidgroups_gt_frames": [NUM_RES, None, None, None],
329
+ "seq_length": [],
330
+ "seq_mask": [NUM_RES],
331
+ "target_feat": [NUM_RES, None],
332
+ "template_aatype": [NUM_TEMPLATES, NUM_RES],
333
+ "template_all_atom_mask": [NUM_TEMPLATES, NUM_RES, None],
334
+ "template_all_atom_positions": [
335
+ NUM_TEMPLATES, NUM_RES, None, None,
336
+ ],
337
+ "template_alt_torsion_angles_sin_cos": [
338
+ NUM_TEMPLATES, NUM_RES, None, None,
339
+ ],
340
+ "template_backbone_rigid_mask": [NUM_TEMPLATES, NUM_RES],
341
+ "template_backbone_rigid_tensor": [
342
+ NUM_TEMPLATES, NUM_RES, None, None,
343
+ ],
344
+ "template_mask": [NUM_TEMPLATES],
345
+ "template_pseudo_beta": [NUM_TEMPLATES, NUM_RES, None],
346
+ "template_pseudo_beta_mask": [NUM_TEMPLATES, NUM_RES],
347
+ "template_sum_probs": [NUM_TEMPLATES, None],
348
+ "template_torsion_angles_mask": [
349
+ NUM_TEMPLATES, NUM_RES, None,
350
+ ],
351
+ "template_torsion_angles_sin_cos": [
352
+ NUM_TEMPLATES, NUM_RES, None, None,
353
+ ],
354
+ "true_msa": [NUM_MSA_SEQ, NUM_RES],
355
+ "use_clamped_fape": [],
356
+ },
357
+ "block_delete_msa": {
358
+ "msa_fraction_per_block": 0.3,
359
+ "randomize_num_blocks": False,
360
+ "num_blocks": 5,
361
+ },
362
+ "masked_msa": {
363
+ "profile_prob": 0.1,
364
+ "same_prob": 0.1,
365
+ "uniform_prob": 0.1,
366
+ },
367
+ "max_recycling_iters": 3,
368
+ "msa_cluster_features": True,
369
+ "reduce_msa_clusters_by_max_templates": False,
370
+ "resample_msa_in_recycling": True,
371
+ "template_features": [
372
+ "template_all_atom_positions",
373
+ "template_sum_probs",
374
+ "template_aatype",
375
+ "template_all_atom_mask",
376
+ ],
377
+ "unsupervised_features": [
378
+ "aatype",
379
+ "residue_index",
380
+ "msa",
381
+ "num_alignments",
382
+ "seq_length",
383
+ "between_segment_residues",
384
+ "deletion_matrix",
385
+ "no_recycling_iters",
386
+ ],
387
+ "use_templates": templates_enabled,
388
+ "use_template_torsion_angles": embed_template_torsion_angles,
389
+ },
390
+ "seqemb_mode": { # Configuration for sequence embedding mode
391
+ "enabled": False, # If True, use seq emb instead of MSA
392
+ },
393
+ "supervised": {
394
+ "clamp_prob": 0.9,
395
+ "supervised_features": [
396
+ "all_atom_mask",
397
+ "all_atom_positions",
398
+ "resolution",
399
+ "use_clamped_fape",
400
+ "is_distillation",
401
+ ],
402
+ },
403
+ "predict": {
404
+ "fixed_size": True,
405
+ "subsample_templates": False, # We want top templates.
406
+ "block_delete_msa": False,
407
+ "masked_msa_replace_fraction": 0.15,
408
+ "max_msa_clusters": 512,
409
+ "max_extra_msa": 1024,
410
+ "max_template_hits": 4,
411
+ "max_templates": 4,
412
+ "crop": False,
413
+ "crop_size": None,
414
+ "spatial_crop_prob": None,
415
+ "interface_threshold": None,
416
+ "supervised": False,
417
+ "uniform_recycling": False,
418
+ },
419
+ "eval": {
420
+ "fixed_size": True,
421
+ "subsample_templates": False, # We want top templates.
422
+ "block_delete_msa": False,
423
+ "masked_msa_replace_fraction": 0.15,
424
+ "max_msa_clusters": 128,
425
+ "max_extra_msa": 1024,
426
+ "max_template_hits": 4,
427
+ "max_templates": 4,
428
+ "crop": False,
429
+ "crop_size": None,
430
+ "spatial_crop_prob": None,
431
+ "interface_threshold": None,
432
+ "supervised": True,
433
+ "uniform_recycling": False,
434
+ },
435
+ "train": {
436
+ "fixed_size": True,
437
+ "subsample_templates": True,
438
+ "block_delete_msa": True,
439
+ "masked_msa_replace_fraction": 0.15,
440
+ "max_msa_clusters": 128,
441
+ "max_extra_msa": 1024,
442
+ "max_template_hits": 4,
443
+ "max_templates": 4,
444
+ "shuffle_top_k_prefiltered": 20,
445
+ "crop": True,
446
+ "crop_size": 256,
447
+ "spatial_crop_prob": 0.,
448
+ "interface_threshold": None,
449
+ "supervised": True,
450
+ "clamp_prob": 0.9,
451
+ "max_distillation_msa_clusters": 1000,
452
+ "uniform_recycling": True,
453
+ "distillation_prob": 0.75,
454
+ },
455
+ "data_module": {
456
+ "use_small_bfd": False,
457
+ "data_loaders": {
458
+ "batch_size": 1,
459
+ "num_workers": 16,
460
+ "pin_memory": True,
461
+ },
462
+ },
463
+ },
464
+ # Recurring FieldReferences that can be changed globally here
465
+ "globals": {
466
+ "blocks_per_ckpt": blocks_per_ckpt,
467
+ "chunk_size": chunk_size,
468
+ # Use DeepSpeed memory-efficient attention kernel. Mutually
469
+ # exclusive with use_lma and use_flash.
470
+ "use_deepspeed_evo_attention": False,
471
+ # Use Staats & Rabe's low-memory attention algorithm. Mutually
472
+ # exclusive with use_deepspeed_evo_attention and use_flash.
473
+ "use_lma": False,
474
+ # Use FlashAttention in selected modules. Mutually exclusive with
475
+ # use_deepspeed_evo_attention and use_lma. Doesn't work that well
476
+ # on long sequences (>1000 residues).
477
+ "use_flash": False,
478
+ "offload_inference": False,
479
+ "c_z": c_z,
480
+ "c_m": c_m,
481
+ "c_t": c_t,
482
+ "c_e": c_e,
483
+ "c_s": c_s,
484
+ "eps": eps,
485
+ "is_multimer": False,
486
+ "seqemb_mode_enabled": False, # Global flag for enabling seq emb mode
487
+ },
488
+ "model": {
489
+ "_mask_trans": False,
490
+ "input_embedder": {
491
+ "tf_dim": 22,
492
+ "msa_dim": 49,
493
+ "c_z": c_z,
494
+ "c_m": c_m,
495
+ "relpos_k": 32,
496
+ },
497
+ "recycling_embedder": {
498
+ "c_z": c_z,
499
+ "c_m": c_m,
500
+ "min_bin": 3.25,
501
+ "max_bin": 20.75,
502
+ "no_bins": 15,
503
+ "inf": 1e8,
504
+ },
505
+ "template": {
506
+ "distogram": {
507
+ "min_bin": 3.25,
508
+ "max_bin": 50.75,
509
+ "no_bins": 39,
510
+ },
511
+ "template_single_embedder": {
512
+ # DISCREPANCY: c_in is supposed to be 51.
513
+ "c_in": 57,
514
+ "c_out": c_m,
515
+ },
516
+ "template_pair_embedder": {
517
+ "c_in": 88,
518
+ "c_out": c_t,
519
+ },
520
+ "template_pair_stack": {
521
+ "c_t": c_t,
522
+ # DISCREPANCY: c_hidden_tri_att here is given in the supplement
523
+ # as 64. In the code, it's 16.
524
+ "c_hidden_tri_att": 16,
525
+ "c_hidden_tri_mul": 64,
526
+ "no_blocks": 2,
527
+ "no_heads": 4,
528
+ "pair_transition_n": 2,
529
+ "dropout_rate": 0.25,
530
+ "tri_mul_first": False,
531
+ "fuse_projection_weights": False,
532
+ "blocks_per_ckpt": blocks_per_ckpt,
533
+ "tune_chunk_size": tune_chunk_size,
534
+ "inf": 1e9,
535
+ },
536
+ "template_pointwise_attention": {
537
+ "c_t": c_t,
538
+ "c_z": c_z,
539
+ # DISCREPANCY: c_hidden here is given in the supplement as 64.
540
+ # It's actually 16.
541
+ "c_hidden": 16,
542
+ "no_heads": 4,
543
+ "inf": 1e5, # 1e9,
544
+ },
545
+ "inf": 1e5, # 1e9,
546
+ "eps": eps, # 1e-6,
547
+ "enabled": templates_enabled,
548
+ "embed_angles": embed_template_torsion_angles,
549
+ "use_unit_vector": False,
550
+ # Approximate template computation, saving memory.
551
+ # In our experiments, results are equivalent to or better than
552
+ # the stock implementation. Should be enabled for all new
553
+ # training runs.
554
+ "average_templates": False,
555
+ # Offload template embeddings to CPU memory. Vastly reduced
556
+ # memory consumption at the cost of a modest increase in
557
+ # runtime. Useful for inference on very long sequences.
558
+ # Mutually exclusive with average_templates. Automatically
559
+ # enabled if offload_inference is set.
560
+ "offload_templates": False,
561
+ },
562
+ "extra_msa": {
563
+ "extra_msa_embedder": {
564
+ "c_in": 25,
565
+ "c_out": c_e,
566
+ },
567
+ "extra_msa_stack": {
568
+ "c_m": c_e,
569
+ "c_z": c_z,
570
+ "c_hidden_msa_att": 8,
571
+ "c_hidden_opm": 32,
572
+ "c_hidden_mul": 128,
573
+ "c_hidden_pair_att": 32,
574
+ "no_heads_msa": 8,
575
+ "no_heads_pair": 4,
576
+ "no_blocks": 4,
577
+ "transition_n": 4,
578
+ "msa_dropout": 0.15,
579
+ "pair_dropout": 0.25,
580
+ "opm_first": False,
581
+ "fuse_projection_weights": False,
582
+ "clear_cache_between_blocks": False,
583
+ "tune_chunk_size": tune_chunk_size,
584
+ "inf": 1e9,
585
+ "eps": eps, # 1e-10,
586
+ "ckpt": blocks_per_ckpt is not None,
587
+ },
588
+ "enabled": True,
589
+ },
590
+ "evoformer_stack": {
591
+ "c_m": c_m,
592
+ "c_z": c_z,
593
+ "c_hidden_msa_att": 32,
594
+ "c_hidden_opm": 32,
595
+ "c_hidden_mul": 128,
596
+ "c_hidden_pair_att": 32,
597
+ "c_s": c_s,
598
+ "no_heads_msa": 8,
599
+ "no_heads_pair": 4,
600
+ "no_blocks": 48,
601
+ "transition_n": 4,
602
+ "msa_dropout": 0.15,
603
+ "pair_dropout": 0.25,
604
+ "no_column_attention": False,
605
+ "opm_first": False,
606
+ "fuse_projection_weights": False,
607
+ "blocks_per_ckpt": blocks_per_ckpt,
608
+ "clear_cache_between_blocks": False,
609
+ "tune_chunk_size": tune_chunk_size,
610
+ "inf": 1e9,
611
+ "eps": eps, # 1e-10,
612
+ },
613
+ "structure_module": {
614
+ "c_s": c_s,
615
+ "c_z": c_z,
616
+ "c_ipa": 16,
617
+ "c_resnet": 128,
618
+ "no_heads_ipa": 12,
619
+ "no_qk_points": 4,
620
+ "no_v_points": 8,
621
+ "dropout_rate": 0.1,
622
+ "no_blocks": 8,
623
+ "no_transition_layers": 1,
624
+ "no_resnet_blocks": 2,
625
+ "no_angles": 7,
626
+ "trans_scale_factor": 10,
627
+ "epsilon": eps, # 1e-12,
628
+ "inf": 1e5,
629
+ },
630
+ "heads": {
631
+ "lddt": {
632
+ "no_bins": 50,
633
+ "c_in": c_s,
634
+ "c_hidden": 128,
635
+ },
636
+ "distogram": {
637
+ "c_z": c_z,
638
+ "no_bins": aux_distogram_bins,
639
+ },
640
+ "tm": {
641
+ "c_z": c_z,
642
+ "no_bins": aux_distogram_bins,
643
+ "enabled": tm_enabled,
644
+ },
645
+ "masked_msa": {
646
+ "c_m": c_m,
647
+ "c_out": 23,
648
+ },
649
+ "experimentally_resolved": {
650
+ "c_s": c_s,
651
+ "c_out": 37,
652
+ },
653
+ },
654
+ # A negative value indicates that no early stopping will occur, i.e.
655
+ # the model will always run `max_recycling_iters` number of recycling
656
+ # iterations. A positive value will enable early stopping if the
657
+ # difference in pairwise distances is less than the tolerance between
658
+ # recycling steps.
659
+ "recycle_early_stop_tolerance": -1.
660
+ },
661
+ "relax": {
662
+ "max_iterations": 0, # no max
663
+ "tolerance": 10.0,
664
+ "stiffness": 10.0,
665
+ "max_outer_iterations": 20,
666
+ "exclude_residues": [],
667
+ },
668
+ "loss": {
669
+ "distogram": {
670
+ "min_bin": 2.3125,
671
+ "max_bin": 21.6875,
672
+ "no_bins": 64,
673
+ "eps": eps, # 1e-6,
674
+ "weight": 0.3,
675
+ },
676
+ "experimentally_resolved": {
677
+ "eps": eps, # 1e-8,
678
+ "min_resolution": 0.1,
679
+ "max_resolution": 3.0,
680
+ "weight": 0.0,
681
+ },
682
+ "fape": {
683
+ "backbone": {
684
+ "clamp_distance": 10.0,
685
+ "loss_unit_distance": 10.0,
686
+ "weight": 0.5,
687
+ },
688
+ "sidechain": {
689
+ "clamp_distance": 10.0,
690
+ "length_scale": 10.0,
691
+ "weight": 0.5,
692
+ },
693
+ "eps": 1e-4,
694
+ "weight": 1.0,
695
+ },
696
+ "plddt_loss": {
697
+ "min_resolution": 0.1,
698
+ "max_resolution": 3.0,
699
+ "cutoff": 15.0,
700
+ "no_bins": 50,
701
+ "eps": eps, # 1e-10,
702
+ "weight": 0.01,
703
+ },
704
+ "masked_msa": {
705
+ "num_classes": 23,
706
+ "eps": eps, # 1e-8,
707
+ "weight": 2.0,
708
+ },
709
+ "supervised_chi": {
710
+ "chi_weight": 0.5,
711
+ "angle_norm_weight": 0.01,
712
+ "eps": eps, # 1e-6,
713
+ "weight": 1.0,
714
+ },
715
+ "violation": {
716
+ "violation_tolerance_factor": 12.0,
717
+ "clash_overlap_tolerance": 1.5,
718
+ "average_clashes": False,
719
+ "eps": eps, # 1e-6,
720
+ "weight": 0.0,
721
+ },
722
+ "tm": {
723
+ "max_bin": 31,
724
+ "no_bins": 64,
725
+ "min_resolution": 0.1,
726
+ "max_resolution": 3.0,
727
+ "eps": eps, # 1e-8,
728
+ "weight": 0.,
729
+ "enabled": tm_enabled,
730
+ },
731
+ "chain_center_of_mass": {
732
+ "clamp_distance": -4.0,
733
+ "weight": 0.,
734
+ "eps": eps,
735
+ "enabled": False,
736
+ },
737
+ "eps": eps,
738
+ },
739
+ "ema": {"decay": 0.999},
740
+ }
741
+ )
742
+
743
+ multimer_config_update = mlc.ConfigDict({
744
+ "globals": {
745
+ "is_multimer": True
746
+ },
747
+ "data": {
748
+ "common": {
749
+ "feat": {
750
+ "aatype": [NUM_RES],
751
+ "all_atom_mask": [NUM_RES, None],
752
+ "all_atom_positions": [NUM_RES, None, None],
753
+ # "all_chains_entity_ids": [], # TODO: Resolve missing features, remove processed msa feats
754
+ # "all_crops_all_chains_mask": [],
755
+ # "all_crops_all_chains_positions": [],
756
+ # "all_crops_all_chains_residue_ids": [],
757
+ "assembly_num_chains": [],
758
+ "asym_id": [NUM_RES],
759
+ "atom14_atom_exists": [NUM_RES, None],
760
+ "atom37_atom_exists": [NUM_RES, None],
761
+ "bert_mask": [NUM_MSA_SEQ, NUM_RES],
762
+ "cluster_bias_mask": [NUM_MSA_SEQ],
763
+ "cluster_profile": [NUM_MSA_SEQ, NUM_RES, None],
764
+ "cluster_deletion_mean": [NUM_MSA_SEQ, NUM_RES],
765
+ "deletion_matrix": [NUM_MSA_SEQ, NUM_RES],
766
+ "deletion_mean": [NUM_RES],
767
+ "entity_id": [NUM_RES],
768
+ "entity_mask": [NUM_RES],
769
+ "extra_deletion_matrix": [NUM_EXTRA_SEQ, NUM_RES],
770
+ "extra_msa": [NUM_EXTRA_SEQ, NUM_RES],
771
+ "extra_msa_mask": [NUM_EXTRA_SEQ, NUM_RES],
772
+ # "mem_peak": [],
773
+ "msa": [NUM_MSA_SEQ, NUM_RES],
774
+ "msa_feat": [NUM_MSA_SEQ, NUM_RES, None],
775
+ "msa_mask": [NUM_MSA_SEQ, NUM_RES],
776
+ "msa_profile": [NUM_RES, None],
777
+ "num_alignments": [],
778
+ "num_templates": [],
779
+ # "queue_size": [],
780
+ "residue_index": [NUM_RES],
781
+ "residx_atom14_to_atom37": [NUM_RES, None],
782
+ "residx_atom37_to_atom14": [NUM_RES, None],
783
+ "resolution": [],
784
+ "seq_length": [],
785
+ "seq_mask": [NUM_RES],
786
+ "sym_id": [NUM_RES],
787
+ "target_feat": [NUM_RES, None],
788
+ "template_aatype": [NUM_TEMPLATES, NUM_RES],
789
+ "template_all_atom_mask": [NUM_TEMPLATES, NUM_RES, None],
790
+ "template_all_atom_positions": [
791
+ NUM_TEMPLATES, NUM_RES, None, None,
792
+ ],
793
+ "true_msa": [NUM_MSA_SEQ, NUM_RES]
794
+ },
795
+ "max_recycling_iters": 20, # For training, value is 3
796
+ "unsupervised_features": [
797
+ "aatype",
798
+ "residue_index",
799
+ "msa",
800
+ "num_alignments",
801
+ "seq_length",
802
+ "between_segment_residues",
803
+ "deletion_matrix",
804
+ "no_recycling_iters",
805
+ # Additional multimer features
806
+ "msa_mask",
807
+ "seq_mask",
808
+ "asym_id",
809
+ "entity_id",
810
+ "sym_id",
811
+ ]
812
+ },
813
+ "supervised": {
814
+ "clamp_prob": 1.
815
+ },
816
+ # TODO: Change max_msa_clusters and max_extra_msa to multimer feats within model:
817
+ # c.model.input_embedder.num_msa = 508
818
+ # c.model.extra_msa.extra_msa_embedder.num_extra_msa = 2048
819
+ "predict": {
820
+ "max_msa_clusters": 508,
821
+ "max_extra_msa": 2048
822
+ },
823
+ "eval": {
824
+ "max_msa_clusters": 508,
825
+ "max_extra_msa": 2048
826
+ },
827
+ "train": {
828
+ "max_msa_clusters": 508,
829
+ "max_extra_msa": 2048,
830
+ "block_delete_msa" : False,
831
+ "crop_size": 640,
832
+ "spatial_crop_prob": 0.5,
833
+ "interface_threshold": 10.,
834
+ "clamp_prob": 1.,
835
+ },
836
+ },
837
+ "model": {
838
+ "input_embedder": {
839
+ "tf_dim": 21,
840
+ #"num_msa": 508,
841
+ "max_relative_chain": 2,
842
+ "max_relative_idx": 32,
843
+ "use_chain_relative": True
844
+ },
845
+ "template": {
846
+ "template_single_embedder": {
847
+ "c_in": 34,
848
+ "c_out": c_m
849
+ },
850
+ "template_pair_embedder": {
851
+ "c_in": c_z,
852
+ "c_out": c_t,
853
+ "c_dgram": 39,
854
+ "c_aatype": 22
855
+ },
856
+ "template_pair_stack": {
857
+ "tri_mul_first": True,
858
+ "fuse_projection_weights": True
859
+ },
860
+ "c_t": c_t,
861
+ "c_z": c_z,
862
+ "use_unit_vector": True
863
+ },
864
+ "extra_msa": {
865
+ # "extra_msa_embedder": {
866
+ # "num_extra_msa": 2048
867
+ # },
868
+ "extra_msa_stack": {
869
+ "opm_first": True,
870
+ "fuse_projection_weights": True
871
+ }
872
+ },
873
+ "evoformer_stack": {
874
+ "opm_first": True,
875
+ "fuse_projection_weights": True
876
+ },
877
+ "structure_module": {
878
+ "trans_scale_factor": 20
879
+ },
880
+ "heads": {
881
+ "tm": {
882
+ "ptm_weight": 0.2,
883
+ "iptm_weight": 0.8,
884
+ "enabled": True
885
+ },
886
+ "masked_msa": {
887
+ "c_out": 22
888
+ },
889
+ },
890
+ "recycle_early_stop_tolerance": 0.5 # For training, value is -1.
891
+ },
892
+ "loss": {
893
+ "fape": {
894
+ "intra_chain_backbone": {
895
+ "clamp_distance": 10.0,
896
+ "loss_unit_distance": 10.0,
897
+ "weight": 0.5
898
+ },
899
+ "interface_backbone": {
900
+ "clamp_distance": 30.0,
901
+ "loss_unit_distance": 20.0,
902
+ "weight": 0.5
903
+ }
904
+ },
905
+ "masked_msa": {
906
+ "num_classes": 22
907
+ },
908
+ "violation": {
909
+ "average_clashes": True,
910
+ "weight": 0.03 # Not finetuning
911
+ },
912
+ "tm": {
913
+ "weight": 0.1,
914
+ "enabled": True
915
+ },
916
+ "chain_center_of_mass": {
917
+ "weight": 0.05,
918
+ "enabled": True
919
+ }
920
+ }
921
+ })
922
+
923
+
924
+ seq_mode_config = mlc.ConfigDict({
925
+ "data": {
926
+ "common": {
927
+ "feat": {
928
+ "seq_embedding": [NUM_RES, None],
929
+ },
930
+ "seqemb_features": [ # List of features to be generated in seqemb mode
931
+ "seq_embedding"
932
+ ],
933
+ },
934
+ "seqemb_mode": { # Configuration for sequence embedding mode
935
+ "enabled": True, # If True, use seq emb instead of MSA
936
+ },
937
+ },
938
+ "globals": {
939
+ "seqemb_mode_enabled": True,
940
+ },
941
+ "model": {
942
+ "preembedding_embedder": { # Used in sequence embedding mode
943
+ "tf_dim": 22,
944
+ "preembedding_dim": preemb_dim_size,
945
+ "c_z": c_z,
946
+ "c_m": c_m,
947
+ "relpos_k": 32,
948
+ },
949
+ "extra_msa": {
950
+ "enabled": False # Disable Extra MSA Stack
951
+ },
952
+ "evoformer_stack": {
953
+ "no_column_attention": True # Turn off Evoformer's column attention
954
+ },
955
+ }
956
+ })
config/config.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model:
2
+ name: OpenFold
3
+ package_path: ../model/openfold
4
+ base_dependency: OneScience
5
+
6
+ runtime:
7
+ pythonpath: ../model
8
+ onescience_root_env: ONESCIENCE_ROOT
9
+ default_config_preset: model_1_ptm
10
+ device: cuda:0
11
+
12
+ train:
13
+ data_dir: ../data/mmcif
14
+ alignment_dir: ../data/alignments
15
+ template_mmcif_dir: ../data/templates/mmcif
16
+ output_dir: ../outputs/train
17
+ checkpoint_dir: ../weight
18
+ deepspeed_config: ./deepspeed_config.json
19
+
20
+ inference:
21
+ fasta_dir: ../data/fasta
22
+ template_mmcif_dir: ../data/templates/mmcif
23
+ precomputed_alignment_dir: ../data/alignments
24
+ output_dir: ../outputs/inference
25
+ checkpoint_path: ../weight/openfold.pt
config/deepspeed_config.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "fp16": {
3
+ "enabled": false,
4
+ "min_loss_scale": 1
5
+ },
6
+ "amp": {
7
+ "enabled": false,
8
+ "opt_level": "O2"
9
+ },
10
+ "bfloat16": {
11
+ "enabled": true
12
+ },
13
+ "zero_optimization": {
14
+ "stage": 2,
15
+ "offload_optimizer": {
16
+ "device": "cpu"
17
+ },
18
+ "contiguous_gradients": true
19
+ },
20
+ "activation_checkpointing": {
21
+ "partition_activations": true,
22
+ "cpu_checkpointing": false,
23
+ "profile": false
24
+ },
25
+ "gradient_clipping": 0.1,
26
+ "zero_force_ds_cpu_optimizer": false
27
+ }
configuration.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_name": "OpenFold",
3
+ "framework": "PyTorch",
4
+ "task": "protein-structure-prediction",
5
+ "description": "Lightweight OpenFold wrapper package. Scripts, model code, config and weight directory are local; datapipes and utils are provided by the OneScience base package.",
6
+ "entry_points": {
7
+ "train": "scripts/train.py",
8
+ "inference": "scripts/inference.py",
9
+ "thread_sequence": "scripts/thread_sequence.py"
10
+ },
11
+ "source_package": "model/openfold",
12
+ "base_dependency": "onescience",
13
+ "config": "config/config.yaml",
14
+ "weight_dir": "weight",
15
+ "license": "Apache-2.0"
16
+ }
data/demo/monomer/alignments/6KWC_1/bfd_uniref_hits.a3m ADDED
The diff for this file is too large to render. See raw diff
 
data/demo/monomer/alignments/6KWC_1/hhsearch_output.hhr ADDED
The diff for this file is too large to render. See raw diff
 
data/demo/monomer/alignments/6KWC_1/mgnify_hits.sto ADDED
The diff for this file is too large to render. See raw diff
 
data/demo/monomer/alignments/6KWC_1/uniref90_hits.sto ADDED
The diff for this file is too large to render. See raw diff
 
data/demo/monomer/fasta_dir/6kwc.fasta ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ >6KWC_1
2
+ GSTIQPGTGYNNGYFYSYWNDGHGGVTYTNGPGGQFSVNWSNSGEFVGGKGWQPGTKNKVINFSGSYNPNGNSYLSVYGWSRNPLIEYYIVENFGTYNPSTGATKLGEVTSDGSVYDIYRTQRVNQPSIIGTATFYQYWSVRRNHRSSGSVNTANHFNAWAQQGLTLGTMDYQIVAVQGYFSSGSASITVS
data/demo/monomer/inference.sh ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
5
+ export PYTHONPATH="${ROOT_DIR}/model:${PYTHONPATH:-}"
6
+
7
+ FASTA_DIR="${ROOT_DIR}/data/demo/monomer/fasta_dir"
8
+ OUTPUT_DIR="${ROOT_DIR}/outputs/demo_monomer"
9
+ PRECOMPUTED_ALIGNMENT_DIR="${ROOT_DIR}/data/demo/monomer/alignments"
10
+ TEMPLATE_MMCIF_DIR="${TEMPLATE_MMCIF_DIR:-${ROOT_DIR}/data/databases/pdb_mmcif/mmcif_files}"
11
+ CHECKPOINT_PATH="${CHECKPOINT_PATH:-${ROOT_DIR}/weight/openfold.pt}"
12
+
13
+ python "${ROOT_DIR}/scripts/inference.py" "${FASTA_DIR}" "${TEMPLATE_MMCIF_DIR}" \
14
+ --output_dir "${OUTPUT_DIR}" \
15
+ --config_preset model_1_ptm \
16
+ --model_device "${MODEL_DEVICE:-cuda:0}" \
17
+ --data_random_seed 42 \
18
+ --use_precomputed_alignments "${PRECOMPUTED_ALIGNMENT_DIR}" \
19
+ --openfold_checkpoint_path "${CHECKPOINT_PATH}" \
20
+ --skip_relaxation
model/openfold/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ 
model/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)
model/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
model/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
model/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
model/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
model/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
model/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
+
model/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
model/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 onescience.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
model/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
+ )
model/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
model/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)
model/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)
model/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
+
scripts/__init__.py ADDED
File without changes
scripts/activate_conda_env.sh ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # source scripts/vars.sh
4
+
5
+ # source lib/conda/etc/profile.d/conda.sh
6
+ # conda activate $ENV_NAME
scripts/alignment_data_to_fasta.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This script generates a FASTA file for all chains in an alignment directory or
3
+ alignment DB.
4
+ """
5
+
6
+ import json
7
+ from argparse import ArgumentParser
8
+ from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ from tqdm import tqdm
13
+
14
+
15
+ def chain_dir_to_fasta(dir: Path) -> str:
16
+ """
17
+ Generates a FASTA string from a chain directory.
18
+ """
19
+ # take some alignment file
20
+ for alignment_file_type in [
21
+ "mgnify_hits.a3m",
22
+ "uniref90_hits.a3m",
23
+ "bfd_uniclust_hits.a3m",
24
+ ]:
25
+ alignment_file = dir / alignment_file_type
26
+ if alignment_file.exists():
27
+ break
28
+
29
+ with open(alignment_file, "r") as f:
30
+ next(f) # skip the first line
31
+ seq = next(f).strip()
32
+
33
+ try:
34
+ next_line = next(f)
35
+ except StopIteration:
36
+ pass
37
+ else:
38
+ assert next_line.startswith(">") # ensure that sequence ended
39
+
40
+ chain_id = dir.name
41
+
42
+ return f">{chain_id}\n{seq}\n"
43
+
44
+
45
+ def index_entry_to_fasta(index_entry: dict, db_dir: Path, chain_id: str) -> str:
46
+ """
47
+ Generates a FASTA string from an alignment-db index entry.
48
+ """
49
+ db_file = db_dir / index_entry["db"]
50
+
51
+ # look for an alignment file
52
+ for alignment_file_type in [
53
+ "mgnify_hits.a3m",
54
+ "uniref90_hits.a3m",
55
+ "bfd_uniclust_hits.a3m",
56
+ ]:
57
+ for file_info in index_entry["files"]:
58
+ if file_info[0] == alignment_file_type:
59
+ start, size = file_info[1], file_info[2]
60
+ break
61
+
62
+ with open(db_file, "rb") as f:
63
+ f.seek(start)
64
+ msa_lines = f.read(size).decode("utf-8").splitlines()
65
+ seq = msa_lines[1]
66
+
67
+ try:
68
+ next_line = msa_lines[2]
69
+ except IndexError:
70
+ pass
71
+ else:
72
+ assert next_line.startswith(">") # ensure that sequence ended
73
+
74
+ return f">{chain_id}\n{seq}\n"
75
+
76
+
77
+ def main(
78
+ output_path: Path, alignment_db_index: Optional[Path], alignment_dir: Optional[Path]
79
+ ) -> None:
80
+ """
81
+ Generate a FASTA file from either an alignment-db index or a chain directory using multi-threading.
82
+ """
83
+ fasta = []
84
+
85
+ if alignment_dir and alignment_db_index:
86
+ raise ValueError(
87
+ "Only one of alignment_db_index and alignment_dir can be provided."
88
+ )
89
+
90
+ if alignment_dir:
91
+ print("Creating FASTA from alignment directory...")
92
+ chain_dirs = list(alignment_dir.iterdir())
93
+
94
+ with ThreadPoolExecutor() as executor:
95
+ futures = [
96
+ executor.submit(chain_dir_to_fasta, chain_dir)
97
+ for chain_dir in chain_dirs
98
+ ]
99
+ for future in tqdm(as_completed(futures), total=len(chain_dirs)):
100
+ fasta.append(future.result())
101
+
102
+ elif alignment_db_index:
103
+ print("Creating FASTA from alignment dbs...")
104
+
105
+ with open(alignment_db_index, "r") as f:
106
+ index = json.load(f)
107
+
108
+ db_dir = alignment_db_index.parent
109
+
110
+ with ThreadPoolExecutor() as executor:
111
+ futures = [
112
+ executor.submit(index_entry_to_fasta, index_entry, db_dir, chain_id)
113
+ for chain_id, index_entry in index.items()
114
+ ]
115
+ for future in tqdm(as_completed(futures), total=len(index)):
116
+ fasta.append(future.result())
117
+ else:
118
+ raise ValueError("Either alignment_db_index or alignment_dir must be provided.")
119
+
120
+ with open(output_path, "w") as f:
121
+ f.write("".join(fasta))
122
+ print(f"FASTA file written to {output_path}.")
123
+
124
+
125
+ if __name__ == "__main__":
126
+ parser = ArgumentParser(description=__doc__)
127
+ parser.add_argument(
128
+ "output_path",
129
+ type=Path,
130
+ help="Path to output FASTA file.",
131
+ )
132
+ parser.add_argument(
133
+ "--alignment_db_index",
134
+ type=Path,
135
+ help="Path to alignment-db index file.",
136
+ )
137
+ parser.add_argument(
138
+ "--alignment_dir",
139
+ type=Path,
140
+ help="Path to alignment directory.",
141
+ )
142
+
143
+ args = parser.parse_args()
144
+ main(args.output_path, args.alignment_db_index, args.alignment_dir)
scripts/alignment_db_scripts/add_non_unique_to_alignment_db.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from argparse import ArgumentParser
2
+ from pathlib import Path
3
+ import json
4
+
5
+
6
+ def main(args):
7
+ # get the super index
8
+ with open(args.alignment_db_super_index_path, "r") as fp:
9
+ super_index = json.load(fp)
10
+
11
+ # get all chains and sequences
12
+ chains_to_seqs = {}
13
+ with open(args.all_chains_fasta, "r") as fp:
14
+ lines = fp.readlines()
15
+
16
+ # iterate through chain-sequence pairs
17
+ for chain_idx in range(0, len(lines), 2):
18
+ chain = lines[chain_idx][1:].strip()
19
+ seq = lines[chain_idx + 1].strip()
20
+ chains_to_seqs[chain] = seq
21
+
22
+ chains_w_alignments = set(super_index.keys())
23
+ chains_wo_alignments = set(chains_to_seqs.keys()) - chains_w_alignments
24
+
25
+ seq_to_chain_w_alignment = {
26
+ chains_to_seqs[chain]: chain for chain in chains_w_alignments
27
+ }
28
+
29
+ print("Unique sequences with alignments:", len(seq_to_chain_w_alignment))
30
+
31
+ # map chain without alignment to alignment entry of another chain with the
32
+ # same sequence
33
+ remaining_unaligned_chains = []
34
+ for chain in chains_wo_alignments:
35
+ seq = chains_to_seqs[chain]
36
+
37
+ try:
38
+ corresponding_alignment = super_index[seq_to_chain_w_alignment[seq]]
39
+ # no corresponding chain with alignment found
40
+ except KeyError:
41
+ remaining_unaligned_chains.append(chain)
42
+ continue
43
+
44
+ super_index[chain] = corresponding_alignment
45
+
46
+ with open(args.output_path, "w") as fp:
47
+ json.dump(super_index, fp)
48
+
49
+ print(
50
+ f"No corresponding alignment found for the following {len(remaining_unaligned_chains)} chains:",
51
+ remaining_unaligned_chains,
52
+ )
53
+
54
+
55
+ if __name__ == "__main__":
56
+ parser = ArgumentParser(
57
+ description="""
58
+ If the alignment-db index was created on unique-chain alignments only,
59
+ this will add the missing chain entries to the super-index file based on
60
+ a .fasta file that contains sequences for all chains.
61
+
62
+ Note that this only modifies the index and not the database itself, as
63
+ the duplicate sequences will just point to the same alignments.
64
+ """
65
+ )
66
+ parser.add_argument(
67
+ "alignment_db_super_index_path",
68
+ type=Path,
69
+ help="Path to alignment-db super index file.",
70
+ )
71
+ parser.add_argument(
72
+ "output_path", type=Path, help="Write the output super index to this path."
73
+ )
74
+ parser.add_argument(
75
+ "all_chains_fasta",
76
+ type=Path,
77
+ help="Path to the fasta file containing sequences for all chains.",
78
+ )
79
+
80
+ args = parser.parse_args()
81
+
82
+ main(args)
scripts/alignment_db_scripts/create_alignment_db.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+
5
+
6
+ def main(args):
7
+ db_path = os.path.join(args.output_db_path, f"{args.output_db_name}.db")
8
+ index_path = os.path.join(
9
+ args.output_db_path, f"{args.output_db_name}.index"
10
+ )
11
+ db_fp = open(db_path, "wb")
12
+ index = {}
13
+ db_offset = 0
14
+ for chain_alignment_dir in os.listdir(args.alignment_dir):
15
+ cad_path = os.path.join(args.alignment_dir, chain_alignment_dir)
16
+ for f in os.listdir(cad_path):
17
+ f_path = os.path.join(cad_path, f)
18
+ with open(f_path, "rb") as fp:
19
+ file_bytes = fp.read()
20
+
21
+ l = len(file_bytes)
22
+ file_list = index.setdefault(chain_alignment_dir, [])
23
+ file_list.append((f, db_offset, l))
24
+
25
+ db_fp.write(file_bytes)
26
+ db_offset += l
27
+
28
+ db_fp.close()
29
+
30
+ with open(index_path, "w") as fp:
31
+ json.dump(index, fp)
32
+
33
+
34
+
35
+ if __name__ == "__main__":
36
+ parser = argparse.ArgumentParser()
37
+ parser.add_argument(
38
+ "alignment_dir", type=str,
39
+ help="""Path to precomputed alignment directory, with one subdirectory
40
+ per chain."""
41
+ )
42
+ parser.add_argument("output_db_path", type=str)
43
+ parser.add_argument("output_db_name", type=str)
44
+
45
+ args = parser.parse_args()
46
+
47
+ main(args)
scripts/alignment_db_scripts/create_alignment_db_sharded.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This is a modified version of the create_alignment_db.py script in OpenFold
3
+ which supports sharding into multiple files. The created index is already a
4
+ super index, meaning that "unify_alignment_db_indices.py" does not need to be
5
+ run on the output index. Additionally this script uses threading and
6
+ multiprocessing and is much faster than the old version.
7
+ """
8
+
9
+ import argparse
10
+ import json
11
+ from collections import defaultdict
12
+ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
13
+ from math import ceil
14
+ from multiprocessing import cpu_count
15
+ from pathlib import Path
16
+
17
+ from tqdm import tqdm
18
+
19
+
20
+ def split_file_list(file_list: list[Path], n_shards: int):
21
+ """
22
+ Split up the total file list into n_shards sublists.
23
+ """
24
+ split_list = []
25
+
26
+ for i in range(n_shards):
27
+ split_list.append(file_list[i::n_shards])
28
+
29
+ assert len([f for sublist in split_list for f in sublist]) == len(file_list)
30
+
31
+ return split_list
32
+
33
+
34
+ def chunked_iterator(lst: list, chunk_size: int):
35
+ """Iterate over a list in chunks of size chunk_size."""
36
+ for i in range(0, len(lst), chunk_size):
37
+ yield lst[i : i + chunk_size]
38
+
39
+
40
+ def read_chain_dir(chain_dir: Path) -> dict:
41
+ """
42
+ Read all alignment files in a single chain directory and return a dict
43
+ mapping chain name to file names and bytes.
44
+ """
45
+ if not chain_dir.is_dir():
46
+ raise ValueError(f"chain_dir must be a directory, but is {chain_dir}")
47
+
48
+ # ensure that PDB IDs are all lowercase
49
+ pdb_id, chain = chain_dir.name.split("_")
50
+ pdb_id = pdb_id.lower()
51
+ chain_name = f"{pdb_id}_{chain}"
52
+
53
+ file_data = []
54
+
55
+ for file_path in sorted(chain_dir.iterdir()):
56
+ file_name = file_path.name
57
+
58
+ with open(file_path, "rb") as file:
59
+ file_bytes = file.read()
60
+
61
+ file_data.append((file_name, file_bytes))
62
+
63
+ return {chain_name: file_data}
64
+
65
+
66
+ def process_chunk(chain_files: list[Path]) -> dict:
67
+ """
68
+ Returns the file names and bytes for all chains in a chunk of files.
69
+ """
70
+ chunk_data = {}
71
+
72
+ with ThreadPoolExecutor() as executor:
73
+ for file_data in executor.map(read_chain_dir, chain_files):
74
+ chunk_data.update(file_data)
75
+
76
+ return chunk_data
77
+
78
+
79
+ def create_index_default_dict() -> dict:
80
+ """
81
+ Returns a default dict for the index entries).
82
+ """
83
+ return {"db": None, "files": []}
84
+
85
+
86
+ def create_shard(
87
+ shard_files: list[Path], output_dir: Path, output_name: str, shard_num: int
88
+ ) -> dict:
89
+ """
90
+ Creates a single shard of the alignment database, and returns the
91
+ corresponding indices for the super index.
92
+ """
93
+ CHUNK_SIZE = 200
94
+ shard_index = defaultdict(
95
+ create_index_default_dict
96
+ ) # e.g. {chain_name: {db: str, files: [(file_name, db_offset, file_length)]}, ...}
97
+ chunk_iter = chunked_iterator(shard_files, CHUNK_SIZE)
98
+
99
+ pbar_desc = f"Shard {shard_num}"
100
+ output_path = output_dir / f"{output_name}_{shard_num}.db"
101
+
102
+ db_offset = 0
103
+ db_file = open(output_path, "wb")
104
+ for files_chunk in tqdm(
105
+ chunk_iter,
106
+ total=ceil(len(shard_files) / CHUNK_SIZE),
107
+ desc=pbar_desc,
108
+ position=shard_num,
109
+ leave=False,
110
+ ):
111
+ # get processed files for one chunk
112
+ chunk_data = process_chunk(files_chunk)
113
+
114
+ # write to db and store info in index
115
+ for chain_name, file_data in chunk_data.items():
116
+ shard_index[chain_name]["db"] = output_path.name
117
+
118
+ for file_name, file_bytes in file_data:
119
+ file_length = len(file_bytes)
120
+ shard_index[chain_name]["files"].append(
121
+ (file_name, db_offset, file_length)
122
+ )
123
+ db_file.write(file_bytes)
124
+ db_offset += file_length
125
+ db_file.close()
126
+
127
+ return shard_index
128
+
129
+
130
+ def main(args):
131
+ alignment_dir = args.alignment_dir
132
+ output_dir = args.output_db_path
133
+ output_dir.mkdir(exist_ok=True, parents=True)
134
+ output_db_name = args.output_db_name
135
+ n_shards = args.n_shards
136
+
137
+ n_cpus = cpu_count()
138
+ if n_shards > n_cpus:
139
+ print(
140
+ f"Warning: Your number of shards ({n_shards}) is greater than the number of cores on your machine ({n_cpus}). "
141
+ "This may result in slower performance. Consider using a smaller number of shards."
142
+ )
143
+
144
+ # get all chain dirs in alignment_dir
145
+ print("Getting chain directories...")
146
+ all_chain_dirs = sorted([f for f in tqdm(alignment_dir.iterdir())])
147
+
148
+ # split chain dirs into n_shards sublists
149
+ chain_dir_shards = split_file_list(all_chain_dirs, n_shards)
150
+
151
+ # total index for all shards
152
+ super_index = {}
153
+
154
+ # create a shard for each sublist
155
+ print(f"Creating {n_shards} alignment-db files...")
156
+ with ProcessPoolExecutor() as executor:
157
+ futures = [
158
+ executor.submit(
159
+ create_shard, shard_files, output_dir, output_db_name, shard_index
160
+ )
161
+ for shard_index, shard_files in enumerate(chain_dir_shards)
162
+ ]
163
+
164
+ for future in as_completed(futures):
165
+ shard_index = future.result()
166
+ super_index.update(shard_index)
167
+ print("\nCreated all shards.")
168
+
169
+ if args.duplicate_chains_file:
170
+ print("Extending super index with duplicate chains...")
171
+ duplicates_added = 0
172
+ with open(args.duplicate_chains_file, "r") as fp:
173
+ duplicate_chains = [line.strip().split() for line in fp]
174
+
175
+ for chains in duplicate_chains:
176
+ # find representative with alignment
177
+ for chain in chains:
178
+ if chain in super_index:
179
+ representative_chain = chain
180
+ break
181
+ else:
182
+ print(f"No representative chain found for {chains}, skipping...")
183
+ continue
184
+
185
+ # add duplicates to index
186
+ for chain in chains:
187
+ if chain != representative_chain:
188
+ super_index[chain] = super_index[representative_chain]
189
+ duplicates_added += 1
190
+
191
+ print(f"Added {duplicates_added} duplicate chains to index.")
192
+
193
+ # write super index to file
194
+ print("\nWriting super index...")
195
+ index_path = output_dir / f"{output_db_name}.index"
196
+ with open(index_path, "w") as fp:
197
+ json.dump(super_index, fp, indent=4)
198
+
199
+ print("Done.")
200
+
201
+
202
+ if __name__ == "__main__":
203
+ parser = argparse.ArgumentParser(
204
+ description="""
205
+ This script creates an alignment database format from a directory of
206
+ precomputed alignments. For better file system health, the total
207
+ database is split into n_shards files, where each shard contains a
208
+ subset of the total alignments. The output is a directory containing the
209
+ n_shards database files, and a single index file mapping chain names to
210
+ the database file and byte offsets for each alignment file.
211
+
212
+ Note: For optimal performance, your machine should have at least as many
213
+ cores as shards you want to create.
214
+ """
215
+ )
216
+ parser.add_argument(
217
+ "alignment_dir",
218
+ type=Path,
219
+ help="""Path to precomputed flattened alignment directory, with one
220
+ subdirectory per chain.""",
221
+ )
222
+ parser.add_argument("output_db_path", type=Path)
223
+ parser.add_argument("output_db_name", type=str)
224
+ parser.add_argument(
225
+ "--n_shards",
226
+ type=int,
227
+ help="Number of shards to split the database into",
228
+ default=10,
229
+ )
230
+ parser.add_argument(
231
+ "--duplicate_chains_file",
232
+ type=Path,
233
+ help="""
234
+ Optional path to file containing duplicate chain information, where each
235
+ line contains chains that are 100% sequence identical. If provided,
236
+ duplicate chains will be added to the index and point to the same
237
+ underlying database entry as their representatives in the alignment dir.
238
+ """,
239
+ default=None,
240
+ )
241
+
242
+ args = parser.parse_args()
243
+
244
+ main(args)
scripts/alignment_db_scripts/unify_alignment_db_indices.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ import os
4
+
5
+
6
+ """ Unifies databases created with create_alignment_db.py """
7
+
8
+
9
+ def main(args):
10
+ super_index = {}
11
+ for f in os.listdir(args.alignment_db_dir):
12
+ if(not os.path.splitext(f)[-1] == ".index"):
13
+ continue
14
+
15
+ with open(os.path.join(args.alignment_db_dir, f), "r") as fp:
16
+ index = json.load(fp)
17
+
18
+ db_name = f"{os.path.splitext(f)[0]}.db"
19
+
20
+ for k in index:
21
+ super_index[k] = {
22
+ "db": db_name,
23
+ "files": index[k],
24
+ }
25
+
26
+ with open(os.path.join(args.output_dir, "super.index"), "w") as fp:
27
+ json.dump(super_index, fp)
28
+
29
+
30
+ if __name__ == "__main__":
31
+ parser = argparse.ArgumentParser()
32
+ parser.add_argument("alignment_db_dir", type=str, help="Path to directory containing alignment_dbs")
33
+ parser.add_argument("output_dir", type=str, help="Path in which to output super index")
34
+
35
+ args = parser.parse_args()
36
+
37
+ main(args)
scripts/build_deepspeed_config.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import argparse
16
+ import json
17
+
18
+
19
+ parser = argparse.ArgumentParser(description='''Outputs a DeepSpeed
20
+ configuration file to
21
+ stdout''')
22
+
23
+ parser.add_argument("--gradient_clipping", type=float, default=None,
24
+ help="Value of gradient clipping")
25
+ p = parser.add_argument_group("Optimizer")
26
+ p.add_argument("--optimizer", default=None,
27
+ help='''Choice of optimizer. Choose between "Adam" or
28
+ "OneBitAdam"''')
29
+ p.add_argument("--lr", dest="lr", type=float, default=1e-3,
30
+ help="The learning rate")
31
+ p.add_argument("--freeze_step", type=int, default=100,
32
+ help='''Number of warm-up steps before 1-bit compression
33
+ activates. Applies only when --optimizer is
34
+ OneBitAdam''')
35
+ p.add_argument("--cuda_aware", action="store_true", default=False,
36
+ help='''Indicates that the underlying MPI library supports
37
+ CUDA-Aware communication. Applies only when
38
+ --optimizer is OneBitAdam''')
39
+ p.add_argument("--comm_backend_name", type=str, default="nccl",
40
+ help='''Communication implementation for OneBitAdam. Choose
41
+ from nccl and mpi''')
42
+ p.add_argument("--eps", type=float, default=1e-8,
43
+ help="Adam epsilon parameter")
44
+
45
+
46
+ sched = parser.add_argument_group("Scheduler")
47
+ sched.add_argument(
48
+ "--scheduler", type=str, default=None,
49
+ help='''The LR scheduler. Choose from "LRRangeTest", "OneCycle", WarmupLR,
50
+ and WarmupDecayLR". Documentation for each can be found here:
51
+ deepspeed.readthedocs.io/en/latest/schedulers.html'''
52
+ )
53
+
54
+ range_test = sched.add_argument_group("LRRangeTest")
55
+ range_test.add_argument(
56
+ "--lr_range_test_min_lr", type=float, default=1e-04
57
+ )
58
+ range_test.add_argument(
59
+ "--lr_range_test_step_size", type=int, default=2000
60
+ )
61
+ range_test.add_argument(
62
+ "--lr_range_test_step_rate", type=float, default=1.0
63
+ )
64
+ range_test.add_argument(
65
+ "--lr_range_test_staircase", type=bool, default=False
66
+ )
67
+
68
+ cycle = sched.add_argument_group("OneCycle")
69
+ cycle.add_argument(
70
+ "--cycle_min_lr", type=float, default=1e-06
71
+ )
72
+ cycle.add_argument(
73
+ "--cycle_max_lr", type=float, default=1e-03
74
+ )
75
+ cycle.add_argument(
76
+ "--cycle_decay_lr_rate", type=float, default=0
77
+ )
78
+ cycle.add_argument(
79
+ "--cycle_first_step_size", type=int, default=2000
80
+ )
81
+ cycle.add_argument(
82
+ "--cycle_second_step_size", type=int, default=None
83
+ )
84
+ cycle.add_argument(
85
+ "--cycle_first_stair_count", type=int, default=0
86
+ )
87
+ cycle.add_argument(
88
+ "--cycle_second_stair_count", type=int, default=0
89
+ )
90
+ cycle.add_argument(
91
+ "--cycle_decay_step_size", type=int, default=0
92
+ )
93
+ cycle.add_argument(
94
+ "--cycle_momentum", type=bool, default=True
95
+ )
96
+ cycle.add_argument(
97
+ "--cycle_min_mom", type=float, default=0.8
98
+ )
99
+ cycle.add_argument(
100
+ "--cycle_max_mom", type=float, default=0.9
101
+ )
102
+ cycle.add_argument(
103
+ "--cycle_decay_mom_rate", type=float, default=0
104
+ )
105
+
106
+ warmup = sched.add_argument_group("WarmupLR")
107
+ warmup.add_argument(
108
+ "--warmup_min_lr", type=float, default=0.
109
+ )
110
+ warmup.add_argument(
111
+ "--warmup_max_lr", type=float, default=0.001
112
+ )
113
+ warmup.add_argument(
114
+ "--warmup_num_steps", type=int, default=1000
115
+ )
116
+
117
+ warmup_decay = sched.add_argument_group("WarmupDecayLR")
118
+ warmup_decay.add_argument(
119
+ "--warmup_decay_total_num_steps", type=int, default=1e05
120
+ )
121
+ warmup_decay.add_argument(
122
+ "--warmup_decay_min_lr", type=float, default=0.
123
+ )
124
+ warmup_decay.add_argument(
125
+ "--warmup_decay_max_lr", type=float, default=0.001
126
+ )
127
+ warmup_decay.add_argument(
128
+ "--warmup_decay_num_steps", type=int, default=1000
129
+ )
130
+
131
+
132
+ p = parser.add_argument_group("Half-precision training (fp16)")
133
+ p.add_argument("--fp16", dest="fp16", action="store_true", default=False,
134
+ help="""Whether to train in 16-bit/mixed-precision mode.
135
+ Mutually exclusive with --amp""")
136
+
137
+ p = parser.add_argument_group("Half-precision training (bfloat16)")
138
+ p.add_argument("--bfloat16", dest="bfloat16", action="store_true",
139
+ default=False,
140
+ help="""Whether to train in 16-bit bfloat16 mode. Mutually
141
+ exclusive with --amp and --fp16. Requires hardware
142
+ support""")
143
+
144
+ p = parser.add_argument_group("AMP")
145
+ p.add_argument("--amp", action="store_true", default=False,
146
+ help="""Whether to enable AMP training. Mutually exclusive with
147
+ --fp16""")
148
+ p.add_argument("--opt_level", action="store_true", default=False,
149
+ help="""AMP optimization level. One of "O0", "O1", "O2", or
150
+ "O3".""")
151
+
152
+ p = parser.add_argument_group("Activation checkpointing")
153
+ p.add_argument("--partition_activations", action="store_true",
154
+ default=False,
155
+ help="Activation checkpointing")
156
+ p.add_argument("--cpu_checkpointing", action="store_true", default=False,
157
+ help="Offload activation checkpoints to CPU")
158
+ p.add_argument("--profile", action="store_true",
159
+ default=False,
160
+ help="Whether to profile activation checkpointing")
161
+
162
+
163
+ p = parser.add_argument_group("ZeRO optimization")
164
+ p.add_argument("--zero_stage", type=int, default=2,
165
+ help="ZeRO optimizer stage")
166
+ p.add_argument("--allgather_partitions", action="store_true",
167
+ default=False,
168
+ help='''Allgather collective vs. broadcast collectives
169
+ for parameter gathering''')
170
+ p.add_argument("--allgather_bucket_size", type=int, default=1e9,
171
+ help="Number of elements allgathered at one time")
172
+ p.add_argument("--overlap_comm", action="store_true", default=False,
173
+ help='''Whether to overlap gradient reduction and backward
174
+ pass''')
175
+ p.add_argument("--reduce_scatter", action="store_true", default=False,
176
+ help="Use reduce to average gradients")
177
+ p.add_argument("--reduce_bucket_size", type=int, default=1e9,
178
+ help="Number of elements reduced at one time")
179
+ p.add_argument("--offload_optimizer", action="store_true", default=False,
180
+ help='''Offload optimizer state to CPU. Valid only when
181
+ --stage is 2 or 3''')
182
+ p.add_argument("--pin_memory", action="store_true", default=False,
183
+ help="Speeds up offloaded throughput at the cost of memory")
184
+
185
+ p = parser.add_argument_group("Flops profiler")
186
+ p.add_argument("--flops_profiler", action="store_true", default=False,
187
+ help="Whether to enable the DeepSpeed Flops Profiler")
188
+ p.add_argument("--profile_step", type=int, default=1,
189
+ help='''The global training step at which to run the flops
190
+ profiler. Has no effect unless --flops_profiler is
191
+ given''')
192
+ p.add_argument("--module_depth", type=int, default=-1,
193
+ help='''Depth to which aggregated module info is printed. Has
194
+ no effect unless --flops_profiler is given''')
195
+ p.add_argument("--top_modules", type=int, default=3,
196
+ help='''Number of top modules to print in the aggregated
197
+ profile. Has no effect unless --flops_profiler is
198
+ given''')
199
+ p.add_argument("--detailed_flops_profile", action="store_true",
200
+ default=False,
201
+ help='''Whether the flops_profiler should be detailed. Has
202
+ no effect unless --flops_profiler is given''')
203
+
204
+ args = parser.parse_args()
205
+
206
+ d = {}
207
+
208
+ # Optimizer settings
209
+ if(args.optimizer is not None):
210
+ optimizer = {}
211
+ optimizer["type"] = args.optimizer
212
+ params = {}
213
+ params["lr"] = args.lr
214
+ params["eps"] = args.eps
215
+
216
+ if(args.optimizer == "OneBitAdam"):
217
+ params["freeze_step"] = args.freeze_step
218
+ params["cuda_aware"] = args.cuda_aware
219
+ params["comm_backend_name"] = args.comm_backend_name
220
+
221
+ optimizer["params"] = params
222
+ d["optimizer"] = optimizer
223
+
224
+ # LR scheduler
225
+ if(args.scheduler is not None):
226
+ scheduler = {}
227
+ scheduler["type"] = args.scheduler
228
+ params = {}
229
+ if(args.scheduler == "LRRangeTest"):
230
+ params["lr_range_test_min_lr"] = args.lr_range_test_min_lr
231
+ params["lr_range_test_step_size"] = args.lr_range_test_step_size
232
+ params["lr_range_test_step_rate"] = args.lr_range_test_step_rate
233
+ params["lr_range_test_staircase"] = args.lr_range_test_staircase
234
+ elif(args.scheduler == "OneCycle"):
235
+ params["cycle_min_lr"] = args.cycle_min_lr
236
+ params["cycle_max_lr"] = args.cycle_max_lr
237
+ params["decay_lr_rate"] = args.cycle_decay_lr_rate
238
+ params["cycle_first_step_size"] = args.cycle_first_step_size
239
+ params["cycle_second_step_size"] = args.cycle_second_step_size
240
+ params["cycle_first_stair_count"] = args.cycle_first_stair_count
241
+ params["cycle_second_stair_count"] = args.cycle_second_stair_count
242
+ params["cycle_momentum"] = args.cycle_momentum
243
+ params["cycle_min_mom"] = args.cycle_min_mom
244
+ params["cycle_max_mom"] = args.cycle_max_mom
245
+ params["decay_mom_rate"] = args.cycle_decay_mom_rate
246
+ elif(args.scheduler == "WarmupLR"):
247
+ params["warmup_min_lr"] = args.warmup_min_lr
248
+ params["warmup_max_lr"] = args.warmup_max_lr
249
+ params["warmup_num_steps"] = args.warmup_num_steps
250
+ elif(args.scheduler == "WarmupDecayLR"):
251
+ params["warmup_min_lr"] = args.warmup_decay_min_lr
252
+ params["warmup_max_lr"] = args.warmup_decay_max_lr
253
+ params["warmup_num_steps"] = args.warmup_decay_num_steps
254
+ params["total_num_steps"] = args.warmup_decay_total_num_steps
255
+ else:
256
+ raise ValueError("Invalid scheduler")
257
+
258
+ scheduler["params"] = params
259
+ d["scheduler"] = scheduler
260
+
261
+ # 16-bit training
262
+ if(sum([args.amp, args.fp16, args.bfloat16]) > 1):
263
+ raise ValueError("Only one of --fp16, --amp, or --bfloat16 can be enabled")
264
+
265
+ if(args.amp):
266
+ amp = {}
267
+ amp["enabled"] = True
268
+ amp["pin_memory"] = args.opt_level
269
+ d["amp"] = amp
270
+ elif(args.fp16):
271
+ fp16 = {}
272
+ fp16["enabled"] = args.fp16
273
+ d["fp16"] = fp16
274
+ elif(args.bfloat16):
275
+ bfloat16 = {}
276
+ bfloat16["enabled"] = args.bfloat16
277
+ d["bfloat16"] = bfloat16
278
+
279
+ # Activation checkpointing
280
+ ac = {}
281
+ ac["partition_activations"] = args.partition_activations
282
+ ac["cpu_checkpointing"] = args.cpu_checkpointing
283
+ ac["profile"] = args.profile
284
+ d["activation_checkpointing"] = ac
285
+
286
+ # ZeRO optimization
287
+ zo = {}
288
+ zo["stage"] = args.zero_stage
289
+ zo["allgather_partitions"] = args.allgather_partitions
290
+ zo["allgather_bucket_size"] = args.allgather_bucket_size
291
+ zo["reduce_bucket_size"] = args.reduce_bucket_size
292
+ zo["overlap_comm"] = args.overlap_comm
293
+ zo["reduce_scatter"] = args.reduce_scatter
294
+
295
+ if(args.offload_optimizer):
296
+ oo = {}
297
+ oo["device"] = "cpu"
298
+ oo["pin_memory"] = args.pin_memory
299
+ zo["offload_optimizer"] = oo
300
+
301
+ d["zero_optimization"] = zo
302
+
303
+ # Flops Profiler
304
+ flops_profiler = {}
305
+ flops_profiler["enabled"] = args.flops_profiler
306
+ flops_profiler["profile_step"] = args.profile_step
307
+ flops_profiler["module_depth"] = args.module_depth
308
+ flops_profiler["top_modules"] = args.top_modules
309
+ flops_profiler["detailed"] = args.detailed_flops_profile
310
+ d ["flops_profiler"] = flops_profiler
311
+
312
+ if(args.gradient_clipping):
313
+ d["gradient_clipping"] = args.gradient_clipping
314
+
315
+ print(json.dumps(d, indent=2))
scripts/colabfold_search.sh ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash -e
2
+ # Copied from colabfold.mmseqs.com
3
+
4
+ MMSEQS="$1"
5
+ QUERY="$2"
6
+ DBBASE="$3"
7
+ BASE="$4"
8
+ DB1="$5"
9
+ DB2="$6"
10
+ DB3="$7"
11
+ USE_ENV="${8:-1}"
12
+ USE_TEMPLATES="${9:-0}"
13
+ FILTER="${10:-1}"
14
+ INDEX=${11:-1}
15
+ DB_LOAD_MODE="${12:-2}"
16
+ EXPAND_EVAL=inf
17
+ ALIGN_EVAL=10
18
+ DIFF=3000
19
+ QSC=-20.0
20
+ MAX_ACCEPT=1000000
21
+ if [ "${FILTER}" = "1" ]; then
22
+ # 0.1 was not used in benchmarks due to POSIX shell bug in line above
23
+ # EXPAND_EVAL=0.1
24
+ ALIGN_EVAL=10
25
+ QSC=0.8
26
+ MAX_ACCEPT=100000
27
+ fi
28
+ if [ "${INDEX}" = "1" ]; then
29
+ SEQ=".idx"
30
+ ALN=".idx"
31
+ IDX=".idx"
32
+ else
33
+ SEQ="_seq"
34
+ ALN="_aln"
35
+ IDX=""
36
+ export MMSEQS_IGNORE_INDEX=1
37
+ fi
38
+ export MMSEQS_CALL_DEPTH=1
39
+ SEARCH_PARAM="--num-iterations 3 --db-load-mode ${DB_LOAD_MODE} -a -s 8 -e 0.1 --max-seqs 10000"
40
+ FILTER_PARAM="--filter-msa ${FILTER} --filter-min-enable 1000 --diff ${DIFF} --qid 0.0,0.2,0.4,0.6,0.8,1.0 --qsc 0 --max-seq-id 0.95"
41
+ EXPAND_PARAM="--expansion-mode 0 -e ${EXPAND_EVAL} --expand-filter-clusters ${FILTER} --max-seq-id 0.95"
42
+ mkdir -p "${BASE}"
43
+ "${MMSEQS}" createdb "${QUERY}" "${BASE}/qdb"
44
+ "${MMSEQS}" search "${BASE}/qdb" "${DBBASE}/${DB1}" "${BASE}/res" "${BASE}/tmp" $SEARCH_PARAM
45
+ "${MMSEQS}" expandaln "${BASE}/qdb" "${DBBASE}/${DB1}${SEQ}" "${BASE}/res" "${DBBASE}/${DB1}${ALN}" "${BASE}/res_exp" --db-load-mode ${DB_LOAD_MODE} ${EXPAND_PARAM}
46
+ "${MMSEQS}" mvdb "${BASE}/tmp/latest/profile_1" "${BASE}/prof_res"
47
+ "${MMSEQS}" lndb "${BASE}/qdb_h" "${BASE}/prof_res_h"
48
+ "${MMSEQS}" align "${BASE}/prof_res" "${DBBASE}/${DB1}${SEQ}" "${BASE}/res_exp" "${BASE}/res_exp_realign" --db-load-mode ${DB_LOAD_MODE} -e ${ALIGN_EVAL} --max-accept ${MAX_ACCEPT} --alt-ali 10 -a
49
+ "${MMSEQS}" filterresult "${BASE}/qdb" "${DBBASE}/${DB1}${SEQ}" "${BASE}/res_exp_realign" "${BASE}/res_exp_realign_filter" --db-load-mode ${DB_LOAD_MODE} --qid 0 --qsc $QSC --diff 0 --max-seq-id 1.0 --filter-min-enable 100
50
+ "${MMSEQS}" result2msa "${BASE}/qdb" "${DBBASE}/${DB1}${SEQ}" "${BASE}/res_exp_realign_filter" "${BASE}/uniref.a3m" --msa-format-mode 6 --db-load-mode ${DB_LOAD_MODE} ${FILTER_PARAM}
51
+ "${MMSEQS}" rmdb "${BASE}/res_exp_realign"
52
+ "${MMSEQS}" rmdb "${BASE}/res_exp"
53
+ "${MMSEQS}" rmdb "${BASE}/res"
54
+ "${MMSEQS}" rmdb "${BASE}/res_exp_realign_filter"
55
+ if [ "${USE_TEMPLATES}" = "1" ]; then
56
+ "${MMSEQS}" search "${BASE}/prof_res" "${DBBASE}/${DB2}" "${BASE}/res_pdb" "${BASE}/tmp" --db-load-mode ${DB_LOAD_MODE} -s 7.5 -a -e 0.1
57
+ "${MMSEQS}" convertalis "${BASE}/prof_res" "${DBBASE}/${DB2}${IDX}" "${BASE}/res_pdb" "${BASE}/${DB2}.m8" --format-output query,target,fident,alnlen,mismatch,gapopen,qstart,qend,tstart,tend,evalue,bits,cigar --db-load-mode ${DB_LOAD_MODE}
58
+ "${MMSEQS}" rmdb "${BASE}/res_pdb"
59
+ fi
60
+ if [ "${USE_ENV}" = "1" ]; then
61
+ "${MMSEQS}" search "${BASE}/prof_res" "${DBBASE}/${DB3}" "${BASE}/res_env" "${BASE}/tmp" $SEARCH_PARAM
62
+ "${MMSEQS}" expandaln "${BASE}/prof_res" "${DBBASE}/${DB3}${SEQ}" "${BASE}/res_env" "${DBBASE}/${DB3}${ALN}" "${BASE}/res_env_exp" -e ${EXPAND_EVAL} --expansion-mode 0 --db-load-mode ${DB_LOAD_MODE}
63
+ "${MMSEQS}" align "${BASE}/tmp/latest/profile_1" "${DBBASE}/${DB3}${SEQ}" "${BASE}/res_env_exp" "${BASE}/res_env_exp_realign" --db-load-mode ${DB_LOAD_MODE} -e ${ALIGN_EVAL} --max-accept ${MAX_ACCEPT} --alt-ali 10 -a
64
+ "${MMSEQS}" filterresult "${BASE}/qdb" "${DBBASE}/${DB3}${SEQ}" "${BASE}/res_env_exp_realign" "${BASE}/res_env_exp_realign_filter" --db-load-mode ${DB_LOAD_MODE} --qid 0 --qsc $QSC --diff 0 --max-seq-id 1.0 --filter-min-enable 100
65
+ "${MMSEQS}" result2msa "${BASE}/qdb" "${DBBASE}/${DB3}${SEQ}" "${BASE}/res_env_exp_realign_filter" "${BASE}/bfd.mgnify30.metaeuk30.smag30.a3m" --msa-format-mode 6 --db-load-mode ${DB_LOAD_MODE} ${FILTER_PARAM}
66
+ "${MMSEQS}" rmdb "${BASE}/res_env_exp_realign_filter"
67
+ "${MMSEQS}" rmdb "${BASE}/res_env_exp_realign"
68
+ "${MMSEQS}" rmdb "${BASE}/res_env_exp"
69
+ "${MMSEQS}" rmdb "${BASE}/res_env"
70
+ fi
71
+ "${MMSEQS}" rmdb "${BASE}/qdb"
72
+ "${MMSEQS}" rmdb "${BASE}/qdb_h"
73
+ "${MMSEQS}" rmdb "${BASE}/res"
74
+ rm -f -- "${BASE}/prof_res"*
75
+ rm -rf -- "${BASE}/tmp"
scripts/convert_of_weights_to_jax.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2022 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
+ # Converts OpenFold .pt checkpoints into AlphaFold .npz ones, which can then be
16
+ # used to run inference using DeepMind's JAX code.
17
+ import argparse
18
+
19
+ import numpy as np
20
+ import torch
21
+
22
+ from onescience.configs.bio.openfold.config import model_config
23
+ from onescience.models.openfold.model import AlphaFold
24
+ from onescience.utils.openfold.import_weights import (
25
+ Param,
26
+ ParamType,
27
+ generate_translation_dict,
28
+ process_translation_dict,
29
+ import_openfold_weights_,
30
+ )
31
+ from onescience.utils.openfold.tensor_utils import tree_map
32
+
33
+
34
+ def reshape_fn(of_param, af_weight):
35
+ transformations = {
36
+ ParamType.LinearWeight: lambda w: w.transpose(-1, -2),
37
+ ParamType.LinearWeightMHA: lambda w: w.transpose(-1, -2).reshape(af_weight.shape),
38
+ ParamType.LinearMHAOutputWeight: lambda w: w.transpose(-1, -2).reshape(af_weight.shape),
39
+ ParamType.LinearBiasMHA: lambda w: w.reshape(af_weight.shape),
40
+ ParamType.LinearWeightOPM: lambda w: w.transpose(-1, -2).reshape(af_weight.shape),
41
+ ParamType.Other: lambda w: w,
42
+ }
43
+
44
+ if(of_param.stacked):
45
+ of_weight = torch.stack([torch.Tensor(p) for p in of_param.param])
46
+ else:
47
+ of_weight = torch.Tensor(of_param.param)
48
+
49
+ return transformations[of_param.param_type](of_weight)
50
+
51
+
52
+ def transfer(of_dict, af_weight_template):
53
+ for k in of_dict:
54
+ if(type(of_dict[k]) == dict):
55
+ transfer(of_dict[k], af_weight_template[k])
56
+ else:
57
+ reshaped = reshape_fn(of_dict[k], af_weight_template[k])
58
+ reshaped = reshaped.detach().numpy()
59
+ np.copyto(af_weight_template[k], reshaped)
60
+
61
+
62
+ def main(args):
63
+ d = torch.load(args.of_pt_path)
64
+
65
+ config = model_config(args.config_preset)
66
+ model = AlphaFold(config)
67
+ import_openfold_weights_(model=model, state_dict=d)
68
+
69
+ translation = generate_translation_dict(model, args.config_preset)
70
+ translation = process_translation_dict(translation)
71
+
72
+ af_weight_template = np.load(args.template_npz_path)
73
+ af_weight_template = {k:v for k,v in af_weight_template.items() if k in translation}
74
+ zero = lambda n: n * 0
75
+ af_weight_template = tree_map(zero, af_weight_template, np.ndarray)
76
+
77
+ transfer(translation, af_weight_template)
78
+
79
+ np.savez(args.out_path, **af_weight_template)
80
+
81
+
82
+ if __name__ == "__main__":
83
+ parser = argparse.ArgumentParser()
84
+ parser.add_argument(
85
+ "of_pt_path", type=str, help="Path to OpenFold .pt checkpoint file"
86
+ )
87
+ parser.add_argument(
88
+ "config_preset", type=str, help="The corresponding config preset"
89
+ )
90
+ parser.add_argument(
91
+ "out_path", type=str, help="Path for output .npz file"
92
+ )
93
+ parser.add_argument(
94
+ "--template_npz_path",
95
+ type=str,
96
+ default="openfold/resources/params/params_model_1_ptm.npz",
97
+ help="""Path to an AlphaFold checkpoint w/ a superset of the OF
98
+ checkpoint's parameters. params_model_1_ptm.npz always works.
99
+ """
100
+ )
101
+
102
+ args = parser.parse_args()
103
+
104
+ main(args)
scripts/convert_v1_to_v2_weights.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2022 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
+ # Converts OpenFold .pt checkpoints into AlphaFold .npz ones, which can then be
16
+ # used to run inference using DeepMind's JAX code.
17
+
18
+ import logging
19
+ import argparse
20
+ import os
21
+ import shutil
22
+ import torch
23
+
24
+ from onescience.utils.openfold.import_weights import convert_deprecated_v1_keys
25
+ from deepspeed.utils.zero_to_fp32 import (
26
+ get_optim_files, parse_optim_states, get_model_state_file
27
+ )
28
+
29
+
30
+ def convert_v1_to_v2_weights(args):
31
+ checkpoint_path = args.input_ckpt_path
32
+ is_dir = os.path.isdir(checkpoint_path)
33
+ if is_dir:
34
+ # A DeepSpeed checkpoint
35
+ logging.info(
36
+ 'Converting deepspeed checkpoint found at {args.input_checkpoint_path}')
37
+ state_dict_key = 'module'
38
+ latest_path = os.path.join(checkpoint_path, 'latest')
39
+ if os.path.isfile(latest_path):
40
+ with open(latest_path, 'r') as fd:
41
+ tag = fd.read().strip()
42
+ else:
43
+ raise ValueError(f"Unable to find 'latest' file at {latest_path}")
44
+
45
+ ds_checkpoint_dir = os.path.join(checkpoint_path, tag)
46
+ model_output_path = os.path.join(args.output_ckpt_path, tag)
47
+ optim_files = get_optim_files(ds_checkpoint_dir)
48
+ zero_stage, _, _ = parse_optim_states(optim_files, ds_checkpoint_dir)
49
+ model_file = get_model_state_file(ds_checkpoint_dir, zero_stage)
50
+ else:
51
+ # A Pytorch Lightning checkpoint
52
+ logging.info(
53
+ 'Converting pytorch lightning checkpoint found at {args.input_checkpoint_path}')
54
+ state_dict_key = 'state_dict'
55
+ model_output_path = args.output_ckpt_path
56
+ model_file = checkpoint_path
57
+
58
+ model_dict = torch.load(model_file, map_location=torch.device('cpu'))
59
+ model_dict[state_dict_key] = convert_deprecated_v1_keys(
60
+ model_dict[state_dict_key])
61
+
62
+ if 'ema' in model_dict:
63
+ ema_state_dict = model_dict['ema']['params']
64
+ model_dict['ema']['params'] = convert_deprecated_v1_keys(
65
+ ema_state_dict)
66
+
67
+ if is_dir:
68
+ param_shapes = convert_deprecated_v1_keys(
69
+ model_dict['param_shapes'][0])
70
+ model_dict['param_shapes'] = [param_shapes]
71
+
72
+ shutil.copytree(checkpoint_path, args.output_ckpt_path)
73
+ out_fname = os.path.join(
74
+ model_output_path, os.path.basename(model_file))
75
+
76
+ for optim_file in optim_files:
77
+ optim_dict = torch.load(optim_file)
78
+ new_optim_dict = optim_dict.copy()
79
+ new_optim_dict['optimizer_state_dict']['param_slice_mappings'][0] = convert_deprecated_v1_keys(
80
+ optim_dict['optimizer_state_dict']['param_slice_mappings'][0])
81
+ out_optim_fname = os.path.join(
82
+ model_output_path, os.path.basename(optim_file))
83
+ torch.save(new_optim_dict, out_optim_fname)
84
+ else:
85
+ out_fname = model_output_path
86
+
87
+ torch.save(model_dict, out_fname)
88
+
89
+
90
+ if __name__ == "__main__":
91
+ parser = argparse.ArgumentParser()
92
+ parser.add_argument("input_ckpt_path", type=str)
93
+ parser.add_argument("output_ckpt_path", type=str)
94
+
95
+ args = parser.parse_args()
96
+
97
+ convert_v1_to_v2_weights(args)
scripts/data_dir_to_fasta.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import logging
3
+ import os
4
+ import string
5
+ from collections import defaultdict
6
+ from onescience.datapipes.openfold import mmcif_parsing
7
+ from onescience.utils.openfold.np import protein, residue_constants
8
+
9
+
10
+ def main(args):
11
+ fasta = []
12
+ for fname in os.listdir(args.data_dir):
13
+ basename, ext = os.path.splitext(fname)
14
+ basename = basename.upper()
15
+ fpath = os.path.join(args.data_dir, fname)
16
+ if(ext == ".cif"):
17
+ with open(fpath, 'r') as fp:
18
+ mmcif_str = fp.read()
19
+
20
+ mmcif = mmcif_parsing.parse(
21
+ file_id=basename, mmcif_string=mmcif_str
22
+ )
23
+ if(mmcif.mmcif_object is None):
24
+ logging.warning(f'Failed to parse {fname}...')
25
+ if(args.raise_errors):
26
+ raise Exception(list(mmcif.errors.values())[0])
27
+ else:
28
+ continue
29
+
30
+ mmcif = mmcif.mmcif_object
31
+ for chain, seq in mmcif.chain_to_seqres.items():
32
+ chain_id = '_'.join([basename, chain])
33
+ fasta.append(f">{chain_id}")
34
+ fasta.append(seq)
35
+ elif(ext == ".pdb"):
36
+ with open(fpath, 'r') as fp:
37
+ pdb_str = fp.read()
38
+
39
+ protein_object = protein.from_pdb_string(pdb_str)
40
+ aatype = protein_object.aatype
41
+ chain_index = protein_object.chain_index
42
+
43
+ last_chain_index = chain_index[0]
44
+ chain_dict = defaultdict(list)
45
+ for i in range(aatype.shape[0]):
46
+ chain_dict[chain_index[i]].append(residue_constants.restypes_with_x[aatype[i]])
47
+
48
+ chain_tags = string.ascii_uppercase
49
+ for chain, seq in chain_dict.items():
50
+ chain_id = '_'.join([basename, chain_tags[chain]])
51
+ fasta.append(f">{chain_id}")
52
+ fasta.append(''.join(seq))
53
+
54
+ elif(ext == ".core"):
55
+ with open(fpath, 'r') as fp:
56
+ core_str = fp.read()
57
+
58
+ core_protein = protein.from_proteinnet_string(core_str)
59
+ aatype = core_protein.aatype
60
+ seq = ''.join([
61
+ residue_constants.restypes_with_x[aatype[i]]
62
+ for i in range(len(aatype))
63
+ ])
64
+ fasta.append(f">{basename}")
65
+ fasta.append(seq)
66
+
67
+
68
+ with open(args.output_path, "w") as fp:
69
+ fp.write('\n'.join(fasta))
70
+
71
+
72
+ if __name__ == "__main__":
73
+ parser = argparse.ArgumentParser()
74
+ parser.add_argument(
75
+ "data_dir", type=str,
76
+ help="Path to a directory containing mmCIF or .core files"
77
+ )
78
+ parser.add_argument(
79
+ "output_path", type=str,
80
+ help="Path to output FASTA file"
81
+ )
82
+ parser.add_argument(
83
+ "--raise_errors", type=bool, default=False,
84
+ help="Whether to crash on parsing errors"
85
+ )
86
+
87
+ args = parser.parse_args()
88
+
89
+ main(args)
scripts/deactivate_conda_env.sh ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ conda deactivate
scripts/deepspeed_inference_test.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os
3
+
4
+ import torch
5
+ import deepspeed
6
+
7
+
8
+ local_rank = int(os.getenv('LOCAL_RANK', '0'))
9
+ world_size = int(os.getenv('WORLD_SIZE', '1'))
10
+
11
+ class Model(torch.nn.Module):
12
+ def __init__(self):
13
+ super().__init__()
14
+
15
+ self.ml = torch.nn.ModuleList()
16
+ for _ in range(4000):
17
+ self.ml.append(torch.nn.Linear(500, 500))
18
+
19
+ def forward(self, batch):
20
+ for i, l in enumerate(self.ml):
21
+ # print(f"{i}: {l.weight.device}")
22
+ batch = l(batch)
23
+
24
+ return batch
25
+
26
+
27
+ class DummyDataset(torch.utils.data.Dataset):
28
+ def __init__(self):
29
+ self.batch = torch.rand(500, 500)
30
+
31
+ def __getitem__(self, idx):
32
+ return copy.deepcopy(self.batch)
33
+
34
+ def __len__(self):
35
+ return 1000
36
+
37
+ dd = DummyDataset()
38
+ dl = torch.utils.data.DataLoader(dd)
39
+ example = next(iter(dl)).to(f"cuda:{local_rank}")
40
+
41
+ model = Model()
42
+ model = model.to(f"cuda:{local_rank}")
43
+
44
+ model = deepspeed.init_inference(
45
+ model,
46
+ mp_size=world_size,
47
+ checkpoint=None,
48
+ replace_method=None,
49
+ #replace_method="auto"
50
+ )
51
+
52
+ out = model(example)
53
+ #if not torch.distributed.is_initialized() or torch.distributed.get_rank() == 0:
54
+ # print(out)
scripts/download_alphafold_dbs.sh ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # Copyright 2021 DeepMind Technologies Limited
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Downloads and unzips all required data for AlphaFold.
18
+ #
19
+ # Usage: bash download_all_data.sh /path/to/download/directory
20
+ set -e
21
+
22
+ if [[ $# -eq 0 ]]; then
23
+ echo "Error: download directory must be provided as an input argument."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v aria2c &> /dev/null ; then
28
+ echo "Error: aria2c could not be found. Please install aria2c (sudo apt install aria2)."
29
+ exit 1
30
+ fi
31
+
32
+ DOWNLOAD_DIR="$1"
33
+ DOWNLOAD_MODE="${2:-full_dbs}" # Default mode to full_dbs.
34
+ if [[ "${DOWNLOAD_MODE}" != full_dbs && "${DOWNLOAD_MODE}" != reduced_dbs ]]
35
+ then
36
+ echo "DOWNLOAD_MODE ${DOWNLOAD_MODE} not recognized."
37
+ exit 1
38
+ fi
39
+
40
+ SCRIPT_DIR="$(dirname "$(realpath "$0")")"
41
+
42
+ if [[ "${DOWNLOAD_MODE}" = full_dbs ]] ; then
43
+ echo "Downloading BFD..."
44
+ bash "${SCRIPT_DIR}/download_bfd.sh" "${DOWNLOAD_DIR}"
45
+ else
46
+ echo "Downloading Small BFD..."
47
+ bash "${SCRIPT_DIR}/download_small_bfd.sh" "${DOWNLOAD_DIR}"
48
+ fi
49
+
50
+ echo "Downloading MGnify..."
51
+ bash "${SCRIPT_DIR}/download_mgnify.sh" "${DOWNLOAD_DIR}"
52
+
53
+ echo "Downloading PDB70..."
54
+ bash "${SCRIPT_DIR}/download_pdb70.sh" "${DOWNLOAD_DIR}"
55
+
56
+ echo "Downloading PDB mmCIF files..."
57
+ bash "${SCRIPT_DIR}/download_pdb_mmcif.sh" "${DOWNLOAD_DIR}"
58
+
59
+ echo "Downloading Uniref30..."
60
+ bash "${SCRIPT_DIR}/download_uniref30.sh" "${DOWNLOAD_DIR}"
61
+
62
+ echo "Downloading Uniref90..."
63
+ bash "${SCRIPT_DIR}/download_uniref90.sh" "${DOWNLOAD_DIR}"
64
+
65
+ echo "Downloading UniProt..."
66
+ bash "${SCRIPT_DIR}/download_uniprot.sh" "${DOWNLOAD_DIR}"
67
+
68
+ echo "Downloading PDB SeqRes..."
69
+ bash "${SCRIPT_DIR}/download_pdb_seqres.sh" "${DOWNLOAD_DIR}"
70
+
71
+ echo "All data downloaded."
scripts/download_alphafold_params.sh ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # Copyright 2021 DeepMind Technologies Limited
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Downloads and unzips the AlphaFold parameters.
18
+ #
19
+ # Usage: bash download_alphafold_params.sh /path/to/download/directory
20
+ set -e
21
+
22
+ if [[ $# -eq 0 ]]; then
23
+ echo "Error: download directory must be provided as an input argument."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v aria2c &> /dev/null ; then
28
+ echo "Error: aria2c could not be found. Please install aria2c (sudo apt install aria2)."
29
+ exit 1
30
+ fi
31
+
32
+ DOWNLOAD_DIR="$1"
33
+ ROOT_DIR="${DOWNLOAD_DIR}/params"
34
+ SOURCE_URL="https://storage.googleapis.com/alphafold/alphafold_params_2022-12-06.tar"
35
+ BASENAME=$(basename "${SOURCE_URL}")
36
+
37
+ mkdir --parents "${ROOT_DIR}"
38
+ aria2c "${SOURCE_URL}" --dir="${ROOT_DIR}"
39
+ tar --extract --verbose --file="${ROOT_DIR}/${BASENAME}" \
40
+ --directory="${ROOT_DIR}" --preserve-permissions
41
+ rm "${ROOT_DIR}/${BASENAME}"
scripts/download_bfd.sh ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # Copyright 2021 DeepMind Technologies Limited
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Downloads and unzips the BFD database for AlphaFold.
18
+ #
19
+ # Usage: bash download_bfd.sh /path/to/download/directory
20
+ set -e
21
+
22
+ if [[ $# -eq 0 ]]; then
23
+ echo "Error: download directory must be provided as an input argument."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v aria2c &> /dev/null ; then
28
+ echo "Error: aria2c could not be found. Please install aria2c (sudo apt install aria2)."
29
+ exit 1
30
+ fi
31
+
32
+ DOWNLOAD_DIR="$1"
33
+ ROOT_DIR="${DOWNLOAD_DIR}/bfd"
34
+ # Mirror of:
35
+ # https://bfd.mmseqs.com/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt.tar.gz.
36
+ SOURCE_URL="https://storage.googleapis.com/alphafold-databases/casp14_versions/bfd_metaclust_clu_complete_id30_c90_final_seq.sorted_opt.tar.gz"
37
+ BASENAME=$(basename "${SOURCE_URL}")
38
+
39
+ mkdir --parents "${ROOT_DIR}"
40
+ aria2c "${SOURCE_URL}" --dir="${ROOT_DIR}"
41
+ tar --extract --verbose --file="${ROOT_DIR}/${BASENAME}" \
42
+ --directory="${ROOT_DIR}"
43
+ rm "${ROOT_DIR}/${BASENAME}"
scripts/download_cameo.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ import argparse
4
+ import json
5
+ import os
6
+ import re
7
+ import requests
8
+
9
+ from onescience.datapipes.openfold import mmcif_parsing
10
+
11
+
12
+ VALID_PERIODS = [
13
+ "1-year",
14
+ "6-months",
15
+ "3-months",
16
+ "1-month",
17
+ "1-week",
18
+ ]
19
+
20
+
21
+ def generate_url(period, end_date):
22
+ return '/'.join([
23
+ "https://www.cameo3d.org/",
24
+ "modeling",
25
+ "targets",
26
+ period,
27
+ "ajax",
28
+ f"?to_date={end_date}",
29
+ ])
30
+
31
+
32
+ def main(args):
33
+ data_dir_path = os.path.join(args.output_dir, "data_dir")
34
+ fasta_dir_path = os.path.join(args.output_dir, "fasta_dir")
35
+
36
+ os.makedirs(data_dir_path, exist_ok=True)
37
+ os.makedirs(fasta_dir_path, exist_ok=True)
38
+
39
+ url = generate_url(args.period, args.end_date)
40
+ raw_data = requests.get(url).text
41
+ parsed_data = json.loads(raw_data)
42
+
43
+ chain_data = parsed_data["aaData"]
44
+ for chain in chain_data:
45
+ pdb_id = chain["pdbid"]
46
+ chain_id = chain["pdbid_chain"]
47
+
48
+ pdb_url = f"https://files.rcsb.org/view/{pdb_id.upper()}.cif"
49
+ pdb_file = requests.get(pdb_url).text
50
+
51
+ parsed_cif = mmcif_parsing.parse(
52
+ file_id=pdb_id, mmcif_string=pdb_file
53
+ )
54
+ mmcif_object = parsed_cif.mmcif_object
55
+ if(mmcif_object is None):
56
+ raise list(parsed_cif.errors.values())[0]
57
+
58
+ seq = mmcif_object.chain_to_seqres[chain_id]
59
+
60
+ if(args.max_seqlen > 0 and len(seq) > args.max_seqlen):
61
+ continue
62
+
63
+ fasta_file = '\n'.join([
64
+ f">{pdb_id}_{chain_id}",
65
+ seq,
66
+ ])
67
+
68
+ fasta_filename = f"{pdb_id}_{chain_id}.fasta"
69
+ with open(os.path.join(fasta_dir_path, fasta_filename), "w") as fp:
70
+ fp.write(fasta_file)
71
+
72
+ cif_filename = f"{pdb_id}.cif"
73
+ with open(os.path.join(data_dir_path, cif_filename), "w") as fp:
74
+ fp.write(pdb_file)
75
+
76
+
77
+ if __name__ == '__main__':
78
+ parser = argparse.ArgumentParser()
79
+ parser.add_argument(
80
+ "period", type=str,
81
+ help=f"""The length of the period from which to draw CAMEO proteins.
82
+ Choose from {VALID_PERIODS}"""
83
+ )
84
+ parser.add_argument(
85
+ "end_date", type=str,
86
+ help="The date marking the end of the period (YYYY-MM-DD)"
87
+ )
88
+ parser.add_argument("output_dir")
89
+ parser.add_argument(
90
+ "--max_seqlen", type=int, default=700,
91
+ help="The maximum length in residues of downloaded proteins (or -1)"
92
+ )
93
+
94
+ args = parser.parse_args()
95
+
96
+ if(args.period not in VALID_PERIODS):
97
+ raise ValueError(f"Invalid period. Choose from {VALID_PERIODS}")
98
+
99
+ date_regex = re.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}$")
100
+ if(not date_regex.match(args.end_date)):
101
+ raise ValueError(f"Invalid end_date: {args.end_date}. Use YYYY-MM-DD format")
102
+
103
+ main(args)
scripts/download_colabfold_envdb.sh ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # Copyright 2021 AlQuraishi Laboratory
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Downloads and unzips the BFD database for AlphaFold.
18
+ #
19
+ # Usage: bash download_bfd.sh /path/to/download/directory
20
+ set -e
21
+
22
+ if [[ $# -eq 0 ]]; then
23
+ echo "Error: download directory must be provided as an input argument."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v aria2c &> /dev/null ; then
28
+ echo "Error: aria2c could not be found. Please install aria2c (sudo apt install aria2)."
29
+ exit 1
30
+ fi
31
+
32
+ DOWNLOAD_DIR="$1"
33
+ ROOT_DIR="${DOWNLOAD_DIR}"
34
+ SOURCE_URL="http://wwwuser.gwdg.de/~compbiol/colabfold/colabfold_envdb_202108.tar.gz"
35
+ BASENAME=$(basename "${SOURCE_URL}")
36
+
37
+ mkdir --parents "${ROOT_DIR}"
38
+ aria2c "${SOURCE_URL}" --dir="${ROOT_DIR}" -x 4 --check-certificate=false
scripts/download_mgnify.sh ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # Copyright 2021 DeepMind Technologies Limited
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Downloads and unzips the MGnify database for AlphaFold.
18
+ #
19
+ # Usage: bash download_mgnify.sh /path/to/download/directory
20
+ set -e
21
+
22
+ if [[ $# -eq 0 ]]; then
23
+ echo "Error: download directory must be provided as an input argument."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v aria2c &> /dev/null ; then
28
+ echo "Error: aria2c could not be found. Please install aria2c (sudo apt install aria2)."
29
+ exit 1
30
+ fi
31
+
32
+ DOWNLOAD_DIR="$1"
33
+ ROOT_DIR="${DOWNLOAD_DIR}/mgnify"
34
+ # Mirror of:
35
+ # ftp://ftp.ebi.ac.uk/pub/databases/metagenomics/peptide_database/2022_05/mgy_clusters.fa.gz
36
+ SOURCE_URL="https://storage.googleapis.com/alphafold-databases/v2.3/mgy_clusters_2022_05.fa.gz"
37
+ BASENAME=$(basename "${SOURCE_URL}")
38
+
39
+ mkdir --parents "${ROOT_DIR}"
40
+ aria2c "${SOURCE_URL}" --dir="${ROOT_DIR}"
41
+ gunzip "${ROOT_DIR}/${BASENAME}"
scripts/download_mmseqs_dbs.sh ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ #
3
+ # Copyright 2021 AlQuraishi Laboratory
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ # Downloads and unzips all required data for AlphaFold.
18
+ #
19
+ # Usage: bash download_all_data.sh /path/to/download/directory
20
+ set -e
21
+
22
+ if [[ $# -eq 0 ]]; then
23
+ echo "Error: download directory must be provided as an input argument."
24
+ exit 1
25
+ fi
26
+
27
+ if ! command -v aria2c &> /dev/null ; then
28
+ echo "Error: aria2c could not be found. Please install aria2c (sudo apt install aria2)."
29
+ exit 1
30
+ fi
31
+
32
+ DOWNLOAD_DIR="$1"
33
+ DOWNLOAD_MODE="${2:-full_dbs}" # Default mode to full_dbs.
34
+ if [[ "${DOWNLOAD_MODE}" != full_dbs && "${DOWNLOAD_MODE}" != reduced_dbs ]]
35
+ then
36
+ echo "DOWNLOAD_MODE ${DOWNLOAD_MODE} not recognized."
37
+ exit 1
38
+ fi
39
+
40
+ SCRIPT_DIR="$(dirname "$(realpath "$0")")"
41
+
42
+ echo "Downloading Uniref30..."
43
+ bash "${SCRIPT_DIR}/download_uniref30.sh" "${DOWNLOAD_DIR}"
44
+
45
+ echo "Downloading ColabFold's environmental database..."
46
+ bash "${SCRIPT_DIR}/download_colabfold_envdb.sh" "${DOWNLOAD_DIR}"
47
+
48
+ echo "All data downloaded."