Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +39 -9
- LICENSE.txt +202 -0
- README.md +203 -0
- config/__init__.py +1 -0
- config/config.py +956 -0
- config/config.yaml +25 -0
- config/deepspeed_config.json +27 -0
- configuration.json +16 -0
- data/demo/monomer/alignments/6KWC_1/bfd_uniref_hits.a3m +0 -0
- data/demo/monomer/alignments/6KWC_1/hhsearch_output.hhr +0 -0
- data/demo/monomer/alignments/6KWC_1/mgnify_hits.sto +0 -0
- data/demo/monomer/alignments/6KWC_1/uniref90_hits.sto +0 -0
- data/demo/monomer/fasta_dir/6kwc.fasta +2 -0
- data/demo/monomer/inference.sh +20 -0
- model/openfold/__init__.py +1 -0
- model/openfold/dropout.py +78 -0
- model/openfold/embedders.py +984 -0
- model/openfold/evoformer.py +1219 -0
- model/openfold/heads.py +267 -0
- model/openfold/model.py +591 -0
- model/openfold/msa.py +476 -0
- model/openfold/outer_product_mean.py +159 -0
- model/openfold/pair_transition.py +99 -0
- model/openfold/primitives.py +902 -0
- model/openfold/structure_module.py +1252 -0
- model/openfold/template.py +693 -0
- model/openfold/torchscript.py +215 -0
- model/openfold/triangular_attention.py +168 -0
- model/openfold/triangular_multiplicative_update.py +647 -0
- scripts/__init__.py +0 -0
- scripts/activate_conda_env.sh +6 -0
- scripts/alignment_data_to_fasta.py +144 -0
- scripts/alignment_db_scripts/add_non_unique_to_alignment_db.py +82 -0
- scripts/alignment_db_scripts/create_alignment_db.py +47 -0
- scripts/alignment_db_scripts/create_alignment_db_sharded.py +244 -0
- scripts/alignment_db_scripts/unify_alignment_db_indices.py +37 -0
- scripts/build_deepspeed_config.py +315 -0
- scripts/colabfold_search.sh +75 -0
- scripts/convert_of_weights_to_jax.py +104 -0
- scripts/convert_v1_to_v2_weights.py +97 -0
- scripts/data_dir_to_fasta.py +89 -0
- scripts/deactivate_conda_env.sh +3 -0
- scripts/deepspeed_inference_test.py +54 -0
- scripts/download_alphafold_dbs.sh +71 -0
- scripts/download_alphafold_params.sh +41 -0
- scripts/download_bfd.sh +43 -0
- scripts/download_cameo.py +103 -0
- scripts/download_colabfold_envdb.sh +38 -0
- scripts/download_mgnify.sh +41 -0
- 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."
|