Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +64 -10
- README.md +283 -0
- configuration.json +2 -0
- model/README.md +10 -0
- model/__init__.py +2 -0
- model/__pycache__/__init__.cpython-311.pyc +0 -0
- model/esm/__init__.py +13 -0
- model/esm/__pycache__/__init__.cpython-311.pyc +0 -0
- model/esm/__pycache__/esm1.cpython-311.pyc +0 -0
- model/esm/__pycache__/esm2.cpython-311.pyc +0 -0
- model/esm/__pycache__/msa_transformer.cpython-311.pyc +0 -0
- model/esm/__pycache__/pretrained.cpython-311.pyc +0 -0
- model/esm/__pycache__/version.cpython-311.pyc +0 -0
- model/esm/esm1.py +200 -0
- model/esm/esm2.py +152 -0
- model/esm/esmfold/__init__.py +0 -0
- model/esm/esmfold/v1/categorical_mixture.py +43 -0
- model/esm/esmfold/v1/misc.py +309 -0
- model/esm/esmfold/v1/pretrained.py +197 -0
- model/esm/inverse_folding/__init__.py +8 -0
- model/esm/inverse_folding/features.py +352 -0
- model/esm/inverse_folding/gvp_encoder.py +56 -0
- model/esm/inverse_folding/gvp_transformer_encoder.py +184 -0
- model/esm/inverse_folding/gvp_utils.py +68 -0
- model/esm/inverse_folding/multichain_util.py +152 -0
- model/esm/inverse_folding/transformer_decoder.py +228 -0
- model/esm/inverse_folding/transformer_layer.py +304 -0
- model/esm/inverse_folding/util.py +323 -0
- model/esm/msa_transformer.py +238 -0
- model/esm/pretrained.py +553 -0
- model/esm/version.py +6 -0
- model/openfold/__init__.py +0 -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
- weight/esm-main/scripts/atlas/v0/full/tarballs/tm_.90_1_plddt_.70_.80.txt +1 -0
- weight/esm-main/scripts/atlas/v0/full/tarballs/tm_0_.40_plddt_0_.40.txt +46 -0
- weight/esm-main/scripts/atlas/v2023_02/full/esm2_embeddings/tm_.70_.80_plddt_.70_.80.txt +13 -0
- weight/esm-main/scripts/atlas/v2023_02/full/esm2_embeddings/tm_.70_.80_plddt_.80_.90.txt +2 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,89 @@
|
|
| 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 |
-
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bin.* filter=lfs diff=lfs merge=lfs -text
|
| 5 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 11 |
*.model filter=lfs diff=lfs merge=lfs -text
|
| 12 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
| 13 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 14 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 15 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 16 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
|
|
|
|
|
|
|
| 18 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 19 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 20 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 21 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 22 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 23 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 24 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 25 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
*.zstandard filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.db* filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.ark* filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
**/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
**/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
**/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.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
|
| 66 |
+
|
| 67 |
+
data/inverse_folding/5YH2.cif filter=lfs diff=lfs merge=lfs -text
|
| 68 |
+
data/inverse_folding/5YH2.pdb filter=lfs diff=lfs merge=lfs -text
|
| 69 |
+
data/variant_prediction/BLAT_ECOLX_Ranganathan2015.csv filter=lfs diff=lfs merge=lfs -text
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
weight/esm-main/examples/data/P62593.fasta filter=lfs diff=lfs merge=lfs -text
|
| 75 |
+
weight/esm-main/examples/inverse_folding/data/4uv3.cif filter=lfs diff=lfs merge=lfs -text
|
| 76 |
+
weight/esm-main/examples/inverse_folding/data/4uv3.pdb filter=lfs diff=lfs merge=lfs -text
|
| 77 |
+
weight/esm-main/examples/inverse_folding/data/5YH2.cif filter=lfs diff=lfs merge=lfs -text
|
| 78 |
+
weight/esm-main/examples/inverse_folding/data/5YH2.pdb filter=lfs diff=lfs merge=lfs -text
|
| 79 |
+
weight/esm-main/examples/inverse_folding/notebook.ipynb filter=lfs diff=lfs merge=lfs -text
|
| 80 |
+
weight/esm-main/examples/inverse_folding/notebook_multichain.ipynb filter=lfs diff=lfs merge=lfs -text
|
| 81 |
+
|
| 82 |
+
weight/esm-main/examples/variant-prediction/data/BLAT_ECOLX_1_b0.5.a3m filter=lfs diff=lfs merge=lfs -text
|
| 83 |
+
weight/esm-main/examples/variant-prediction/data/BLAT_ECOLX_Ranganathan2015.csv filter=lfs diff=lfs merge=lfs -text
|
| 84 |
+
weight/esm-main/examples/variant-prediction/data/BLAT_ECOLX_Ranganathan2015_labeled.csv filter=lfs diff=lfs merge=lfs -text
|
| 85 |
+
weight/esm-main/examples/variant-prediction/data/BLAT_ECOLX_Ranganathan2015_labeled_masked.csv filter=lfs diff=lfs merge=lfs -text
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tasks:
|
| 4 |
+
- protein-structure-prediction
|
| 5 |
+
frameworks:
|
| 6 |
+
- pytorch
|
| 7 |
+
language:
|
| 8 |
+
- en
|
| 9 |
+
- zh
|
| 10 |
+
tags:
|
| 11 |
+
- OneScience
|
| 12 |
+
- Life Sciences
|
| 13 |
+
- Protein Language Model
|
| 14 |
+
- Protein Structure Prediction
|
| 15 |
+
- Variant Effect Prediction
|
| 16 |
+
- ESM
|
| 17 |
+
huggingface:
|
| 18 |
+
model: OneScience-Sugon/ESM
|
| 19 |
+
data_path: data/
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
<p align="center">
|
| 23 |
+
<strong>
|
| 24 |
+
<span style="font-size: 30px;">ESM</span>
|
| 25 |
+
</strong>
|
| 26 |
+
</p>
|
| 27 |
+
|
| 28 |
+
# Model Introduction
|
| 29 |
+
|
| 30 |
+
ESM (Evolutionary Scale Modeling) is a family of protein language models released by Meta AI / FAIR. It can be used for protein representation extraction, structure prediction, variant effect scoring, and fixed-backbone sequence design.
|
| 31 |
+
|
| 32 |
+
Paper: Evolutionary-scale prediction of atomic-level protein structure with a language model
|
| 33 |
+
https://www.science.org/doi/10.1126/science.ade2574
|
| 34 |
+
|
| 35 |
+
# Model Description
|
| 36 |
+
|
| 37 |
+
This model package provides PyTorch inference support for ESM-1, ESM-2, MSA Transformer, ESMFold, ESM-1v, and ESM-IF1, together with adaptations for running on DCUs. Sample data is distributed with the Hugging Face model repository `OneScience-Sugon/ESM`.
|
| 38 |
+
|
| 39 |
+
# Use Cases
|
| 40 |
+
|
| 41 |
+
| Scenario | Description |
|
| 42 |
+
| :---: | :--- |
|
| 43 |
+
| Protein representation extraction | Takes a FASTA file as input and outputs per-token, mean-pooled, BOS, or contact representations |
|
| 44 |
+
| Protein structure prediction | Takes one or more amino acid sequences as input and outputs corresponding PDB structure files |
|
| 45 |
+
| Variant effect scoring | Takes a wild-type sequence and a DMS mutation table as input and outputs mutation effect scores |
|
| 46 |
+
| Fixed-backbone sequence design | Takes a PDB / CIF structure and chain ID as input and samples candidate sequences that satisfy the backbone constraints |
|
| 47 |
+
| Structure-conditioned sequence scoring | Takes a structure and candidate sequences as input and computes their conditional log-likelihoods |
|
| 48 |
+
| Hugging Face / OneCode execution | After downloading the model project, quickly verifies that the scripts run correctly in a life-sciences runtime environment |
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# Usage Guide
|
| 52 |
+
|
| 53 |
+
## 1. OneCode Usage
|
| 54 |
+
|
| 55 |
+
Try one-click AI4S development in the OneCode online environment:
|
| 56 |
+
|
| 57 |
+
[Try one-click AI4S development](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 58 |
+
|
| 59 |
+
## 2. Manual Installation and Usage
|
| 60 |
+
|
| 61 |
+
**Hardware Requirements**
|
| 62 |
+
|
| 63 |
+
- GPU or DCU is recommended.
|
| 64 |
+
- A CPU can be used for import checks and lightweight configuration tests; full training and inference will be slow.
|
| 65 |
+
- DCU users must install DTK in advance. DTK 25.04.2 or later is recommended, or a OneScience-recommended version matching the current cluster.
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
**Environment Check**
|
| 72 |
+
|
| 73 |
+
- NVIDIA GPU:
|
| 74 |
+
|
| 75 |
+
```bash
|
| 76 |
+
nvidia-smi
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
- Hygon DCU:
|
| 80 |
+
|
| 81 |
+
```bash
|
| 82 |
+
hy-smi
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
### Download the Model Package
|
| 86 |
+
|
| 87 |
+
```bash
|
| 88 |
+
hf download --model OneScience-Sugon/ESM --local-dir ./ESM
|
| 89 |
+
cd ESM
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
This model package includes a small set of sample data that can be used directly to validate the default workflow.
|
| 93 |
+
|
| 94 |
+
### Install the Runtime Environment
|
| 95 |
+
|
| 96 |
+
**DCU Environment**
|
| 97 |
+
|
| 98 |
+
```bash
|
| 99 |
+
# Activate DTK and CONDA first
|
| 100 |
+
conda create -n onescience311 python=3.11 -y
|
| 101 |
+
conda activate onescience311
|
| 102 |
+
# uv installation supported
|
| 103 |
+
pip install onescience[bio-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
```bash
|
| 107 |
+
# If required libraries cannot be found, activate the CUDA compatibility environment as follows:
|
| 108 |
+
source ${ROCM_PATH}/cuda/env.sh
|
| 109 |
+
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib:$LD_LIBRARY_PATH"
|
| 110 |
+
export LD_LIBRARY_PATH="$CONDA_PREFIX/lib/python3.11/site-packages/fastpt/torch/lib:$LD_LIBRARY_PATH"
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
After installation, return to the model package directory:
|
| 114 |
+
|
| 115 |
+
```bash
|
| 116 |
+
cd ./ESM
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
### Training and Inference Data Overview
|
| 120 |
+
|
| 121 |
+
The FASTA, PDB / CIF, and DMS files used in the ESM examples are distributed with the [Hugging Face model repository OneScience-Sugon/ESM](https://huggingface.co/OneScience-Sugon/ESM). After downloading the complete model package, the files are available under `data/`. This model package does not include a training entry point; the data is intended for example inference and workflow validation. You can also download only the data directory:
|
| 122 |
+
|
| 123 |
+
```bash
|
| 124 |
+
hf download --model OneScience-Sugon/ESM ESM/data --local-dir ./data
|
| 125 |
+
```
|
| 126 |
+
### Model Weights
|
| 127 |
+
|
| 128 |
+
The repository includes multiple ESM model checkpoints under `weight/`; select the appropriate checkpoint for inference.
|
| 129 |
+
|
| 130 |
+
### Preparing Weights
|
| 131 |
+
|
| 132 |
+
Place the required ESM weights in the following directory:
|
| 133 |
+
|
| 134 |
+
```text
|
| 135 |
+
weight/
|
| 136 |
+
checkpoints/
|
| 137 |
+
esm2_t6_8M_UR50D.pt
|
| 138 |
+
esmfold_3B_v1.pt
|
| 139 |
+
esm1v_t33_650M_UR90S_1.pt
|
| 140 |
+
esm_if1_gvp4_t16_142M_UR50.pt
|
| 141 |
+
...
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
When using a shared runtime environment, you can specify the weights location through an environment variable:
|
| 145 |
+
|
| 146 |
+
```bash
|
| 147 |
+
export ESM_WEIGHT_DIR=/path/to/esm/weight
|
| 148 |
+
```
|
| 149 |
+
|
| 150 |
+
The default example uses:
|
| 151 |
+
|
| 152 |
+
- `weight/checkpoints/esm2_t6_8M_UR50D.pt`
|
| 153 |
+
|
| 154 |
+
The ESMFold, ESM-1v, and ESM-IF1 examples require their respective weights to be available.
|
| 155 |
+
|
| 156 |
+
### Default Example
|
| 157 |
+
|
| 158 |
+
```bash
|
| 159 |
+
bash scripts/infer.sh
|
| 160 |
+
```
|
| 161 |
+
|
| 162 |
+
The default example reads `data/fasta/few_proteins.fasta`, extracts protein representations using `esm2_t6_8M_UR50D.pt`, and saves the results to `outputs/embeddings/`.
|
| 163 |
+
|
| 164 |
+
### Sequence Representation Extraction
|
| 165 |
+
|
| 166 |
+
```bash
|
| 167 |
+
python scripts/extract.py \
|
| 168 |
+
weight/checkpoints/esm2_t6_8M_UR50D.pt \
|
| 169 |
+
data/fasta/few_proteins.fasta \
|
| 170 |
+
outputs/embeddings \
|
| 171 |
+
--include mean per_tok \
|
| 172 |
+
--repr_layers 6
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
### ESMFold Structure Prediction
|
| 176 |
+
|
| 177 |
+
```bash
|
| 178 |
+
python scripts/fold.py \
|
| 179 |
+
-i data/fasta/few_proteins.fasta \
|
| 180 |
+
-o outputs/pdb \
|
| 181 |
+
--model-dir weight \
|
| 182 |
+
--cpu-only
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
The output directory will contain one or more `.pdb` files. For production GPU / DCU inference, remove `--cpu-only` and configure `--chunk-size` or `--max-tokens-per-batch` according to the available accelerator memory.
|
| 186 |
+
|
| 187 |
+
You can also explicitly enable ESMFold via the default script:
|
| 188 |
+
|
| 189 |
+
```bash
|
| 190 |
+
RUN_ESMFOLD=1 bash scripts/infer.sh
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
### Inverse Folding — Sequence Sampling
|
| 194 |
+
|
| 195 |
+
```bash
|
| 196 |
+
python scripts/inverse_folding/sample_sequences.py \
|
| 197 |
+
data/inverse_folding/5YH2.pdb \
|
| 198 |
+
--chain A \
|
| 199 |
+
--outpath outputs/sampled_seqs.fasta \
|
| 200 |
+
--num-samples 1 \
|
| 201 |
+
--nogpu
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
### Inverse Folding — Sequence Scoring
|
| 205 |
+
|
| 206 |
+
```bash
|
| 207 |
+
python scripts/inverse_folding/score_log_likelihoods.py \
|
| 208 |
+
data/inverse_folding/5YH2.pdb \
|
| 209 |
+
data/inverse_folding/5YH2_mutated_seqs.fasta \
|
| 210 |
+
--chain A \
|
| 211 |
+
--outpath outputs/sequence_scores.csv \
|
| 212 |
+
--nogpu
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
### Variant Effect Prediction
|
| 216 |
+
|
| 217 |
+
Variant effect prediction requires a wild-type sequence that is consistent with the mutation annotations in the DMS table:
|
| 218 |
+
|
| 219 |
+
```bash
|
| 220 |
+
python scripts/variant_prediction/predict.py \
|
| 221 |
+
--model-location esm1v_t33_650M_UR90S_1 \
|
| 222 |
+
--sequence "${ESM_VARIANT_SEQUENCE}" \
|
| 223 |
+
--dms-input data/variant_prediction/BLAT_ECOLX_Ranganathan2015.csv \
|
| 224 |
+
--mutation-col mutant \
|
| 225 |
+
--dms-output outputs/variant_prediction.csv \
|
| 226 |
+
--offset-idx 24 \
|
| 227 |
+
--scoring-strategy wt-marginals
|
| 228 |
+
```
|
| 229 |
+
|
| 230 |
+
# Data Format
|
| 231 |
+
|
| 232 |
+
Sample data is stored under `data/` by default:
|
| 233 |
+
|
| 234 |
+
```text
|
| 235 |
+
data/
|
| 236 |
+
fasta/
|
| 237 |
+
few_proteins.fasta
|
| 238 |
+
some_proteins.fasta
|
| 239 |
+
inverse_folding/
|
| 240 |
+
5YH2.pdb
|
| 241 |
+
5YH2.cif
|
| 242 |
+
5YH2_mutated_seqs.fasta
|
| 243 |
+
example.json
|
| 244 |
+
variant_prediction/
|
| 245 |
+
BLAT_ECOLX_Ranganathan2015.csv
|
| 246 |
+
rho_pp.csv
|
| 247 |
+
aggregated_rho.csv
|
| 248 |
+
aggregated_rho_round3.csv
|
| 249 |
+
```
|
| 250 |
+
|
| 251 |
+
In this structure:
|
| 252 |
+
|
| 253 |
+
- FASTA files are used for sequence representation extraction and structure prediction.
|
| 254 |
+
- PDB / CIF files are used for inverse folding sampling and structure-conditioned sequence scoring.
|
| 255 |
+
- Variant effect prediction CSV files must include a mutation column. The default column name is `mutant`, and mutations use notation such as `A123B`.
|
| 256 |
+
- For custom DMS data, the wild-type amino acid at each mutated position in the sequence provided via `--sequence` must match the corresponding mutation annotation.
|
| 257 |
+
|
| 258 |
+
# Verification
|
| 259 |
+
|
| 260 |
+
Static import check:
|
| 261 |
+
|
| 262 |
+
```bash
|
| 263 |
+
python scripts/check_import_boundaries.py
|
| 264 |
+
```
|
| 265 |
+
|
| 266 |
+
Syntax check:
|
| 267 |
+
|
| 268 |
+
```bash
|
| 269 |
+
python -B -c "import ast, pathlib; [ast.parse(p.read_text(encoding='utf-8'), filename=str(p)) for root in ['model', 'scripts', 'tests'] for p in pathlib.Path(root).rglob('*.py')]"
|
| 270 |
+
```
|
| 271 |
+
|
| 272 |
+
# OneScience Official Information
|
| 273 |
+
|
| 274 |
+
| Platform | OneScience Main Repository | Skills Repository |
|
| 275 |
+
| --- | --- | --- |
|
| 276 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 277 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 278 |
+
|
| 279 |
+
# Citation & License
|
| 280 |
+
|
| 281 |
+
- This repository is adapted from the open-source ESM model to support DCUs.
|
| 282 |
+
- The ESM source code is licensed under the MIT License; see `LICENSE`. For the usage terms governing model weights and data, refer to the documentation provided by the respective publishers.
|
| 283 |
+
- For scientific use, please cite the corresponding original ESM paper for each submodel used. For ESM-2 / ESMFold, cite: [Evolutionary-scale prediction of atomic-level protein structure with a language model](https://www.science.org/doi/10.1126/science.ade2574).
|
configuration.json
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"framework":"Pytorch","task":"other"}
|
| 2 |
+
|
model/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Local Model Source
|
| 2 |
+
|
| 3 |
+
This directory contains OneScience `models` sources that ESM imports directly.
|
| 4 |
+
|
| 5 |
+
- `esm/`: local copy of `onescience.models.esm`
|
| 6 |
+
- `openfold/`: local copy of `onescience.models.openfold`, required by ESMFold
|
| 7 |
+
- `protenix/layer_norm/`: optional fused layer norm dependency used by OpenFold when `LAYERNORM_TYPE=fast_layernorm`
|
| 8 |
+
|
| 9 |
+
Other OneScience namespaces such as `onescience.datapipes`, `onescience.modules`, and `onescience.utils` are intentionally imported from the installed OneScience environment.
|
| 10 |
+
|
model/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Local model packages copied from OneScience for the standalone ESM project."""
|
| 2 |
+
|
model/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (269 Bytes). View file
|
|
|
model/esm/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from onescience.datapipes import esm as data # noqa
|
| 7 |
+
from onescience.datapipes.esm import Alphabet, BatchConverter, FastaBatchedDataset # noqa
|
| 8 |
+
|
| 9 |
+
from .version import version as __version__ # noqa
|
| 10 |
+
from .esm1 import ProteinBertModel # noqa
|
| 11 |
+
from .esm2 import ESM2 # noqa
|
| 12 |
+
from .msa_transformer import MSATransformer #noqa
|
| 13 |
+
from . import pretrained # noqa
|
model/esm/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (689 Bytes). View file
|
|
|
model/esm/__pycache__/esm1.cpython-311.pyc
ADDED
|
Binary file (10.1 kB). View file
|
|
|
model/esm/__pycache__/esm2.cpython-311.pyc
ADDED
|
Binary file (6.71 kB). View file
|
|
|
model/esm/__pycache__/msa_transformer.cpython-311.pyc
ADDED
|
Binary file (10.3 kB). View file
|
|
|
model/esm/__pycache__/pretrained.cpython-311.pyc
ADDED
|
Binary file (29.9 kB). View file
|
|
|
model/esm/__pycache__/version.cpython-311.pyc
ADDED
|
Binary file (201 Bytes). View file
|
|
|
model/esm/esm1.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
|
| 12 |
+
from onescience.modules.esm import (
|
| 13 |
+
TransformerLayer,
|
| 14 |
+
LearnedPositionalEmbedding,
|
| 15 |
+
SinusoidalPositionalEmbedding,
|
| 16 |
+
RobertaLMHead,
|
| 17 |
+
ESM1bLayerNorm,
|
| 18 |
+
ContactPredictionHead,
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class ProteinBertModel(nn.Module):
|
| 23 |
+
@classmethod
|
| 24 |
+
def add_args(cls, parser):
|
| 25 |
+
parser.add_argument(
|
| 26 |
+
"--num_layers", default=36, type=int, metavar="N", help="number of layers"
|
| 27 |
+
)
|
| 28 |
+
parser.add_argument(
|
| 29 |
+
"--embed_dim", default=1280, type=int, metavar="N", help="embedding dimension"
|
| 30 |
+
)
|
| 31 |
+
parser.add_argument(
|
| 32 |
+
"--logit_bias", action="store_true", help="whether to apply bias to logits"
|
| 33 |
+
)
|
| 34 |
+
parser.add_argument(
|
| 35 |
+
"--ffn_embed_dim",
|
| 36 |
+
default=5120,
|
| 37 |
+
type=int,
|
| 38 |
+
metavar="N",
|
| 39 |
+
help="embedding dimension for FFN",
|
| 40 |
+
)
|
| 41 |
+
parser.add_argument(
|
| 42 |
+
"--attention_heads",
|
| 43 |
+
default=20,
|
| 44 |
+
type=int,
|
| 45 |
+
metavar="N",
|
| 46 |
+
help="number of attention heads",
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
def __init__(self, args, alphabet):
|
| 50 |
+
super().__init__()
|
| 51 |
+
self.args = args
|
| 52 |
+
self.alphabet_size = len(alphabet)
|
| 53 |
+
self.padding_idx = alphabet.padding_idx
|
| 54 |
+
self.mask_idx = alphabet.mask_idx
|
| 55 |
+
self.cls_idx = alphabet.cls_idx
|
| 56 |
+
self.eos_idx = alphabet.eos_idx
|
| 57 |
+
self.prepend_bos = alphabet.prepend_bos
|
| 58 |
+
self.append_eos = alphabet.append_eos
|
| 59 |
+
self.emb_layer_norm_before = getattr(self.args, "emb_layer_norm_before", False)
|
| 60 |
+
if self.args.arch == "roberta_large":
|
| 61 |
+
self.model_version = "ESM-1b"
|
| 62 |
+
self._init_submodules_esm1b()
|
| 63 |
+
else:
|
| 64 |
+
self.model_version = "ESM-1"
|
| 65 |
+
self._init_submodules_esm1()
|
| 66 |
+
|
| 67 |
+
def _init_submodules_common(self):
|
| 68 |
+
self.embed_tokens = nn.Embedding(
|
| 69 |
+
self.alphabet_size, self.args.embed_dim, padding_idx=self.padding_idx
|
| 70 |
+
)
|
| 71 |
+
self.layers = nn.ModuleList(
|
| 72 |
+
[
|
| 73 |
+
TransformerLayer(
|
| 74 |
+
self.args.embed_dim,
|
| 75 |
+
self.args.ffn_embed_dim,
|
| 76 |
+
self.args.attention_heads,
|
| 77 |
+
add_bias_kv=(self.model_version != "ESM-1b"),
|
| 78 |
+
use_esm1b_layer_norm=(self.model_version == "ESM-1b"),
|
| 79 |
+
)
|
| 80 |
+
for _ in range(self.args.layers)
|
| 81 |
+
]
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
self.contact_head = ContactPredictionHead(
|
| 85 |
+
self.args.layers * self.args.attention_heads,
|
| 86 |
+
self.prepend_bos,
|
| 87 |
+
self.append_eos,
|
| 88 |
+
eos_idx=self.eos_idx,
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
def _init_submodules_esm1b(self):
|
| 92 |
+
self._init_submodules_common()
|
| 93 |
+
self.embed_scale = 1
|
| 94 |
+
self.embed_positions = LearnedPositionalEmbedding(
|
| 95 |
+
self.args.max_positions, self.args.embed_dim, self.padding_idx
|
| 96 |
+
)
|
| 97 |
+
self.emb_layer_norm_before = (
|
| 98 |
+
ESM1bLayerNorm(self.args.embed_dim) if self.emb_layer_norm_before else None
|
| 99 |
+
)
|
| 100 |
+
self.emb_layer_norm_after = ESM1bLayerNorm(self.args.embed_dim)
|
| 101 |
+
self.lm_head = RobertaLMHead(
|
| 102 |
+
embed_dim=self.args.embed_dim,
|
| 103 |
+
output_dim=self.alphabet_size,
|
| 104 |
+
weight=self.embed_tokens.weight,
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
def _init_submodules_esm1(self):
|
| 108 |
+
self._init_submodules_common()
|
| 109 |
+
self.embed_scale = math.sqrt(self.args.embed_dim)
|
| 110 |
+
self.embed_positions = SinusoidalPositionalEmbedding(self.args.embed_dim, self.padding_idx)
|
| 111 |
+
self.embed_out = nn.Parameter(torch.zeros((self.alphabet_size, self.args.embed_dim)))
|
| 112 |
+
self.embed_out_bias = None
|
| 113 |
+
if self.args.final_bias:
|
| 114 |
+
self.embed_out_bias = nn.Parameter(torch.zeros(self.alphabet_size))
|
| 115 |
+
|
| 116 |
+
def forward(self, tokens, repr_layers=[], need_head_weights=False, return_contacts=False):
|
| 117 |
+
if return_contacts:
|
| 118 |
+
need_head_weights = True
|
| 119 |
+
|
| 120 |
+
assert tokens.ndim == 2
|
| 121 |
+
padding_mask = tokens.eq(self.padding_idx) # B, T
|
| 122 |
+
|
| 123 |
+
x = self.embed_scale * self.embed_tokens(tokens)
|
| 124 |
+
|
| 125 |
+
if getattr(self.args, "token_dropout", False):
|
| 126 |
+
x.masked_fill_((tokens == self.mask_idx).unsqueeze(-1), 0.0)
|
| 127 |
+
# x: B x T x C
|
| 128 |
+
mask_ratio_train = 0.15 * 0.8
|
| 129 |
+
src_lengths = (~padding_mask).sum(-1)
|
| 130 |
+
mask_ratio_observed = (tokens == self.mask_idx).sum(-1).float() / src_lengths
|
| 131 |
+
x = x * (1 - mask_ratio_train) / (1 - mask_ratio_observed)[:, None, None]
|
| 132 |
+
|
| 133 |
+
x = x + self.embed_positions(tokens)
|
| 134 |
+
|
| 135 |
+
if self.model_version == "ESM-1b":
|
| 136 |
+
if self.emb_layer_norm_before:
|
| 137 |
+
x = self.emb_layer_norm_before(x)
|
| 138 |
+
if padding_mask is not None:
|
| 139 |
+
x = x * (1 - padding_mask.unsqueeze(-1).type_as(x))
|
| 140 |
+
|
| 141 |
+
repr_layers = set(repr_layers)
|
| 142 |
+
hidden_representations = {}
|
| 143 |
+
if 0 in repr_layers:
|
| 144 |
+
hidden_representations[0] = x
|
| 145 |
+
|
| 146 |
+
if need_head_weights:
|
| 147 |
+
attn_weights = []
|
| 148 |
+
|
| 149 |
+
# (B, T, E) => (T, B, E)
|
| 150 |
+
x = x.transpose(0, 1)
|
| 151 |
+
|
| 152 |
+
if not padding_mask.any():
|
| 153 |
+
padding_mask = None
|
| 154 |
+
|
| 155 |
+
for layer_idx, layer in enumerate(self.layers):
|
| 156 |
+
x, attn = layer(
|
| 157 |
+
x, self_attn_padding_mask=padding_mask, need_head_weights=need_head_weights
|
| 158 |
+
)
|
| 159 |
+
if (layer_idx + 1) in repr_layers:
|
| 160 |
+
hidden_representations[layer_idx + 1] = x.transpose(0, 1)
|
| 161 |
+
if need_head_weights:
|
| 162 |
+
# (H, B, T, T) => (B, H, T, T)
|
| 163 |
+
attn_weights.append(attn.transpose(1, 0))
|
| 164 |
+
|
| 165 |
+
if self.model_version == "ESM-1b":
|
| 166 |
+
x = self.emb_layer_norm_after(x)
|
| 167 |
+
x = x.transpose(0, 1) # (T, B, E) => (B, T, E)
|
| 168 |
+
|
| 169 |
+
# last hidden representation should have layer norm applied
|
| 170 |
+
if (layer_idx + 1) in repr_layers:
|
| 171 |
+
hidden_representations[layer_idx + 1] = x
|
| 172 |
+
x = self.lm_head(x)
|
| 173 |
+
else:
|
| 174 |
+
x = F.linear(x, self.embed_out, bias=self.embed_out_bias)
|
| 175 |
+
x = x.transpose(0, 1) # (T, B, E) => (B, T, E)
|
| 176 |
+
|
| 177 |
+
result = {"logits": x, "representations": hidden_representations}
|
| 178 |
+
if need_head_weights:
|
| 179 |
+
# attentions: B x L x H x T x T
|
| 180 |
+
attentions = torch.stack(attn_weights, 1)
|
| 181 |
+
if self.model_version == "ESM-1":
|
| 182 |
+
# ESM-1 models have an additional null-token for attention, which we remove
|
| 183 |
+
attentions = attentions[..., :-1]
|
| 184 |
+
if padding_mask is not None:
|
| 185 |
+
attention_mask = 1 - padding_mask.type_as(attentions)
|
| 186 |
+
attention_mask = attention_mask.unsqueeze(1) * attention_mask.unsqueeze(2)
|
| 187 |
+
attentions = attentions * attention_mask[:, None, None, :, :]
|
| 188 |
+
result["attentions"] = attentions
|
| 189 |
+
if return_contacts:
|
| 190 |
+
contacts = self.contact_head(tokens, attentions)
|
| 191 |
+
result["contacts"] = contacts
|
| 192 |
+
|
| 193 |
+
return result
|
| 194 |
+
|
| 195 |
+
def predict_contacts(self, tokens):
|
| 196 |
+
return self(tokens, return_contacts=True)["contacts"]
|
| 197 |
+
|
| 198 |
+
@property
|
| 199 |
+
def num_layers(self):
|
| 200 |
+
return self.args.layers
|
model/esm/esm2.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from typing import Union
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
|
| 10 |
+
from onescience.datapipes.esm import Alphabet
|
| 11 |
+
from onescience.modules.esm import (
|
| 12 |
+
ContactPredictionHead,
|
| 13 |
+
ESM1bLayerNorm,
|
| 14 |
+
RobertaLMHead,
|
| 15 |
+
TransformerLayer,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class ESM2(nn.Module):
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
num_layers: int = 33,
|
| 23 |
+
embed_dim: int = 1280,
|
| 24 |
+
attention_heads: int = 20,
|
| 25 |
+
alphabet: Union[Alphabet, str] = "ESM-1b",
|
| 26 |
+
token_dropout: bool = True,
|
| 27 |
+
):
|
| 28 |
+
super().__init__()
|
| 29 |
+
self.num_layers = num_layers
|
| 30 |
+
self.embed_dim = embed_dim
|
| 31 |
+
self.attention_heads = attention_heads
|
| 32 |
+
if not isinstance(alphabet, Alphabet):
|
| 33 |
+
alphabet = Alphabet.from_architecture(alphabet)
|
| 34 |
+
self.alphabet = alphabet
|
| 35 |
+
self.alphabet_size = len(alphabet)
|
| 36 |
+
self.padding_idx = alphabet.padding_idx
|
| 37 |
+
self.mask_idx = alphabet.mask_idx
|
| 38 |
+
self.cls_idx = alphabet.cls_idx
|
| 39 |
+
self.eos_idx = alphabet.eos_idx
|
| 40 |
+
self.prepend_bos = alphabet.prepend_bos
|
| 41 |
+
self.append_eos = alphabet.append_eos
|
| 42 |
+
self.token_dropout = token_dropout
|
| 43 |
+
|
| 44 |
+
self._init_submodules()
|
| 45 |
+
|
| 46 |
+
def _init_submodules(self):
|
| 47 |
+
self.embed_scale = 1
|
| 48 |
+
self.embed_tokens = nn.Embedding(
|
| 49 |
+
self.alphabet_size,
|
| 50 |
+
self.embed_dim,
|
| 51 |
+
padding_idx=self.padding_idx,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
self.layers = nn.ModuleList(
|
| 55 |
+
[
|
| 56 |
+
TransformerLayer(
|
| 57 |
+
self.embed_dim,
|
| 58 |
+
4 * self.embed_dim,
|
| 59 |
+
self.attention_heads,
|
| 60 |
+
add_bias_kv=False,
|
| 61 |
+
use_esm1b_layer_norm=True,
|
| 62 |
+
use_rotary_embeddings=True,
|
| 63 |
+
)
|
| 64 |
+
for _ in range(self.num_layers)
|
| 65 |
+
]
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
self.contact_head = ContactPredictionHead(
|
| 69 |
+
self.num_layers * self.attention_heads,
|
| 70 |
+
self.prepend_bos,
|
| 71 |
+
self.append_eos,
|
| 72 |
+
eos_idx=self.eos_idx,
|
| 73 |
+
)
|
| 74 |
+
self.emb_layer_norm_after = ESM1bLayerNorm(self.embed_dim)
|
| 75 |
+
|
| 76 |
+
self.lm_head = RobertaLMHead(
|
| 77 |
+
embed_dim=self.embed_dim,
|
| 78 |
+
output_dim=self.alphabet_size,
|
| 79 |
+
weight=self.embed_tokens.weight,
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
def forward(self, tokens, repr_layers=[], need_head_weights=False, return_contacts=False):
|
| 83 |
+
if return_contacts:
|
| 84 |
+
need_head_weights = True
|
| 85 |
+
|
| 86 |
+
assert tokens.ndim == 2
|
| 87 |
+
padding_mask = tokens.eq(self.padding_idx) # B, T
|
| 88 |
+
|
| 89 |
+
x = self.embed_scale * self.embed_tokens(tokens)
|
| 90 |
+
|
| 91 |
+
if self.token_dropout:
|
| 92 |
+
x.masked_fill_((tokens == self.mask_idx).unsqueeze(-1), 0.0)
|
| 93 |
+
# x: B x T x C
|
| 94 |
+
mask_ratio_train = 0.15 * 0.8
|
| 95 |
+
src_lengths = (~padding_mask).sum(-1)
|
| 96 |
+
mask_ratio_observed = (tokens == self.mask_idx).sum(-1).to(x.dtype) / src_lengths
|
| 97 |
+
x = x * (1 - mask_ratio_train) / (1 - mask_ratio_observed)[:, None, None]
|
| 98 |
+
|
| 99 |
+
if padding_mask is not None:
|
| 100 |
+
x = x * (1 - padding_mask.unsqueeze(-1).type_as(x))
|
| 101 |
+
|
| 102 |
+
repr_layers = set(repr_layers)
|
| 103 |
+
hidden_representations = {}
|
| 104 |
+
if 0 in repr_layers:
|
| 105 |
+
hidden_representations[0] = x
|
| 106 |
+
|
| 107 |
+
if need_head_weights:
|
| 108 |
+
attn_weights = []
|
| 109 |
+
|
| 110 |
+
# (B, T, E) => (T, B, E)
|
| 111 |
+
x = x.transpose(0, 1)
|
| 112 |
+
|
| 113 |
+
if not padding_mask.any():
|
| 114 |
+
padding_mask = None
|
| 115 |
+
|
| 116 |
+
for layer_idx, layer in enumerate(self.layers):
|
| 117 |
+
x, attn = layer(
|
| 118 |
+
x,
|
| 119 |
+
self_attn_padding_mask=padding_mask,
|
| 120 |
+
need_head_weights=need_head_weights,
|
| 121 |
+
)
|
| 122 |
+
if (layer_idx + 1) in repr_layers:
|
| 123 |
+
hidden_representations[layer_idx + 1] = x.transpose(0, 1)
|
| 124 |
+
if need_head_weights:
|
| 125 |
+
# (H, B, T, T) => (B, H, T, T)
|
| 126 |
+
attn_weights.append(attn.transpose(1, 0))
|
| 127 |
+
|
| 128 |
+
x = self.emb_layer_norm_after(x)
|
| 129 |
+
x = x.transpose(0, 1) # (T, B, E) => (B, T, E)
|
| 130 |
+
|
| 131 |
+
# last hidden representation should have layer norm applied
|
| 132 |
+
if (layer_idx + 1) in repr_layers:
|
| 133 |
+
hidden_representations[layer_idx + 1] = x
|
| 134 |
+
x = self.lm_head(x)
|
| 135 |
+
|
| 136 |
+
result = {"logits": x, "representations": hidden_representations}
|
| 137 |
+
if need_head_weights:
|
| 138 |
+
# attentions: B x L x H x T x T
|
| 139 |
+
attentions = torch.stack(attn_weights, 1)
|
| 140 |
+
if padding_mask is not None:
|
| 141 |
+
attention_mask = 1 - padding_mask.type_as(attentions)
|
| 142 |
+
attention_mask = attention_mask.unsqueeze(1) * attention_mask.unsqueeze(2)
|
| 143 |
+
attentions = attentions * attention_mask[:, None, None, :, :]
|
| 144 |
+
result["attentions"] = attentions
|
| 145 |
+
if return_contacts:
|
| 146 |
+
contacts = self.contact_head(tokens, attentions)
|
| 147 |
+
result["contacts"] = contacts
|
| 148 |
+
|
| 149 |
+
return result
|
| 150 |
+
|
| 151 |
+
def predict_contacts(self, tokens):
|
| 152 |
+
return self(tokens, return_contacts=True)["contacts"]
|
model/esm/esmfold/__init__.py
ADDED
|
File without changes
|
model/esm/esmfold/v1/categorical_mixture.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class CategoricalMixture:
|
| 9 |
+
def __init__(self, param, bins=50, start=0, end=1):
|
| 10 |
+
# All tensors are of shape ..., bins.
|
| 11 |
+
self.logits = param
|
| 12 |
+
bins = torch.linspace(
|
| 13 |
+
start, end, bins + 1, device=self.logits.device, dtype=self.logits.dtype
|
| 14 |
+
)
|
| 15 |
+
self.v_bins = (bins[:-1] + bins[1:]) / 2
|
| 16 |
+
|
| 17 |
+
def log_prob(self, true):
|
| 18 |
+
# Shapes are:
|
| 19 |
+
# self.probs: ... x bins
|
| 20 |
+
# true : ...
|
| 21 |
+
true_index = (
|
| 22 |
+
(
|
| 23 |
+
true.unsqueeze(-1)
|
| 24 |
+
- self.v_bins[
|
| 25 |
+
[
|
| 26 |
+
None,
|
| 27 |
+
]
|
| 28 |
+
* true.ndim
|
| 29 |
+
]
|
| 30 |
+
)
|
| 31 |
+
.abs()
|
| 32 |
+
.argmin(-1)
|
| 33 |
+
)
|
| 34 |
+
nll = self.logits.log_softmax(-1)
|
| 35 |
+
return torch.take_along_dim(nll, true_index.unsqueeze(-1), dim=-1).squeeze(-1)
|
| 36 |
+
|
| 37 |
+
def mean(self):
|
| 38 |
+
return (self.logits.softmax(-1) @ self.v_bins.unsqueeze(1)).squeeze(-1)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def categorical_lddt(logits, bins=50):
|
| 42 |
+
# Logits are ..., 37, bins.
|
| 43 |
+
return CategoricalMixture(logits, bins=bins).mean()
|
model/esm/esmfold/v1/misc.py
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
import typing as T
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn.functional as F
|
| 10 |
+
from einops import rearrange, repeat
|
| 11 |
+
from torch import nn
|
| 12 |
+
from onescience.utils.openfold.np import residue_constants
|
| 13 |
+
from onescience.utils.openfold.np.protein import Protein as OFProtein
|
| 14 |
+
from onescience.utils.openfold.np.protein import to_pdb
|
| 15 |
+
from onescience.utils.openfold.feats import atom14_to_atom37
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def encode_sequence(
|
| 19 |
+
seq: str,
|
| 20 |
+
residue_index_offset: T.Optional[int] = 512,
|
| 21 |
+
chain_linker: T.Optional[str] = "G" * 25,
|
| 22 |
+
) -> T.Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 23 |
+
if chain_linker is None:
|
| 24 |
+
chain_linker = ""
|
| 25 |
+
if residue_index_offset is None:
|
| 26 |
+
residue_index_offset = 0
|
| 27 |
+
|
| 28 |
+
chains = seq.split(":")
|
| 29 |
+
seq = chain_linker.join(chains)
|
| 30 |
+
|
| 31 |
+
unk_idx = residue_constants.restype_order_with_x["X"]
|
| 32 |
+
encoded = torch.tensor(
|
| 33 |
+
[residue_constants.restype_order_with_x.get(aa, unk_idx) for aa in seq]
|
| 34 |
+
)
|
| 35 |
+
residx = torch.arange(len(encoded))
|
| 36 |
+
|
| 37 |
+
if residue_index_offset > 0:
|
| 38 |
+
start = 0
|
| 39 |
+
for i, chain in enumerate(chains):
|
| 40 |
+
residx[start : start + len(chain) + len(chain_linker)] += (
|
| 41 |
+
i * residue_index_offset
|
| 42 |
+
)
|
| 43 |
+
start += len(chain) + len(chain_linker)
|
| 44 |
+
|
| 45 |
+
linker_mask = torch.ones_like(encoded, dtype=torch.float32)
|
| 46 |
+
chain_index = []
|
| 47 |
+
offset = 0
|
| 48 |
+
for i, chain in enumerate(chains):
|
| 49 |
+
if i > 0:
|
| 50 |
+
chain_index.extend([i - 1] * len(chain_linker))
|
| 51 |
+
chain_index.extend([i] * len(chain))
|
| 52 |
+
offset += len(chain)
|
| 53 |
+
linker_mask[offset : offset + len(chain_linker)] = 0
|
| 54 |
+
offset += len(chain_linker)
|
| 55 |
+
|
| 56 |
+
chain_index = torch.tensor(chain_index, dtype=torch.int64)
|
| 57 |
+
|
| 58 |
+
return encoded, residx, linker_mask, chain_index
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def batch_encode_sequences(
|
| 62 |
+
sequences: T.Sequence[str],
|
| 63 |
+
residue_index_offset: T.Optional[int] = 512,
|
| 64 |
+
chain_linker: T.Optional[str] = "G" * 25,
|
| 65 |
+
) -> T.Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 66 |
+
|
| 67 |
+
aatype_list = []
|
| 68 |
+
residx_list = []
|
| 69 |
+
linker_mask_list = []
|
| 70 |
+
chain_index_list = []
|
| 71 |
+
for seq in sequences:
|
| 72 |
+
aatype_seq, residx_seq, linker_mask_seq, chain_index_seq = encode_sequence(
|
| 73 |
+
seq,
|
| 74 |
+
residue_index_offset=residue_index_offset,
|
| 75 |
+
chain_linker=chain_linker,
|
| 76 |
+
)
|
| 77 |
+
aatype_list.append(aatype_seq)
|
| 78 |
+
residx_list.append(residx_seq)
|
| 79 |
+
linker_mask_list.append(linker_mask_seq)
|
| 80 |
+
chain_index_list.append(chain_index_seq)
|
| 81 |
+
|
| 82 |
+
aatype = collate_dense_tensors(aatype_list)
|
| 83 |
+
mask = collate_dense_tensors(
|
| 84 |
+
[aatype.new_ones(len(aatype_seq)) for aatype_seq in aatype_list]
|
| 85 |
+
)
|
| 86 |
+
residx = collate_dense_tensors(residx_list)
|
| 87 |
+
linker_mask = collate_dense_tensors(linker_mask_list)
|
| 88 |
+
chain_index_list = collate_dense_tensors(chain_index_list, -1)
|
| 89 |
+
|
| 90 |
+
return aatype, mask, residx, linker_mask, chain_index_list
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def output_to_pdb(output: T.Dict) -> T.List[str]:
|
| 94 |
+
"""Returns the pbd (file) string from the model given the model output."""
|
| 95 |
+
# atom14_to_atom37 must be called first, as it fails on latest numpy if the
|
| 96 |
+
# input is a numpy array. It will work if the input is a torch tensor.
|
| 97 |
+
final_atom_positions = atom14_to_atom37(output["positions"][-1], output)
|
| 98 |
+
output = {k: v.to("cpu").numpy() for k, v in output.items()}
|
| 99 |
+
final_atom_positions = final_atom_positions.cpu().numpy()
|
| 100 |
+
final_atom_mask = output["atom37_atom_exists"]
|
| 101 |
+
pdbs = []
|
| 102 |
+
for i in range(output["aatype"].shape[0]):
|
| 103 |
+
aa = output["aatype"][i]
|
| 104 |
+
pred_pos = final_atom_positions[i]
|
| 105 |
+
mask = final_atom_mask[i]
|
| 106 |
+
resid = output["residue_index"][i] + 1
|
| 107 |
+
pred = OFProtein(
|
| 108 |
+
aatype=aa,
|
| 109 |
+
atom_positions=pred_pos,
|
| 110 |
+
atom_mask=mask,
|
| 111 |
+
residue_index=resid,
|
| 112 |
+
b_factors=output["plddt"][i],
|
| 113 |
+
chain_index=output["chain_index"][i] if "chain_index" in output else None,
|
| 114 |
+
)
|
| 115 |
+
pdbs.append(to_pdb(pred))
|
| 116 |
+
return pdbs
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def collate_dense_tensors(
|
| 120 |
+
samples: T.List[torch.Tensor], pad_v: float = 0
|
| 121 |
+
) -> torch.Tensor:
|
| 122 |
+
"""
|
| 123 |
+
Takes a list of tensors with the following dimensions:
|
| 124 |
+
[(d_11, ..., d_1K),
|
| 125 |
+
(d_21, ..., d_2K),
|
| 126 |
+
...,
|
| 127 |
+
(d_N1, ..., d_NK)]
|
| 128 |
+
and stack + pads them into a single tensor of:
|
| 129 |
+
(N, max_i=1,N { d_i1 }, ..., max_i=1,N {diK})
|
| 130 |
+
"""
|
| 131 |
+
if len(samples) == 0:
|
| 132 |
+
return torch.Tensor()
|
| 133 |
+
if len(set(x.dim() for x in samples)) != 1:
|
| 134 |
+
raise RuntimeError(
|
| 135 |
+
f"Samples has varying dimensions: {[x.dim() for x in samples]}"
|
| 136 |
+
)
|
| 137 |
+
(device,) = tuple(set(x.device for x in samples)) # assumes all on same device
|
| 138 |
+
max_shape = [max(lst) for lst in zip(*[x.shape for x in samples])]
|
| 139 |
+
result = torch.empty(
|
| 140 |
+
len(samples), *max_shape, dtype=samples[0].dtype, device=device
|
| 141 |
+
)
|
| 142 |
+
result.fill_(pad_v)
|
| 143 |
+
for i in range(len(samples)):
|
| 144 |
+
result_i = result[i]
|
| 145 |
+
t = samples[i]
|
| 146 |
+
result_i[tuple(slice(0, k) for k in t.shape)] = t
|
| 147 |
+
return result
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
class Attention(nn.Module):
|
| 151 |
+
def __init__(self, embed_dim, num_heads, head_width, gated=False):
|
| 152 |
+
super().__init__()
|
| 153 |
+
assert embed_dim == num_heads * head_width
|
| 154 |
+
|
| 155 |
+
self.embed_dim = embed_dim
|
| 156 |
+
self.num_heads = num_heads
|
| 157 |
+
self.head_width = head_width
|
| 158 |
+
|
| 159 |
+
self.proj = nn.Linear(embed_dim, embed_dim * 3, bias=False)
|
| 160 |
+
self.o_proj = nn.Linear(embed_dim, embed_dim, bias=True)
|
| 161 |
+
self.gated = gated
|
| 162 |
+
if gated:
|
| 163 |
+
self.g_proj = nn.Linear(embed_dim, embed_dim)
|
| 164 |
+
torch.nn.init.zeros_(self.g_proj.weight)
|
| 165 |
+
torch.nn.init.ones_(self.g_proj.bias)
|
| 166 |
+
|
| 167 |
+
self.rescale_factor = self.head_width**-0.5
|
| 168 |
+
|
| 169 |
+
torch.nn.init.zeros_(self.o_proj.bias)
|
| 170 |
+
|
| 171 |
+
def forward(self, x, mask=None, bias=None, indices=None):
|
| 172 |
+
"""
|
| 173 |
+
Basic self attention with optional mask and external pairwise bias.
|
| 174 |
+
To handle sequences of different lengths, use mask.
|
| 175 |
+
|
| 176 |
+
Inputs:
|
| 177 |
+
x: batch of input sequneces (.. x L x C)
|
| 178 |
+
mask: batch of boolean masks where 1=valid, 0=padding position (.. x L_k). optional.
|
| 179 |
+
bias: batch of scalar pairwise attention biases (.. x Lq x Lk x num_heads). optional.
|
| 180 |
+
|
| 181 |
+
Outputs:
|
| 182 |
+
sequence projection (B x L x embed_dim), attention maps (B x L x L x num_heads)
|
| 183 |
+
"""
|
| 184 |
+
|
| 185 |
+
t = rearrange(self.proj(x), "... l (h c) -> ... h l c", h=self.num_heads)
|
| 186 |
+
q, k, v = t.chunk(3, dim=-1)
|
| 187 |
+
|
| 188 |
+
q = self.rescale_factor * q
|
| 189 |
+
a = torch.einsum("...qc,...kc->...qk", q, k)
|
| 190 |
+
|
| 191 |
+
# Add external attention bias.
|
| 192 |
+
if bias is not None:
|
| 193 |
+
a = a + rearrange(bias, "... lq lk h -> ... h lq lk")
|
| 194 |
+
|
| 195 |
+
# Do not attend to padding tokens.
|
| 196 |
+
if mask is not None:
|
| 197 |
+
mask = repeat(
|
| 198 |
+
mask, "... lk -> ... h lq lk", h=self.num_heads, lq=q.shape[-2]
|
| 199 |
+
)
|
| 200 |
+
a = a.masked_fill(mask == False, -np.inf)
|
| 201 |
+
|
| 202 |
+
a = F.softmax(a, dim=-1)
|
| 203 |
+
|
| 204 |
+
y = torch.einsum("...hqk,...hkc->...qhc", a, v)
|
| 205 |
+
y = rearrange(y, "... h c -> ... (h c)", h=self.num_heads)
|
| 206 |
+
|
| 207 |
+
if self.gated:
|
| 208 |
+
y = self.g_proj(x).sigmoid() * y
|
| 209 |
+
y = self.o_proj(y)
|
| 210 |
+
|
| 211 |
+
return y, rearrange(a, "... lq lk h -> ... h lq lk")
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
class Dropout(nn.Module):
|
| 215 |
+
"""
|
| 216 |
+
Implementation of dropout with the ability to share the dropout mask
|
| 217 |
+
along a particular dimension.
|
| 218 |
+
"""
|
| 219 |
+
|
| 220 |
+
def __init__(self, r: float, batch_dim: T.Union[int, T.List[int]]):
|
| 221 |
+
super(Dropout, self).__init__()
|
| 222 |
+
|
| 223 |
+
self.r = r
|
| 224 |
+
if type(batch_dim) == int:
|
| 225 |
+
batch_dim = [batch_dim]
|
| 226 |
+
self.batch_dim = batch_dim
|
| 227 |
+
self.dropout = nn.Dropout(self.r)
|
| 228 |
+
|
| 229 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 230 |
+
shape = list(x.shape)
|
| 231 |
+
if self.batch_dim is not None:
|
| 232 |
+
for bd in self.batch_dim:
|
| 233 |
+
shape[bd] = 1
|
| 234 |
+
return x * self.dropout(x.new_ones(shape))
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
class SequenceToPair(nn.Module):
|
| 238 |
+
def __init__(self, sequence_state_dim, inner_dim, pairwise_state_dim):
|
| 239 |
+
super().__init__()
|
| 240 |
+
|
| 241 |
+
self.layernorm = nn.LayerNorm(sequence_state_dim)
|
| 242 |
+
self.proj = nn.Linear(sequence_state_dim, inner_dim * 2, bias=True)
|
| 243 |
+
self.o_proj = nn.Linear(2 * inner_dim, pairwise_state_dim, bias=True)
|
| 244 |
+
|
| 245 |
+
torch.nn.init.zeros_(self.proj.bias)
|
| 246 |
+
torch.nn.init.zeros_(self.o_proj.bias)
|
| 247 |
+
|
| 248 |
+
def forward(self, sequence_state):
|
| 249 |
+
"""
|
| 250 |
+
Inputs:
|
| 251 |
+
sequence_state: B x L x sequence_state_dim
|
| 252 |
+
|
| 253 |
+
Output:
|
| 254 |
+
pairwise_state: B x L x L x pairwise_state_dim
|
| 255 |
+
|
| 256 |
+
Intermediate state:
|
| 257 |
+
B x L x L x 2*inner_dim
|
| 258 |
+
"""
|
| 259 |
+
|
| 260 |
+
assert len(sequence_state.shape) == 3
|
| 261 |
+
|
| 262 |
+
s = self.layernorm(sequence_state)
|
| 263 |
+
s = self.proj(s)
|
| 264 |
+
q, k = s.chunk(2, dim=-1)
|
| 265 |
+
|
| 266 |
+
prod = q[:, None, :, :] * k[:, :, None, :]
|
| 267 |
+
diff = q[:, None, :, :] - k[:, :, None, :]
|
| 268 |
+
|
| 269 |
+
x = torch.cat([prod, diff], dim=-1)
|
| 270 |
+
x = self.o_proj(x)
|
| 271 |
+
|
| 272 |
+
return x
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
class PairToSequence(nn.Module):
|
| 276 |
+
def __init__(self, pairwise_state_dim, num_heads):
|
| 277 |
+
super().__init__()
|
| 278 |
+
|
| 279 |
+
self.layernorm = nn.LayerNorm(pairwise_state_dim)
|
| 280 |
+
self.linear = nn.Linear(pairwise_state_dim, num_heads, bias=False)
|
| 281 |
+
|
| 282 |
+
def forward(self, pairwise_state):
|
| 283 |
+
"""
|
| 284 |
+
Inputs:
|
| 285 |
+
pairwise_state: B x L x L x pairwise_state_dim
|
| 286 |
+
|
| 287 |
+
Output:
|
| 288 |
+
pairwise_bias: B x L x L x num_heads
|
| 289 |
+
"""
|
| 290 |
+
assert len(pairwise_state.shape) == 4
|
| 291 |
+
z = self.layernorm(pairwise_state)
|
| 292 |
+
pairwise_bias = self.linear(z)
|
| 293 |
+
return pairwise_bias
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
class ResidueMLP(nn.Module):
|
| 297 |
+
def __init__(self, embed_dim, inner_dim, norm=nn.LayerNorm, dropout=0):
|
| 298 |
+
super().__init__()
|
| 299 |
+
|
| 300 |
+
self.mlp = nn.Sequential(
|
| 301 |
+
norm(embed_dim),
|
| 302 |
+
nn.Linear(embed_dim, inner_dim),
|
| 303 |
+
nn.ReLU(),
|
| 304 |
+
nn.Linear(inner_dim, embed_dim),
|
| 305 |
+
nn.Dropout(dropout),
|
| 306 |
+
)
|
| 307 |
+
|
| 308 |
+
def forward(self, x):
|
| 309 |
+
return x + self.mlp(x)
|
model/esm/esmfold/v1/pretrained.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
|
| 10 |
+
from model.esm.esmfold.v1.esmfold import ESMFold
|
| 11 |
+
|
| 12 |
+
def _upgrade_state_dict(model_state):
|
| 13 |
+
"""Map legacy ESMFold checkpoint keys to the current OpenFold modules."""
|
| 14 |
+
point_projection_prefixes = (
|
| 15 |
+
"trunk.structure_module.ipa.linear_q_points",
|
| 16 |
+
"trunk.structure_module.ipa.linear_kv_points",
|
| 17 |
+
"trunk.structure_module.ipa.linear_k_points",
|
| 18 |
+
"trunk.structure_module.ipa.linear_v_points",
|
| 19 |
+
)
|
| 20 |
+
for prefix in point_projection_prefixes:
|
| 21 |
+
for suffix in ("weight", "bias"):
|
| 22 |
+
old_key = f"{prefix}.{suffix}"
|
| 23 |
+
new_key = f"{prefix}.linear.{suffix}"
|
| 24 |
+
if old_key in model_state and new_key not in model_state:
|
| 25 |
+
model_state[new_key] = model_state.pop(old_key)
|
| 26 |
+
return model_state
|
| 27 |
+
|
| 28 |
+
def _load_model(model_name):
|
| 29 |
+
if model_name.endswith(".pt"): # local, treat as filepath
|
| 30 |
+
model_path = Path(model_name)
|
| 31 |
+
model_data = torch.load(str(model_path), map_location="cpu")
|
| 32 |
+
else: # load from hub
|
| 33 |
+
url = f"https://dl.fbaipublicfiles.com/fair-esm/models/{model_name}.pt"
|
| 34 |
+
model_data = torch.hub.load_state_dict_from_url(url, progress=False, map_location="cpu")
|
| 35 |
+
|
| 36 |
+
cfg = model_data["cfg"]["model"]
|
| 37 |
+
model_state = _upgrade_state_dict(model_data["model"])
|
| 38 |
+
# model_state = model_data["model"]
|
| 39 |
+
model = ESMFold(esmfold_config=cfg)
|
| 40 |
+
|
| 41 |
+
expected_keys = set(model.state_dict().keys())
|
| 42 |
+
found_keys = set(model_state.keys())
|
| 43 |
+
|
| 44 |
+
missing_essential_keys = []
|
| 45 |
+
for missing_key in expected_keys - found_keys:
|
| 46 |
+
if not missing_key.startswith("esm."):
|
| 47 |
+
missing_essential_keys.append(missing_key)
|
| 48 |
+
|
| 49 |
+
if missing_essential_keys:
|
| 50 |
+
raise RuntimeError(f"Keys '{', '.join(missing_essential_keys)}' are missing.")
|
| 51 |
+
|
| 52 |
+
model.load_state_dict(model_state, strict=False)
|
| 53 |
+
|
| 54 |
+
return model
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def esmfold_v0():
|
| 58 |
+
"""
|
| 59 |
+
ESMFold v0 model with 3B ESM-2, 48 folding blocks.
|
| 60 |
+
This version was used for the paper (Lin et al, 2022). It was trained
|
| 61 |
+
on all PDB chains until 2020-05, to ensure temporal holdout with CASP14
|
| 62 |
+
and the CAMEO validation and test set reported there.
|
| 63 |
+
"""
|
| 64 |
+
return _load_model("esmfold_3B_v0")
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def esmfold_v1():
|
| 68 |
+
"""
|
| 69 |
+
ESMFold v1 model using 3B ESM-2, 48 folding blocks.
|
| 70 |
+
ESMFold provides fast high accuracy atomic level structure prediction
|
| 71 |
+
directly from the individual sequence of a protein. ESMFold uses the ESM2
|
| 72 |
+
protein language model to extract meaningful representations from the
|
| 73 |
+
protein sequence.
|
| 74 |
+
"""
|
| 75 |
+
return _load_model("esmfold_3B_v1")
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def esmfold_structure_module_only_8M():
|
| 79 |
+
"""
|
| 80 |
+
ESMFold baseline model using 8M ESM-2, 0 folding blocks.
|
| 81 |
+
ESM-2 here is trained out to 500K updates.
|
| 82 |
+
This is a model designed to test the capabilities of the language model
|
| 83 |
+
when ablated for number of parameters in the language model.
|
| 84 |
+
See table S1 in (Lin et al, 2022).
|
| 85 |
+
"""
|
| 86 |
+
return _load_model("esmfold_structure_module_only_8M")
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def esmfold_structure_module_only_8M_270K():
|
| 90 |
+
"""
|
| 91 |
+
ESMFold baseline model using 8M ESM-2, 0 folding blocks.
|
| 92 |
+
ESM-2 here is trained out to 270K updates.
|
| 93 |
+
This is a model designed to test the capabilities of the language model
|
| 94 |
+
when ablated for number of parameters in the language model.
|
| 95 |
+
See table S1 in (Lin et al, 2022).
|
| 96 |
+
"""
|
| 97 |
+
return _load_model("esmfold_structure_module_only_8M_270K")
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def esmfold_structure_module_only_35M():
|
| 101 |
+
"""
|
| 102 |
+
ESMFold baseline model using 35M ESM-2, 0 folding blocks.
|
| 103 |
+
ESM-2 here is trained out to 500K updates.
|
| 104 |
+
This is a model designed to test the capabilities of the language model
|
| 105 |
+
when ablated for number of parameters in the language model.
|
| 106 |
+
See table S1 in (Lin et al, 2022).
|
| 107 |
+
"""
|
| 108 |
+
return _load_model("esmfold_structure_module_only_35M")
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def esmfold_structure_module_only_35M_270K():
|
| 112 |
+
"""
|
| 113 |
+
ESMFold baseline model using 35M ESM-2, 0 folding blocks.
|
| 114 |
+
ESM-2 here is trained out to 270K updates.
|
| 115 |
+
This is a model designed to test the capabilities of the language model
|
| 116 |
+
when ablated for number of parameters in the language model.
|
| 117 |
+
See table S1 in (Lin et al, 2022).
|
| 118 |
+
"""
|
| 119 |
+
return _load_model("esmfold_structure_module_only_35M_270K")
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def esmfold_structure_module_only_150M():
|
| 123 |
+
"""
|
| 124 |
+
ESMFold baseline model using 150M ESM-2, 0 folding blocks.
|
| 125 |
+
ESM-2 here is trained out to 500K updates.
|
| 126 |
+
This is a model designed to test the capabilities of the language model
|
| 127 |
+
when ablated for number of parameters in the language model.
|
| 128 |
+
See table S1 in (Lin et al, 2022).
|
| 129 |
+
"""
|
| 130 |
+
return _load_model("esmfold_structure_module_only_150M")
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def esmfold_structure_module_only_150M_270K():
|
| 134 |
+
"""
|
| 135 |
+
ESMFold baseline model using 150M ESM-2, 0 folding blocks.
|
| 136 |
+
ESM-2 here is trained out to 270K updates.
|
| 137 |
+
This is a model designed to test the capabilities of the language model
|
| 138 |
+
when ablated for number of parameters in the language model.
|
| 139 |
+
See table S1 in (Lin et al, 2022).
|
| 140 |
+
"""
|
| 141 |
+
return _load_model("esmfold_structure_module_only_150M_270K")
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def esmfold_structure_module_only_650M():
|
| 145 |
+
"""
|
| 146 |
+
ESMFold baseline model using 650M ESM-2, 0 folding blocks.
|
| 147 |
+
ESM-2 here is trained out to 500K updates.
|
| 148 |
+
This is a model designed to test the capabilities of the language model
|
| 149 |
+
when ablated for number of parameters in the language model.
|
| 150 |
+
See table S1 in (Lin et al, 2022).
|
| 151 |
+
"""
|
| 152 |
+
return _load_model("esmfold_structure_module_only_650M")
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def esmfold_structure_module_only_650M_270K():
|
| 156 |
+
"""
|
| 157 |
+
ESMFold baseline model using 650M ESM-2, 0 folding blocks.
|
| 158 |
+
ESM-2 here is trained out to 270K updates.
|
| 159 |
+
This is a model designed to test the capabilities of the language model
|
| 160 |
+
when ablated for number of parameters in the language model.
|
| 161 |
+
See table S1 in (Lin et al, 2022).
|
| 162 |
+
"""
|
| 163 |
+
return _load_model("esmfold_structure_module_only_650M_270K")
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def esmfold_structure_module_only_3B():
|
| 167 |
+
"""
|
| 168 |
+
ESMFold baseline model using 3B ESM-2, 0 folding blocks.
|
| 169 |
+
ESM-2 here is trained out to 500K updates.
|
| 170 |
+
This is a model designed to test the capabilities of the language model
|
| 171 |
+
when ablated for number of parameters in the language model.
|
| 172 |
+
See table S1 in (Lin et al, 2022).
|
| 173 |
+
"""
|
| 174 |
+
return _load_model("esmfold_structure_module_only_3B")
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def esmfold_structure_module_only_3B_270K():
|
| 178 |
+
"""
|
| 179 |
+
ESMFold baseline model using 3B ESM-2, 0 folding blocks.
|
| 180 |
+
ESM-2 here is trained out to 270K updates.
|
| 181 |
+
This is a model designed to test the capabilities of the language model
|
| 182 |
+
when ablated for number of parameters in the language model.
|
| 183 |
+
See table S1 in (Lin et al, 2022).
|
| 184 |
+
"""
|
| 185 |
+
return _load_model("esmfold_structure_module_only_3B_270K")
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def esmfold_structure_module_only_15B():
|
| 189 |
+
"""
|
| 190 |
+
ESMFold baseline model using 15B ESM-2, 0 folding blocks.
|
| 191 |
+
ESM-2 here is trained out to 270K updates.
|
| 192 |
+
The 15B parameter ESM-2 was not trained out to 500K updates
|
| 193 |
+
This is a model designed to test the capabilities of the language model
|
| 194 |
+
when ablated for number of parameters in the language model.
|
| 195 |
+
See table S1 in (Lin et al, 2022).
|
| 196 |
+
"""
|
| 197 |
+
return _load_model("esmfold_structure_module_only_15B")
|
model/esm/inverse_folding/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from . import gvp_transformer
|
| 7 |
+
from . import util
|
| 8 |
+
from . import multichain_util
|
model/esm/inverse_folding/features.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
#
|
| 6 |
+
# Portions of this file were adapted from the open source code for the following
|
| 7 |
+
# two papers:
|
| 8 |
+
#
|
| 9 |
+
# Ingraham, J., Garg, V., Barzilay, R., & Jaakkola, T. (2019). Generative
|
| 10 |
+
# models for graph-based protein design. Advances in Neural Information
|
| 11 |
+
# Processing Systems, 32.
|
| 12 |
+
#
|
| 13 |
+
# Jing, B., Eismann, S., Suriana, P., Townshend, R. J. L., & Dror, R. (2020).
|
| 14 |
+
# Learning from Protein Structure with Geometric Vector Perceptrons. In
|
| 15 |
+
# International Conference on Learning Representations.
|
| 16 |
+
#
|
| 17 |
+
# MIT License
|
| 18 |
+
#
|
| 19 |
+
# Copyright (c) 2020 Bowen Jing, Stephan Eismann, Patricia Suriana, Raphael Townshend, Ron Dror
|
| 20 |
+
#
|
| 21 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 22 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 23 |
+
# in the Software without restriction, including without limitation the rights
|
| 24 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 25 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 26 |
+
# furnished to do so, subject to the following conditions:
|
| 27 |
+
#
|
| 28 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 29 |
+
# copies or substantial portions of the Software.
|
| 30 |
+
#
|
| 31 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 32 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 33 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 34 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 35 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 36 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 37 |
+
# SOFTWARE.
|
| 38 |
+
#
|
| 39 |
+
# ================================================================
|
| 40 |
+
# The below license applies to the portions of the code (parts of
|
| 41 |
+
# src/datasets.py and src/models.py) adapted from Ingraham, et al.
|
| 42 |
+
# ================================================================
|
| 43 |
+
#
|
| 44 |
+
# MIT License
|
| 45 |
+
#
|
| 46 |
+
# Copyright (c) 2019 John Ingraham, Vikas Garg, Regina Barzilay, Tommi Jaakkola
|
| 47 |
+
#
|
| 48 |
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 49 |
+
# of this software and associated documentation files (the "Software"), to deal
|
| 50 |
+
# in the Software without restriction, including without limitation the rights
|
| 51 |
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 52 |
+
# copies of the Software, and to permit persons to whom the Software is
|
| 53 |
+
# furnished to do so, subject to the following conditions:
|
| 54 |
+
#
|
| 55 |
+
# The above copyright notice and this permission notice shall be included in all
|
| 56 |
+
# copies or substantial portions of the Software.
|
| 57 |
+
#
|
| 58 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 59 |
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 60 |
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 61 |
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 62 |
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 63 |
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 64 |
+
# SOFTWARE.
|
| 65 |
+
|
| 66 |
+
import math
|
| 67 |
+
import numpy as np
|
| 68 |
+
import torch
|
| 69 |
+
import torch.nn as nn
|
| 70 |
+
import torch.nn.functional as F
|
| 71 |
+
|
| 72 |
+
from .gvp_utils import flatten_graph
|
| 73 |
+
from .gvp_modules import GVP, LayerNorm
|
| 74 |
+
from .util import normalize, norm, nan_to_num, rbf
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class GVPInputFeaturizer(nn.Module):
|
| 78 |
+
|
| 79 |
+
@staticmethod
|
| 80 |
+
def get_node_features(coords, coord_mask, with_coord_mask=True):
|
| 81 |
+
# scalar features
|
| 82 |
+
node_scalar_features = GVPInputFeaturizer._dihedrals(coords)
|
| 83 |
+
if with_coord_mask:
|
| 84 |
+
node_scalar_features = torch.cat([
|
| 85 |
+
node_scalar_features,
|
| 86 |
+
coord_mask.float().unsqueeze(-1)
|
| 87 |
+
], dim=-1)
|
| 88 |
+
# vector features
|
| 89 |
+
X_ca = coords[:, :, 1]
|
| 90 |
+
orientations = GVPInputFeaturizer._orientations(X_ca)
|
| 91 |
+
sidechains = GVPInputFeaturizer._sidechains(coords)
|
| 92 |
+
node_vector_features = torch.cat([orientations, sidechains.unsqueeze(-2)], dim=-2)
|
| 93 |
+
return node_scalar_features, node_vector_features
|
| 94 |
+
|
| 95 |
+
@staticmethod
|
| 96 |
+
def _orientations(X):
|
| 97 |
+
forward = normalize(X[:, 1:] - X[:, :-1])
|
| 98 |
+
backward = normalize(X[:, :-1] - X[:, 1:])
|
| 99 |
+
forward = F.pad(forward, [0, 0, 0, 1])
|
| 100 |
+
backward = F.pad(backward, [0, 0, 1, 0])
|
| 101 |
+
return torch.cat([forward.unsqueeze(-2), backward.unsqueeze(-2)], -2)
|
| 102 |
+
|
| 103 |
+
@staticmethod
|
| 104 |
+
def _sidechains(X):
|
| 105 |
+
n, origin, c = X[:, :, 0], X[:, :, 1], X[:, :, 2]
|
| 106 |
+
c, n = normalize(c - origin), normalize(n - origin)
|
| 107 |
+
bisector = normalize(c + n)
|
| 108 |
+
perp = normalize(torch.cross(c, n, dim=-1))
|
| 109 |
+
vec = -bisector * math.sqrt(1 / 3) - perp * math.sqrt(2 / 3)
|
| 110 |
+
return vec
|
| 111 |
+
|
| 112 |
+
@staticmethod
|
| 113 |
+
def _dihedrals(X, eps=1e-7):
|
| 114 |
+
X = torch.flatten(X[:, :, :3], 1, 2)
|
| 115 |
+
bsz = X.shape[0]
|
| 116 |
+
dX = X[:, 1:] - X[:, :-1]
|
| 117 |
+
U = normalize(dX, dim=-1)
|
| 118 |
+
u_2 = U[:, :-2]
|
| 119 |
+
u_1 = U[:, 1:-1]
|
| 120 |
+
u_0 = U[:, 2:]
|
| 121 |
+
|
| 122 |
+
# Backbone normals
|
| 123 |
+
n_2 = normalize(torch.cross(u_2, u_1, dim=-1), dim=-1)
|
| 124 |
+
n_1 = normalize(torch.cross(u_1, u_0, dim=-1), dim=-1)
|
| 125 |
+
|
| 126 |
+
# Angle between normals
|
| 127 |
+
cosD = torch.sum(n_2 * n_1, -1)
|
| 128 |
+
cosD = torch.clamp(cosD, -1 + eps, 1 - eps)
|
| 129 |
+
D = torch.sign(torch.sum(u_2 * n_1, -1)) * torch.acos(cosD)
|
| 130 |
+
|
| 131 |
+
# This scheme will remove phi[0], psi[-1], omega[-1]
|
| 132 |
+
D = F.pad(D, [1, 2])
|
| 133 |
+
D = torch.reshape(D, [bsz, -1, 3])
|
| 134 |
+
# Lift angle representations to the circle
|
| 135 |
+
D_features = torch.cat([torch.cos(D), torch.sin(D)], -1)
|
| 136 |
+
return D_features
|
| 137 |
+
|
| 138 |
+
@staticmethod
|
| 139 |
+
def _positional_embeddings(edge_index,
|
| 140 |
+
num_embeddings=None,
|
| 141 |
+
num_positional_embeddings=16,
|
| 142 |
+
period_range=[2, 1000]):
|
| 143 |
+
# From https://github.com/jingraham/neurips19-graph-protein-design
|
| 144 |
+
num_embeddings = num_embeddings or num_positional_embeddings
|
| 145 |
+
d = edge_index[0] - edge_index[1]
|
| 146 |
+
|
| 147 |
+
frequency = torch.exp(
|
| 148 |
+
torch.arange(0, num_embeddings, 2, dtype=torch.float32,
|
| 149 |
+
device=edge_index.device)
|
| 150 |
+
* -(np.log(10000.0) / num_embeddings)
|
| 151 |
+
)
|
| 152 |
+
angles = d.unsqueeze(-1) * frequency
|
| 153 |
+
E = torch.cat((torch.cos(angles), torch.sin(angles)), -1)
|
| 154 |
+
return E
|
| 155 |
+
|
| 156 |
+
@staticmethod
|
| 157 |
+
def _dist(X, coord_mask, padding_mask, top_k_neighbors, eps=1e-8):
|
| 158 |
+
""" Pairwise euclidean distances """
|
| 159 |
+
bsz, maxlen = X.size(0), X.size(1)
|
| 160 |
+
coord_mask_2D = torch.unsqueeze(coord_mask,1) * torch.unsqueeze(coord_mask,2)
|
| 161 |
+
residue_mask = ~padding_mask
|
| 162 |
+
residue_mask_2D = torch.unsqueeze(residue_mask,1) * torch.unsqueeze(residue_mask,2)
|
| 163 |
+
dX = torch.unsqueeze(X,1) - torch.unsqueeze(X,2)
|
| 164 |
+
D = coord_mask_2D * norm(dX, dim=-1)
|
| 165 |
+
|
| 166 |
+
# sorting preference: first those with coords, then among the residues that
|
| 167 |
+
# exist but are masked use distance in sequence as tie breaker, and then the
|
| 168 |
+
# residues that came from padding are last
|
| 169 |
+
seqpos = torch.arange(maxlen, device=X.device)
|
| 170 |
+
Dseq = torch.abs(seqpos.unsqueeze(1) - seqpos.unsqueeze(0)).repeat(bsz, 1, 1)
|
| 171 |
+
D_adjust = nan_to_num(D) + (~coord_mask_2D) * (1e8 + Dseq*1e6) + (
|
| 172 |
+
~residue_mask_2D) * (1e10)
|
| 173 |
+
|
| 174 |
+
if top_k_neighbors == -1:
|
| 175 |
+
D_neighbors = D_adjust
|
| 176 |
+
E_idx = seqpos.repeat(
|
| 177 |
+
*D_neighbors.shape[:-1], 1)
|
| 178 |
+
else:
|
| 179 |
+
# Identify k nearest neighbors (including self)
|
| 180 |
+
k = min(top_k_neighbors, X.size(1))
|
| 181 |
+
D_neighbors, E_idx = torch.topk(D_adjust, k, dim=-1, largest=False)
|
| 182 |
+
|
| 183 |
+
coord_mask_neighbors = (D_neighbors < 5e7)
|
| 184 |
+
residue_mask_neighbors = (D_neighbors < 5e9)
|
| 185 |
+
return D_neighbors, E_idx, coord_mask_neighbors, residue_mask_neighbors
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class Normalize(nn.Module):
|
| 189 |
+
def __init__(self, features, epsilon=1e-6):
|
| 190 |
+
super(Normalize, self).__init__()
|
| 191 |
+
self.gain = nn.Parameter(torch.ones(features))
|
| 192 |
+
self.bias = nn.Parameter(torch.zeros(features))
|
| 193 |
+
self.epsilon = epsilon
|
| 194 |
+
|
| 195 |
+
def forward(self, x, dim=-1):
|
| 196 |
+
mu = x.mean(dim, keepdim=True)
|
| 197 |
+
sigma = torch.sqrt(x.var(dim, keepdim=True) + self.epsilon)
|
| 198 |
+
gain = self.gain
|
| 199 |
+
bias = self.bias
|
| 200 |
+
# Reshape
|
| 201 |
+
if dim != -1:
|
| 202 |
+
shape = [1] * len(mu.size())
|
| 203 |
+
shape[dim] = self.gain.size()[0]
|
| 204 |
+
gain = gain.view(shape)
|
| 205 |
+
bias = bias.view(shape)
|
| 206 |
+
return gain * (x - mu) / (sigma + self.epsilon) + bias
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
class DihedralFeatures(nn.Module):
|
| 210 |
+
def __init__(self, node_embed_dim):
|
| 211 |
+
""" Embed dihedral angle features. """
|
| 212 |
+
super(DihedralFeatures, self).__init__()
|
| 213 |
+
# 3 dihedral angles; sin and cos of each angle
|
| 214 |
+
node_in = 6
|
| 215 |
+
# Normalization and embedding
|
| 216 |
+
self.node_embedding = nn.Linear(node_in, node_embed_dim, bias=True)
|
| 217 |
+
self.norm_nodes = Normalize(node_embed_dim)
|
| 218 |
+
|
| 219 |
+
def forward(self, X):
|
| 220 |
+
""" Featurize coordinates as an attributed graph """
|
| 221 |
+
V = self._dihedrals(X)
|
| 222 |
+
V = self.node_embedding(V)
|
| 223 |
+
V = self.norm_nodes(V)
|
| 224 |
+
return V
|
| 225 |
+
|
| 226 |
+
@staticmethod
|
| 227 |
+
def _dihedrals(X, eps=1e-7, return_angles=False):
|
| 228 |
+
# First 3 coordinates are N, CA, C
|
| 229 |
+
X = X[:,:,:3,:].reshape(X.shape[0], 3*X.shape[1], 3)
|
| 230 |
+
|
| 231 |
+
# Shifted slices of unit vectors
|
| 232 |
+
dX = X[:,1:,:] - X[:,:-1,:]
|
| 233 |
+
U = F.normalize(dX, dim=-1)
|
| 234 |
+
u_2 = U[:,:-2,:]
|
| 235 |
+
u_1 = U[:,1:-1,:]
|
| 236 |
+
u_0 = U[:,2:,:]
|
| 237 |
+
# Backbone normals
|
| 238 |
+
n_2 = F.normalize(torch.cross(u_2, u_1, dim=-1), dim=-1)
|
| 239 |
+
n_1 = F.normalize(torch.cross(u_1, u_0, dim=-1), dim=-1)
|
| 240 |
+
|
| 241 |
+
# Angle between normals
|
| 242 |
+
cosD = (n_2 * n_1).sum(-1)
|
| 243 |
+
cosD = torch.clamp(cosD, -1+eps, 1-eps)
|
| 244 |
+
D = torch.sign((u_2 * n_1).sum(-1)) * torch.acos(cosD)
|
| 245 |
+
|
| 246 |
+
# This scheme will remove phi[0], psi[-1], omega[-1]
|
| 247 |
+
D = F.pad(D, (1,2), 'constant', 0)
|
| 248 |
+
D = D.view((D.size(0), int(D.size(1)/3), 3))
|
| 249 |
+
phi, psi, omega = torch.unbind(D,-1)
|
| 250 |
+
|
| 251 |
+
if return_angles:
|
| 252 |
+
return phi, psi, omega
|
| 253 |
+
|
| 254 |
+
# Lift angle representations to the circle
|
| 255 |
+
D_features = torch.cat((torch.cos(D), torch.sin(D)), 2)
|
| 256 |
+
return D_features
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
class GVPGraphEmbedding(GVPInputFeaturizer):
|
| 260 |
+
|
| 261 |
+
def __init__(self, args):
|
| 262 |
+
super().__init__()
|
| 263 |
+
self.top_k_neighbors = args.top_k_neighbors
|
| 264 |
+
self.num_positional_embeddings = 16
|
| 265 |
+
self.remove_edges_without_coords = True
|
| 266 |
+
node_input_dim = (7, 3)
|
| 267 |
+
edge_input_dim = (34, 1)
|
| 268 |
+
node_hidden_dim = (args.node_hidden_dim_scalar,
|
| 269 |
+
args.node_hidden_dim_vector)
|
| 270 |
+
edge_hidden_dim = (args.edge_hidden_dim_scalar,
|
| 271 |
+
args.edge_hidden_dim_vector)
|
| 272 |
+
self.embed_node = nn.Sequential(
|
| 273 |
+
GVP(node_input_dim, node_hidden_dim, activations=(None, None)),
|
| 274 |
+
LayerNorm(node_hidden_dim, eps=1e-4)
|
| 275 |
+
)
|
| 276 |
+
self.embed_edge = nn.Sequential(
|
| 277 |
+
GVP(edge_input_dim, edge_hidden_dim, activations=(None, None)),
|
| 278 |
+
LayerNorm(edge_hidden_dim, eps=1e-4)
|
| 279 |
+
)
|
| 280 |
+
self.embed_confidence = nn.Linear(16, args.node_hidden_dim_scalar)
|
| 281 |
+
|
| 282 |
+
def forward(self, coords, coord_mask, padding_mask, confidence):
|
| 283 |
+
with torch.no_grad():
|
| 284 |
+
node_features = self.get_node_features(coords, coord_mask)
|
| 285 |
+
edge_features, edge_index = self.get_edge_features(
|
| 286 |
+
coords, coord_mask, padding_mask)
|
| 287 |
+
node_embeddings_scalar, node_embeddings_vector = self.embed_node(node_features)
|
| 288 |
+
edge_embeddings = self.embed_edge(edge_features)
|
| 289 |
+
|
| 290 |
+
rbf_rep = rbf(confidence, 0., 1.)
|
| 291 |
+
node_embeddings = (
|
| 292 |
+
node_embeddings_scalar + self.embed_confidence(rbf_rep),
|
| 293 |
+
node_embeddings_vector
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
node_embeddings, edge_embeddings, edge_index = flatten_graph(
|
| 297 |
+
node_embeddings, edge_embeddings, edge_index)
|
| 298 |
+
return node_embeddings, edge_embeddings, edge_index
|
| 299 |
+
|
| 300 |
+
def get_edge_features(self, coords, coord_mask, padding_mask):
|
| 301 |
+
X_ca = coords[:, :, 1]
|
| 302 |
+
# Get distances to the top k neighbors
|
| 303 |
+
E_dist, E_idx, E_coord_mask, E_residue_mask = GVPInputFeaturizer._dist(
|
| 304 |
+
X_ca, coord_mask, padding_mask, self.top_k_neighbors)
|
| 305 |
+
# Flatten the graph to be batch size 1 for torch_geometric package
|
| 306 |
+
dest = E_idx
|
| 307 |
+
B, L, k = E_idx.shape[:3]
|
| 308 |
+
src = torch.arange(L, device=E_idx.device).view([1, L, 1]).expand(B, L, k)
|
| 309 |
+
# After flattening, [2, B, E]
|
| 310 |
+
edge_index = torch.stack([src, dest], dim=0).flatten(2, 3)
|
| 311 |
+
# After flattening, [B, E]
|
| 312 |
+
E_dist = E_dist.flatten(1, 2)
|
| 313 |
+
E_coord_mask = E_coord_mask.flatten(1, 2).unsqueeze(-1)
|
| 314 |
+
E_residue_mask = E_residue_mask.flatten(1, 2)
|
| 315 |
+
# Calculate relative positional embeddings and distance RBF
|
| 316 |
+
pos_embeddings = GVPInputFeaturizer._positional_embeddings(
|
| 317 |
+
edge_index,
|
| 318 |
+
num_positional_embeddings=self.num_positional_embeddings,
|
| 319 |
+
)
|
| 320 |
+
D_rbf = rbf(E_dist, 0., 20.)
|
| 321 |
+
# Calculate relative orientation
|
| 322 |
+
X_src = X_ca.unsqueeze(2).expand(-1, -1, k, -1).flatten(1, 2)
|
| 323 |
+
X_dest = torch.gather(
|
| 324 |
+
X_ca,
|
| 325 |
+
1,
|
| 326 |
+
edge_index[1, :, :].unsqueeze(-1).expand([B, L*k, 3])
|
| 327 |
+
)
|
| 328 |
+
coord_mask_src = coord_mask.unsqueeze(2).expand(-1, -1, k).flatten(1, 2)
|
| 329 |
+
coord_mask_dest = torch.gather(
|
| 330 |
+
coord_mask,
|
| 331 |
+
1,
|
| 332 |
+
edge_index[1, :, :].expand([B, L*k])
|
| 333 |
+
)
|
| 334 |
+
E_vectors = X_src - X_dest
|
| 335 |
+
# For the ones without coordinates, substitute in the average vector
|
| 336 |
+
E_vector_mean = torch.sum(E_vectors * E_coord_mask, dim=1,
|
| 337 |
+
keepdims=True) / torch.sum(E_coord_mask, dim=1, keepdims=True)
|
| 338 |
+
E_vectors = E_vectors * E_coord_mask + E_vector_mean * ~(E_coord_mask)
|
| 339 |
+
# Normalize and remove nans
|
| 340 |
+
edge_s = torch.cat([D_rbf, pos_embeddings], dim=-1)
|
| 341 |
+
edge_v = normalize(E_vectors).unsqueeze(-2)
|
| 342 |
+
edge_s, edge_v = map(nan_to_num, (edge_s, edge_v))
|
| 343 |
+
# Also add indications of whether the coordinates are present
|
| 344 |
+
edge_s = torch.cat([
|
| 345 |
+
edge_s,
|
| 346 |
+
(~coord_mask_src).float().unsqueeze(-1),
|
| 347 |
+
(~coord_mask_dest).float().unsqueeze(-1),
|
| 348 |
+
], dim=-1)
|
| 349 |
+
edge_index[:, ~E_residue_mask] = -1
|
| 350 |
+
if self.remove_edges_without_coords:
|
| 351 |
+
edge_index[:, ~E_coord_mask.squeeze(-1)] = -1
|
| 352 |
+
return (edge_s, edge_v), edge_index.transpose(0, 1)
|
model/esm/inverse_folding/gvp_encoder.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
from argparse import Namespace
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
import torch.nn as nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
|
| 12 |
+
from .features import GVPGraphEmbedding
|
| 13 |
+
from .gvp_modules import GVPConvLayer, LayerNorm
|
| 14 |
+
from .gvp_utils import unflatten_graph
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class GVPEncoder(nn.Module):
|
| 19 |
+
|
| 20 |
+
def __init__(self, args):
|
| 21 |
+
super().__init__()
|
| 22 |
+
self.args = args
|
| 23 |
+
self.embed_graph = GVPGraphEmbedding(args)
|
| 24 |
+
|
| 25 |
+
node_hidden_dim = (args.node_hidden_dim_scalar,
|
| 26 |
+
args.node_hidden_dim_vector)
|
| 27 |
+
edge_hidden_dim = (args.edge_hidden_dim_scalar,
|
| 28 |
+
args.edge_hidden_dim_vector)
|
| 29 |
+
|
| 30 |
+
conv_activations = (F.relu, torch.sigmoid)
|
| 31 |
+
self.encoder_layers = nn.ModuleList(
|
| 32 |
+
GVPConvLayer(
|
| 33 |
+
node_hidden_dim,
|
| 34 |
+
edge_hidden_dim,
|
| 35 |
+
drop_rate=args.dropout,
|
| 36 |
+
vector_gate=True,
|
| 37 |
+
attention_heads=0,
|
| 38 |
+
n_message=3,
|
| 39 |
+
conv_activations=conv_activations,
|
| 40 |
+
n_edge_gvps=0,
|
| 41 |
+
eps=1e-4,
|
| 42 |
+
layernorm=True,
|
| 43 |
+
)
|
| 44 |
+
for i in range(args.num_encoder_layers)
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
def forward(self, coords, coord_mask, padding_mask, confidence):
|
| 48 |
+
node_embeddings, edge_embeddings, edge_index = self.embed_graph(
|
| 49 |
+
coords, coord_mask, padding_mask, confidence)
|
| 50 |
+
|
| 51 |
+
for i, layer in enumerate(self.encoder_layers):
|
| 52 |
+
node_embeddings, edge_embeddings = layer(node_embeddings,
|
| 53 |
+
edge_index, edge_embeddings)
|
| 54 |
+
|
| 55 |
+
node_embeddings = unflatten_graph(node_embeddings, coords.shape[0])
|
| 56 |
+
return node_embeddings
|
model/esm/inverse_folding/gvp_transformer_encoder.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# Contents of this file were adapted from the open source fairseq repository.
|
| 4 |
+
#
|
| 5 |
+
# This source code is licensed under the MIT license found in the
|
| 6 |
+
# LICENSE file in the root directory of this source tree.
|
| 7 |
+
|
| 8 |
+
import argparse
|
| 9 |
+
import math
|
| 10 |
+
from typing import Dict, List, Optional
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
from torch import Tensor
|
| 15 |
+
|
| 16 |
+
from onescience.modules.esm import SinusoidalPositionalEmbedding
|
| 17 |
+
from .features import GVPInputFeaturizer, DihedralFeatures
|
| 18 |
+
from .gvp_encoder import GVPEncoder
|
| 19 |
+
from .transformer_layer import TransformerEncoderLayer
|
| 20 |
+
from .util import nan_to_num, get_rotation_frames, rotate, rbf
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class GVPTransformerEncoder(nn.Module):
|
| 24 |
+
"""
|
| 25 |
+
Transformer encoder consisting of *args.encoder.layers* layers. Each layer
|
| 26 |
+
is a :class:`TransformerEncoderLayer`.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 30 |
+
dictionary (~fairseq.data.Dictionary): encoding dictionary
|
| 31 |
+
embed_tokens (torch.nn.Embedding): input embedding
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
def __init__(self, args, dictionary, embed_tokens):
|
| 35 |
+
super().__init__()
|
| 36 |
+
self.args = args
|
| 37 |
+
self.dictionary = dictionary
|
| 38 |
+
|
| 39 |
+
self.dropout_module = nn.Dropout(args.dropout)
|
| 40 |
+
|
| 41 |
+
embed_dim = embed_tokens.embedding_dim
|
| 42 |
+
self.padding_idx = embed_tokens.padding_idx
|
| 43 |
+
|
| 44 |
+
self.embed_tokens = embed_tokens
|
| 45 |
+
self.embed_scale = math.sqrt(embed_dim)
|
| 46 |
+
self.embed_positions = SinusoidalPositionalEmbedding(
|
| 47 |
+
embed_dim,
|
| 48 |
+
self.padding_idx,
|
| 49 |
+
)
|
| 50 |
+
self.embed_gvp_input_features = nn.Linear(15, embed_dim)
|
| 51 |
+
self.embed_confidence = nn.Linear(16, embed_dim)
|
| 52 |
+
self.embed_dihedrals = DihedralFeatures(embed_dim)
|
| 53 |
+
|
| 54 |
+
gvp_args = argparse.Namespace()
|
| 55 |
+
for k, v in vars(args).items():
|
| 56 |
+
if k.startswith("gvp_"):
|
| 57 |
+
setattr(gvp_args, k[4:], v)
|
| 58 |
+
self.gvp_encoder = GVPEncoder(gvp_args)
|
| 59 |
+
gvp_out_dim = gvp_args.node_hidden_dim_scalar + (3 *
|
| 60 |
+
gvp_args.node_hidden_dim_vector)
|
| 61 |
+
self.embed_gvp_output = nn.Linear(gvp_out_dim, embed_dim)
|
| 62 |
+
|
| 63 |
+
self.layers = nn.ModuleList([])
|
| 64 |
+
self.layers.extend(
|
| 65 |
+
[self.build_encoder_layer(args) for i in range(args.encoder_layers)]
|
| 66 |
+
)
|
| 67 |
+
self.num_layers = len(self.layers)
|
| 68 |
+
self.layer_norm = nn.LayerNorm(embed_dim)
|
| 69 |
+
|
| 70 |
+
def build_encoder_layer(self, args):
|
| 71 |
+
return TransformerEncoderLayer(args)
|
| 72 |
+
|
| 73 |
+
def forward_embedding(self, coords, padding_mask, confidence):
|
| 74 |
+
"""
|
| 75 |
+
Args:
|
| 76 |
+
coords: N, CA, C backbone coordinates in shape length x 3 (atoms) x 3
|
| 77 |
+
padding_mask: boolean Tensor (true for padding) of shape length
|
| 78 |
+
confidence: confidence scores between 0 and 1 of shape length
|
| 79 |
+
"""
|
| 80 |
+
components = dict()
|
| 81 |
+
coord_mask = torch.all(torch.all(torch.isfinite(coords), dim=-1), dim=-1)
|
| 82 |
+
coords = nan_to_num(coords)
|
| 83 |
+
mask_tokens = (
|
| 84 |
+
padding_mask * self.dictionary.padding_idx +
|
| 85 |
+
~padding_mask * self.dictionary.get_idx("<mask>")
|
| 86 |
+
)
|
| 87 |
+
components["tokens"] = self.embed_tokens(mask_tokens) * self.embed_scale
|
| 88 |
+
components["diherals"] = self.embed_dihedrals(coords)
|
| 89 |
+
|
| 90 |
+
# GVP encoder
|
| 91 |
+
gvp_out_scalars, gvp_out_vectors = self.gvp_encoder(coords,
|
| 92 |
+
coord_mask, padding_mask, confidence)
|
| 93 |
+
R = get_rotation_frames(coords)
|
| 94 |
+
# Rotate to local rotation frame for rotation-invariance
|
| 95 |
+
gvp_out_features = torch.cat([
|
| 96 |
+
gvp_out_scalars,
|
| 97 |
+
rotate(gvp_out_vectors, R.transpose(-2, -1)).flatten(-2, -1),
|
| 98 |
+
], dim=-1)
|
| 99 |
+
components["gvp_out"] = self.embed_gvp_output(gvp_out_features)
|
| 100 |
+
|
| 101 |
+
components["confidence"] = self.embed_confidence(
|
| 102 |
+
rbf(confidence, 0., 1.))
|
| 103 |
+
|
| 104 |
+
# In addition to GVP encoder outputs, also directly embed GVP input node
|
| 105 |
+
# features to the Transformer
|
| 106 |
+
scalar_features, vector_features = GVPInputFeaturizer.get_node_features(
|
| 107 |
+
coords, coord_mask, with_coord_mask=False)
|
| 108 |
+
features = torch.cat([
|
| 109 |
+
scalar_features,
|
| 110 |
+
rotate(vector_features, R.transpose(-2, -1)).flatten(-2, -1),
|
| 111 |
+
], dim=-1)
|
| 112 |
+
components["gvp_input_features"] = self.embed_gvp_input_features(features)
|
| 113 |
+
|
| 114 |
+
embed = sum(components.values())
|
| 115 |
+
# for k, v in components.items():
|
| 116 |
+
# print(k, torch.mean(v, dim=(0,1)), torch.std(v, dim=(0,1)))
|
| 117 |
+
|
| 118 |
+
x = embed
|
| 119 |
+
x = x + self.embed_positions(mask_tokens)
|
| 120 |
+
x = self.dropout_module(x)
|
| 121 |
+
return x, components
|
| 122 |
+
|
| 123 |
+
def forward(
|
| 124 |
+
self,
|
| 125 |
+
coords,
|
| 126 |
+
encoder_padding_mask,
|
| 127 |
+
confidence,
|
| 128 |
+
return_all_hiddens: bool = False,
|
| 129 |
+
):
|
| 130 |
+
"""
|
| 131 |
+
Args:
|
| 132 |
+
coords (Tensor): backbone coordinates
|
| 133 |
+
shape batch_size x num_residues x num_atoms (3 for N, CA, C) x 3
|
| 134 |
+
encoder_padding_mask (ByteTensor): the positions of
|
| 135 |
+
padding elements of shape `(batch_size x num_residues)`
|
| 136 |
+
confidence (Tensor): the confidence score of shape (batch_size x
|
| 137 |
+
num_residues). The value is between 0. and 1. for each residue
|
| 138 |
+
coordinate, or -1. if no coordinate is given
|
| 139 |
+
return_all_hiddens (bool, optional): also return all of the
|
| 140 |
+
intermediate hidden states (default: False).
|
| 141 |
+
|
| 142 |
+
Returns:
|
| 143 |
+
dict:
|
| 144 |
+
- **encoder_out** (Tensor): the last encoder layer's output of
|
| 145 |
+
shape `(num_residues, batch_size, embed_dim)`
|
| 146 |
+
- **encoder_padding_mask** (ByteTensor): the positions of
|
| 147 |
+
padding elements of shape `(batch_size, num_residues)`
|
| 148 |
+
- **encoder_embedding** (Tensor): the (scaled) embedding lookup
|
| 149 |
+
of shape `(batch_size, num_residues, embed_dim)`
|
| 150 |
+
- **encoder_states** (List[Tensor]): all intermediate
|
| 151 |
+
hidden states of shape `(num_residues, batch_size, embed_dim)`.
|
| 152 |
+
Only populated if *return_all_hiddens* is True.
|
| 153 |
+
"""
|
| 154 |
+
x, encoder_embedding = self.forward_embedding(coords,
|
| 155 |
+
encoder_padding_mask, confidence)
|
| 156 |
+
# account for padding while computing the representation
|
| 157 |
+
x = x * (1 - encoder_padding_mask.unsqueeze(-1).type_as(x))
|
| 158 |
+
|
| 159 |
+
# B x T x C -> T x B x C
|
| 160 |
+
x = x.transpose(0, 1)
|
| 161 |
+
|
| 162 |
+
encoder_states = []
|
| 163 |
+
|
| 164 |
+
if return_all_hiddens:
|
| 165 |
+
encoder_states.append(x)
|
| 166 |
+
|
| 167 |
+
# encoder layers
|
| 168 |
+
for layer in self.layers:
|
| 169 |
+
x = layer(
|
| 170 |
+
x, encoder_padding_mask=encoder_padding_mask
|
| 171 |
+
)
|
| 172 |
+
if return_all_hiddens:
|
| 173 |
+
assert encoder_states is not None
|
| 174 |
+
encoder_states.append(x)
|
| 175 |
+
|
| 176 |
+
if self.layer_norm is not None:
|
| 177 |
+
x = self.layer_norm(x)
|
| 178 |
+
|
| 179 |
+
return {
|
| 180 |
+
"encoder_out": [x], # T x B x C
|
| 181 |
+
"encoder_padding_mask": [encoder_padding_mask], # B x T
|
| 182 |
+
"encoder_embedding": [encoder_embedding], # dictionary
|
| 183 |
+
"encoder_states": encoder_states, # List[T x B x C]
|
| 184 |
+
}
|
model/esm/inverse_folding/gvp_utils.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def flatten_graph(node_embeddings, edge_embeddings, edge_index):
|
| 10 |
+
"""
|
| 11 |
+
Flattens the graph into a batch size one (with disconnected subgraphs for
|
| 12 |
+
each example) to be compatible with pytorch-geometric package.
|
| 13 |
+
Args:
|
| 14 |
+
node_embeddings: node embeddings in tuple form (scalar, vector)
|
| 15 |
+
- scalar: shape batch size x nodes x node_embed_dim
|
| 16 |
+
- vector: shape batch size x nodes x node_embed_dim x 3
|
| 17 |
+
edge_embeddings: edge embeddings of in tuple form (scalar, vector)
|
| 18 |
+
- scalar: shape batch size x edges x edge_embed_dim
|
| 19 |
+
- vector: shape batch size x edges x edge_embed_dim x 3
|
| 20 |
+
edge_index: shape batch_size x 2 (source node and target node) x edges
|
| 21 |
+
Returns:
|
| 22 |
+
node_embeddings: node embeddings in tuple form (scalar, vector)
|
| 23 |
+
- scalar: shape batch total_nodes x node_embed_dim
|
| 24 |
+
- vector: shape batch total_nodes x node_embed_dim x 3
|
| 25 |
+
edge_embeddings: edge embeddings of in tuple form (scalar, vector)
|
| 26 |
+
- scalar: shape batch total_edges x edge_embed_dim
|
| 27 |
+
- vector: shape batch total_edges x edge_embed_dim x 3
|
| 28 |
+
edge_index: shape 2 x total_edges
|
| 29 |
+
"""
|
| 30 |
+
x_s, x_v = node_embeddings
|
| 31 |
+
e_s, e_v = edge_embeddings
|
| 32 |
+
batch_size, N = x_s.shape[0], x_s.shape[1]
|
| 33 |
+
node_embeddings = (torch.flatten(x_s, 0, 1), torch.flatten(x_v, 0, 1))
|
| 34 |
+
edge_embeddings = (torch.flatten(e_s, 0, 1), torch.flatten(e_v, 0, 1))
|
| 35 |
+
|
| 36 |
+
edge_mask = torch.any(edge_index != -1, dim=1)
|
| 37 |
+
# Re-number the nodes by adding batch_idx * N to each batch
|
| 38 |
+
edge_index = edge_index + (torch.arange(batch_size, device=edge_index.device) *
|
| 39 |
+
N).unsqueeze(-1).unsqueeze(-1)
|
| 40 |
+
edge_index = edge_index.permute(1, 0, 2).flatten(1, 2)
|
| 41 |
+
edge_mask = edge_mask.flatten()
|
| 42 |
+
edge_index = edge_index[:, edge_mask]
|
| 43 |
+
edge_embeddings = (
|
| 44 |
+
edge_embeddings[0][edge_mask, :],
|
| 45 |
+
edge_embeddings[1][edge_mask, :]
|
| 46 |
+
)
|
| 47 |
+
return node_embeddings, edge_embeddings, edge_index
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def unflatten_graph(node_embeddings, batch_size):
|
| 51 |
+
"""
|
| 52 |
+
Unflattens node embeddings.
|
| 53 |
+
Args:
|
| 54 |
+
node_embeddings: node embeddings in tuple form (scalar, vector)
|
| 55 |
+
- scalar: shape batch total_nodes x node_embed_dim
|
| 56 |
+
- vector: shape batch total_nodes x node_embed_dim x 3
|
| 57 |
+
batch_size: int
|
| 58 |
+
Returns:
|
| 59 |
+
node_embeddings: node embeddings in tuple form (scalar, vector)
|
| 60 |
+
- scalar: shape batch size x nodes x node_embed_dim
|
| 61 |
+
- vector: shape batch size x nodes x node_embed_dim x 3
|
| 62 |
+
"""
|
| 63 |
+
x_s, x_v = node_embeddings
|
| 64 |
+
x_s = x_s.reshape(batch_size, -1, x_s.shape[1])
|
| 65 |
+
x_v = x_v.reshape(batch_size, -1, x_v.shape[1], x_v.shape[2])
|
| 66 |
+
return (x_s, x_v)
|
| 67 |
+
|
| 68 |
+
|
model/esm/inverse_folding/multichain_util.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import biotite.structure
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
from typing import Sequence, Tuple, List
|
| 10 |
+
|
| 11 |
+
from model.esm.inverse_folding.util import (
|
| 12 |
+
load_structure,
|
| 13 |
+
extract_coords_from_structure,
|
| 14 |
+
load_coords,
|
| 15 |
+
get_sequence_loss,
|
| 16 |
+
get_encoder_output,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def extract_coords_from_complex(structure: biotite.structure.AtomArray):
|
| 21 |
+
"""
|
| 22 |
+
Args:
|
| 23 |
+
structure: biotite AtomArray
|
| 24 |
+
Returns:
|
| 25 |
+
Tuple (coords_list, seq_list)
|
| 26 |
+
- coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
|
| 27 |
+
coordinates representing the backbone of each chain
|
| 28 |
+
- seqs: Dictionary mapping chain ids to native sequences of each chain
|
| 29 |
+
"""
|
| 30 |
+
coords = {}
|
| 31 |
+
seqs = {}
|
| 32 |
+
all_chains = biotite.structure.get_chains(structure)
|
| 33 |
+
for chain_id in all_chains:
|
| 34 |
+
chain = structure[structure.chain_id == chain_id]
|
| 35 |
+
coords[chain_id], seqs[chain_id] = extract_coords_from_structure(chain)
|
| 36 |
+
return coords, seqs
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def load_complex_coords(fpath, chains):
|
| 40 |
+
"""
|
| 41 |
+
Args:
|
| 42 |
+
fpath: filepath to either pdb or cif file
|
| 43 |
+
chains: the chain ids (the order matters for autoregressive model)
|
| 44 |
+
Returns:
|
| 45 |
+
Tuple (coords_list, seq_list)
|
| 46 |
+
- coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
|
| 47 |
+
coordinates representing the backbone of each chain
|
| 48 |
+
- seqs: Dictionary mapping chain ids to native sequences of each chain
|
| 49 |
+
"""
|
| 50 |
+
structure = load_structure(fpath, chains)
|
| 51 |
+
return extract_coords_from_complex(structure)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _concatenate_coords(coords, target_chain_id, padding_length=10):
|
| 55 |
+
"""
|
| 56 |
+
Args:
|
| 57 |
+
coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
|
| 58 |
+
coordinates representing the backbone of each chain
|
| 59 |
+
target_chain_id: The chain id to sample sequences for
|
| 60 |
+
padding_length: Length of padding between concatenated chains
|
| 61 |
+
Returns:
|
| 62 |
+
Tuple (coords, seq)
|
| 63 |
+
- coords is an L x 3 x 3 array for N, CA, C coordinates, a
|
| 64 |
+
concatenation of the chains with padding in between
|
| 65 |
+
- seq is the extracted sequence, with padding tokens inserted
|
| 66 |
+
between the concatenated chains
|
| 67 |
+
"""
|
| 68 |
+
pad_coords = np.full((padding_length, 3, 3), np.nan, dtype=np.float32)
|
| 69 |
+
# For best performance, put the target chain first in concatenation.
|
| 70 |
+
coords_list = [coords[target_chain_id]]
|
| 71 |
+
for chain_id in coords:
|
| 72 |
+
if chain_id == target_chain_id:
|
| 73 |
+
continue
|
| 74 |
+
coords_list.append(pad_coords)
|
| 75 |
+
coords_list.append(coords[chain_id])
|
| 76 |
+
coords_concatenated = np.concatenate(coords_list, axis=0)
|
| 77 |
+
return coords_concatenated
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def sample_sequence_in_complex(model, coords, target_chain_id, temperature=1.,
|
| 81 |
+
padding_length=10):
|
| 82 |
+
"""
|
| 83 |
+
Samples sequence for one chain in a complex.
|
| 84 |
+
Args:
|
| 85 |
+
model: An instance of the GVPTransformer model
|
| 86 |
+
coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
|
| 87 |
+
coordinates representing the backbone of each chain
|
| 88 |
+
target_chain_id: The chain id to sample sequences for
|
| 89 |
+
padding_length: padding length in between chains
|
| 90 |
+
Returns:
|
| 91 |
+
Sampled sequence for the target chain
|
| 92 |
+
"""
|
| 93 |
+
target_chain_len = coords[target_chain_id].shape[0]
|
| 94 |
+
all_coords = _concatenate_coords(coords, target_chain_id)
|
| 95 |
+
device = next(model.parameters()).device
|
| 96 |
+
|
| 97 |
+
# Supply padding tokens for other chains to avoid unused sampling for speed
|
| 98 |
+
padding_pattern = ['<pad>'] * all_coords.shape[0]
|
| 99 |
+
for i in range(target_chain_len):
|
| 100 |
+
padding_pattern[i] = '<mask>'
|
| 101 |
+
sampled = model.sample(all_coords, partial_seq=padding_pattern,
|
| 102 |
+
temperature=temperature, device=device)
|
| 103 |
+
sampled = sampled[:target_chain_len]
|
| 104 |
+
return sampled
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def score_sequence_in_complex(model, alphabet, coords, target_chain_id,
|
| 108 |
+
target_seq, padding_length=10):
|
| 109 |
+
"""
|
| 110 |
+
Scores sequence for one chain in a complex.
|
| 111 |
+
Args:
|
| 112 |
+
model: An instance of the GVPTransformer model
|
| 113 |
+
alphabet: Alphabet for the model
|
| 114 |
+
coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
|
| 115 |
+
coordinates representing the backbone of each chain
|
| 116 |
+
target_chain_id: The chain id to sample sequences for
|
| 117 |
+
target_seq: Target sequence for the target chain for scoring.
|
| 118 |
+
padding_length: padding length in between chains
|
| 119 |
+
Returns:
|
| 120 |
+
Tuple (ll_fullseq, ll_withcoord)
|
| 121 |
+
- ll_fullseq: Average log-likelihood over the full target chain
|
| 122 |
+
- ll_withcoord: Average log-likelihood in target chain excluding those
|
| 123 |
+
residues without coordinates
|
| 124 |
+
"""
|
| 125 |
+
all_coords = _concatenate_coords(coords, target_chain_id)
|
| 126 |
+
|
| 127 |
+
loss, target_padding_mask = get_sequence_loss(model, alphabet, all_coords,
|
| 128 |
+
target_seq)
|
| 129 |
+
ll_fullseq = -np.sum(loss * ~target_padding_mask) / np.sum(
|
| 130 |
+
~target_padding_mask)
|
| 131 |
+
|
| 132 |
+
# Also calculate average when excluding masked portions
|
| 133 |
+
coord_mask = np.all(np.isfinite(coords[target_chain_id]), axis=(-1, -2))
|
| 134 |
+
ll_withcoord = -np.sum(loss * coord_mask) / np.sum(coord_mask)
|
| 135 |
+
return ll_fullseq, ll_withcoord
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def get_encoder_output_for_complex(model, alphabet, coords, target_chain_id):
|
| 139 |
+
"""
|
| 140 |
+
Args:
|
| 141 |
+
model: An instance of the GVPTransformer model
|
| 142 |
+
alphabet: Alphabet for the model
|
| 143 |
+
coords: Dictionary mapping chain ids to L x 3 x 3 array for N, CA, C
|
| 144 |
+
coordinates representing the backbone of each chain
|
| 145 |
+
target_chain_id: The chain id to sample sequences for
|
| 146 |
+
Returns:
|
| 147 |
+
Dictionary mapping chain id to encoder output for each chain
|
| 148 |
+
"""
|
| 149 |
+
all_coords = _concatenate_coords(coords, target_chain_id)
|
| 150 |
+
all_rep = get_encoder_output(model, alphabet, all_coords)
|
| 151 |
+
target_chain_len = coords[target_chain_id].shape[0]
|
| 152 |
+
return all_rep[:target_chain_len]
|
model/esm/inverse_folding/transformer_decoder.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# Contents of this file were adapted from the open source fairseq repository.
|
| 4 |
+
#
|
| 5 |
+
# This source code is licensed under the MIT license found in the
|
| 6 |
+
# LICENSE file in the root directory of this source tree.
|
| 7 |
+
|
| 8 |
+
import math
|
| 9 |
+
from typing import Any, Dict, List, Optional
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
from torch import Tensor
|
| 14 |
+
|
| 15 |
+
from onescience.modules.esm import SinusoidalPositionalEmbedding
|
| 16 |
+
from .transformer_layer import TransformerDecoderLayer
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def fill_with_neg_inf(t):
|
| 20 |
+
"""FP16-compatible function that fills a tensor with -inf."""
|
| 21 |
+
return t.float().fill_(float("-inf")).type_as(t)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class TransformerDecoder(nn.Module):
|
| 25 |
+
"""
|
| 26 |
+
Transformer decoder consisting of *args.decoder.layers* layers. Each layer
|
| 27 |
+
is a :class:`TransformerDecoderLayer`.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 31 |
+
dictionary (~fairseq.data.Dictionary): decoding dictionary
|
| 32 |
+
embed_tokens (torch.nn.Embedding): output embedding
|
| 33 |
+
no_encoder_attn (bool, optional): whether to attend to encoder outputs
|
| 34 |
+
(default: False).
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __init__(
|
| 38 |
+
self,
|
| 39 |
+
args,
|
| 40 |
+
dictionary,
|
| 41 |
+
embed_tokens,
|
| 42 |
+
):
|
| 43 |
+
super().__init__()
|
| 44 |
+
self.args = args
|
| 45 |
+
self.dictionary = dictionary
|
| 46 |
+
self._future_mask = torch.empty(0)
|
| 47 |
+
|
| 48 |
+
self.dropout_module = nn.Dropout(args.dropout)
|
| 49 |
+
|
| 50 |
+
input_embed_dim = embed_tokens.embedding_dim
|
| 51 |
+
embed_dim = args.decoder_embed_dim
|
| 52 |
+
self.embed_dim = embed_dim
|
| 53 |
+
|
| 54 |
+
self.padding_idx = embed_tokens.padding_idx
|
| 55 |
+
|
| 56 |
+
self.embed_tokens = embed_tokens
|
| 57 |
+
self.embed_scale = math.sqrt(embed_dim)
|
| 58 |
+
|
| 59 |
+
self.project_in_dim = (
|
| 60 |
+
nn.Linear(input_embed_dim, embed_dim, bias=False)
|
| 61 |
+
if embed_dim != input_embed_dim
|
| 62 |
+
else None
|
| 63 |
+
)
|
| 64 |
+
self.embed_positions = SinusoidalPositionalEmbedding(
|
| 65 |
+
embed_dim,
|
| 66 |
+
self.padding_idx,
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
self.layers = nn.ModuleList([])
|
| 70 |
+
self.layers.extend(
|
| 71 |
+
[
|
| 72 |
+
self.build_decoder_layer(args)
|
| 73 |
+
for _ in range(args.decoder_layers)
|
| 74 |
+
]
|
| 75 |
+
)
|
| 76 |
+
self.num_layers = len(self.layers)
|
| 77 |
+
self.layer_norm = nn.LayerNorm(embed_dim)
|
| 78 |
+
|
| 79 |
+
self.build_output_projection(args, dictionary)
|
| 80 |
+
|
| 81 |
+
def build_output_projection(self, args, dictionary):
|
| 82 |
+
self.output_projection = nn.Linear(
|
| 83 |
+
args.decoder_embed_dim, len(dictionary), bias=False
|
| 84 |
+
)
|
| 85 |
+
nn.init.normal_(
|
| 86 |
+
self.output_projection.weight, mean=0, std=args.decoder_embed_dim ** -0.5
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
def build_decoder_layer(self, args):
|
| 90 |
+
return TransformerDecoderLayer(args)
|
| 91 |
+
|
| 92 |
+
def forward(
|
| 93 |
+
self,
|
| 94 |
+
prev_output_tokens,
|
| 95 |
+
encoder_out: Optional[Dict[str, List[Tensor]]] = None,
|
| 96 |
+
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
| 97 |
+
features_only: bool = False,
|
| 98 |
+
return_all_hiddens: bool = False,
|
| 99 |
+
):
|
| 100 |
+
"""
|
| 101 |
+
Args:
|
| 102 |
+
prev_output_tokens (LongTensor): previous decoder outputs of shape
|
| 103 |
+
`(batch, tgt_len)`, for teacher forcing
|
| 104 |
+
encoder_out (optional): output from the encoder, used for
|
| 105 |
+
encoder-side attention, should be of size T x B x C
|
| 106 |
+
incremental_state (dict): dictionary used for storing state during
|
| 107 |
+
:ref:`Incremental decoding`
|
| 108 |
+
features_only (bool, optional): only return features without
|
| 109 |
+
applying output layer (default: False).
|
| 110 |
+
|
| 111 |
+
Returns:
|
| 112 |
+
tuple:
|
| 113 |
+
- the decoder's output of shape `(batch, tgt_len, vocab)`
|
| 114 |
+
- a dictionary with any model-specific outputs
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
x, extra = self.extract_features(
|
| 118 |
+
prev_output_tokens,
|
| 119 |
+
encoder_out=encoder_out,
|
| 120 |
+
incremental_state=incremental_state,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
if not features_only:
|
| 124 |
+
x = self.output_layer(x)
|
| 125 |
+
x = x.transpose(1, 2) # B x T x C -> B x C x T
|
| 126 |
+
return x, extra
|
| 127 |
+
|
| 128 |
+
def extract_features(
|
| 129 |
+
self,
|
| 130 |
+
prev_output_tokens,
|
| 131 |
+
encoder_out: Optional[Dict[str, List[Tensor]]],
|
| 132 |
+
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
| 133 |
+
):
|
| 134 |
+
"""
|
| 135 |
+
Similar to *forward* but only return features.
|
| 136 |
+
|
| 137 |
+
Includes several features from "Jointly Learning to Align and
|
| 138 |
+
Translate with Transformer Models" (Garg et al., EMNLP 2019).
|
| 139 |
+
|
| 140 |
+
Returns:
|
| 141 |
+
tuple:
|
| 142 |
+
- the decoder's features of shape `(batch, tgt_len, embed_dim)`
|
| 143 |
+
- a dictionary with any model-specific outputs
|
| 144 |
+
"""
|
| 145 |
+
bs, slen = prev_output_tokens.size()
|
| 146 |
+
|
| 147 |
+
enc: Optional[Tensor] = None
|
| 148 |
+
padding_mask: Optional[Tensor] = None
|
| 149 |
+
if encoder_out is not None and len(encoder_out["encoder_out"]) > 0:
|
| 150 |
+
enc = encoder_out["encoder_out"][0]
|
| 151 |
+
assert (
|
| 152 |
+
enc.size()[1] == bs
|
| 153 |
+
), f"Expected enc.shape == (t, {bs}, c) got {enc.shape}"
|
| 154 |
+
if encoder_out is not None and len(encoder_out["encoder_padding_mask"]) > 0:
|
| 155 |
+
padding_mask = encoder_out["encoder_padding_mask"][0]
|
| 156 |
+
|
| 157 |
+
# embed positions
|
| 158 |
+
positions = self.embed_positions(
|
| 159 |
+
prev_output_tokens
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
if incremental_state is not None:
|
| 163 |
+
prev_output_tokens = prev_output_tokens[:, -1:]
|
| 164 |
+
positions = positions[:, -1:]
|
| 165 |
+
|
| 166 |
+
# embed tokens and positions
|
| 167 |
+
x = self.embed_scale * self.embed_tokens(prev_output_tokens)
|
| 168 |
+
|
| 169 |
+
if self.project_in_dim is not None:
|
| 170 |
+
x = self.project_in_dim(x)
|
| 171 |
+
|
| 172 |
+
x += positions
|
| 173 |
+
|
| 174 |
+
x = self.dropout_module(x)
|
| 175 |
+
|
| 176 |
+
# B x T x C -> T x B x C
|
| 177 |
+
x = x.transpose(0, 1)
|
| 178 |
+
|
| 179 |
+
self_attn_padding_mask: Optional[Tensor] = None
|
| 180 |
+
if prev_output_tokens.eq(self.padding_idx).any():
|
| 181 |
+
self_attn_padding_mask = prev_output_tokens.eq(self.padding_idx)
|
| 182 |
+
|
| 183 |
+
# decoder layers
|
| 184 |
+
attn: Optional[Tensor] = None
|
| 185 |
+
inner_states: List[Optional[Tensor]] = [x]
|
| 186 |
+
for idx, layer in enumerate(self.layers):
|
| 187 |
+
if incremental_state is None:
|
| 188 |
+
self_attn_mask = self.buffered_future_mask(x)
|
| 189 |
+
else:
|
| 190 |
+
self_attn_mask = None
|
| 191 |
+
|
| 192 |
+
x, layer_attn, _ = layer(
|
| 193 |
+
x,
|
| 194 |
+
enc,
|
| 195 |
+
padding_mask,
|
| 196 |
+
incremental_state,
|
| 197 |
+
self_attn_mask=self_attn_mask,
|
| 198 |
+
self_attn_padding_mask=self_attn_padding_mask,
|
| 199 |
+
need_attn=False,
|
| 200 |
+
need_head_weights=False,
|
| 201 |
+
)
|
| 202 |
+
inner_states.append(x)
|
| 203 |
+
|
| 204 |
+
if self.layer_norm is not None:
|
| 205 |
+
x = self.layer_norm(x)
|
| 206 |
+
|
| 207 |
+
# T x B x C -> B x C x T
|
| 208 |
+
x = x.transpose(0, 1)
|
| 209 |
+
|
| 210 |
+
return x, {"inner_states": inner_states}
|
| 211 |
+
|
| 212 |
+
def output_layer(self, features):
|
| 213 |
+
"""Project features to the vocabulary size."""
|
| 214 |
+
return self.output_projection(features)
|
| 215 |
+
|
| 216 |
+
def buffered_future_mask(self, tensor):
|
| 217 |
+
dim = tensor.size(0)
|
| 218 |
+
# self._future_mask.device != tensor.device is not working in TorchScript. This is a workaround.
|
| 219 |
+
if (
|
| 220 |
+
self._future_mask.size(0) == 0
|
| 221 |
+
or (not self._future_mask.device == tensor.device)
|
| 222 |
+
or self._future_mask.size(0) < dim
|
| 223 |
+
):
|
| 224 |
+
self._future_mask = torch.triu(
|
| 225 |
+
fill_with_neg_inf(torch.zeros([dim, dim])), 1
|
| 226 |
+
)
|
| 227 |
+
self._future_mask = self._future_mask.to(tensor)
|
| 228 |
+
return self._future_mask[:dim, :dim]
|
model/esm/inverse_folding/transformer_layer.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# Contents of this file were adapted from the open source fairseq repository.
|
| 4 |
+
#
|
| 5 |
+
# This source code is licensed under the MIT license found in the
|
| 6 |
+
# LICENSE file in the root directory of this source tree.
|
| 7 |
+
|
| 8 |
+
from typing import Dict, List, Optional
|
| 9 |
+
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
import torch.nn.functional as F
|
| 13 |
+
from onescience.modules.attention import MultiheadAttention
|
| 14 |
+
from torch import Tensor
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class TransformerEncoderLayer(nn.Module):
|
| 18 |
+
"""Encoder layer block.
|
| 19 |
+
`layernorm -> dropout -> add residual`
|
| 20 |
+
|
| 21 |
+
Args:
|
| 22 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
def __init__(self, args):
|
| 26 |
+
super().__init__()
|
| 27 |
+
self.args = args
|
| 28 |
+
self.embed_dim = args.encoder_embed_dim
|
| 29 |
+
self.self_attn = self.build_self_attention(self.embed_dim, args)
|
| 30 |
+
self.self_attn_layer_norm = torch.nn.LayerNorm(self.embed_dim)
|
| 31 |
+
self.dropout_module = nn.Dropout(args.dropout)
|
| 32 |
+
self.activation_fn = F.relu
|
| 33 |
+
self.fc1 = self.build_fc1(
|
| 34 |
+
self.embed_dim,
|
| 35 |
+
args.encoder_ffn_embed_dim,
|
| 36 |
+
)
|
| 37 |
+
self.fc2 = self.build_fc2(
|
| 38 |
+
args.encoder_ffn_embed_dim,
|
| 39 |
+
self.embed_dim,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
self.final_layer_norm = nn.LayerNorm(self.embed_dim)
|
| 43 |
+
|
| 44 |
+
def build_fc1(self, input_dim, output_dim):
|
| 45 |
+
return nn.Linear(input_dim, output_dim)
|
| 46 |
+
|
| 47 |
+
def build_fc2(self, input_dim, output_dim):
|
| 48 |
+
return nn.Linear(input_dim, output_dim)
|
| 49 |
+
|
| 50 |
+
def build_self_attention(self, embed_dim, args):
|
| 51 |
+
return MultiheadAttention(
|
| 52 |
+
embed_dim,
|
| 53 |
+
args.encoder_attention_heads,
|
| 54 |
+
dropout=args.attention_dropout,
|
| 55 |
+
self_attention=True,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
def residual_connection(self, x, residual):
|
| 59 |
+
return residual + x
|
| 60 |
+
|
| 61 |
+
def forward(
|
| 62 |
+
self,
|
| 63 |
+
x,
|
| 64 |
+
encoder_padding_mask: Optional[Tensor],
|
| 65 |
+
attn_mask: Optional[Tensor] = None,
|
| 66 |
+
):
|
| 67 |
+
"""
|
| 68 |
+
Args:
|
| 69 |
+
x (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)`
|
| 70 |
+
encoder_padding_mask (ByteTensor): binary ByteTensor of shape
|
| 71 |
+
`(batch, seq_len)` where padding elements are indicated by ``1``.
|
| 72 |
+
attn_mask (ByteTensor): binary tensor of shape `(tgt_len, src_len)`,
|
| 73 |
+
where `tgt_len` is the length of output and `src_len` is the
|
| 74 |
+
length of input, though here both are equal to `seq_len`.
|
| 75 |
+
`attn_mask[tgt_i, src_j] = 1` means that when calculating the
|
| 76 |
+
embedding for `tgt_i`, we exclude (mask out) `src_j`. This is
|
| 77 |
+
useful for strided self-attention.
|
| 78 |
+
|
| 79 |
+
Returns:
|
| 80 |
+
encoded output of shape `(seq_len, batch, embed_dim)`
|
| 81 |
+
"""
|
| 82 |
+
# anything in original attn_mask = 1, becomes -1e8
|
| 83 |
+
# anything in original attn_mask = 0, becomes 0
|
| 84 |
+
# Note that we cannot use -inf here, because at some edge cases,
|
| 85 |
+
# the attention weight (before softmax) for some padded element in query
|
| 86 |
+
# will become -inf, which results in NaN in model parameters
|
| 87 |
+
if attn_mask is not None:
|
| 88 |
+
attn_mask = attn_mask.masked_fill(
|
| 89 |
+
attn_mask.to(torch.bool), -1e8 if x.dtype == torch.float32 else -1e4
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
residual = x
|
| 93 |
+
x = self.self_attn_layer_norm(x)
|
| 94 |
+
x, _ = self.self_attn(
|
| 95 |
+
query=x,
|
| 96 |
+
key=x,
|
| 97 |
+
value=x,
|
| 98 |
+
key_padding_mask=encoder_padding_mask,
|
| 99 |
+
need_weights=False,
|
| 100 |
+
attn_mask=attn_mask,
|
| 101 |
+
)
|
| 102 |
+
x = self.dropout_module(x)
|
| 103 |
+
x = self.residual_connection(x, residual)
|
| 104 |
+
|
| 105 |
+
residual = x
|
| 106 |
+
x = self.final_layer_norm(x)
|
| 107 |
+
x = self.activation_fn(self.fc1(x))
|
| 108 |
+
x = self.fc2(x)
|
| 109 |
+
x = self.dropout_module(x)
|
| 110 |
+
x = self.residual_connection(x, residual)
|
| 111 |
+
return x
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
class TransformerDecoderLayer(nn.Module):
|
| 115 |
+
"""Decoder layer block.
|
| 116 |
+
`layernorm -> dropout -> add residual`
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
args (argparse.Namespace): parsed command-line arguments
|
| 120 |
+
no_encoder_attn (bool, optional): whether to attend to encoder outputs
|
| 121 |
+
(default: False).
|
| 122 |
+
"""
|
| 123 |
+
|
| 124 |
+
def __init__(
|
| 125 |
+
self, args, no_encoder_attn=False, add_bias_kv=False, add_zero_attn=False
|
| 126 |
+
):
|
| 127 |
+
super().__init__()
|
| 128 |
+
self.embed_dim = args.decoder_embed_dim
|
| 129 |
+
self.dropout_module = nn.Dropout(args.dropout)
|
| 130 |
+
|
| 131 |
+
self.self_attn = self.build_self_attention(
|
| 132 |
+
self.embed_dim,
|
| 133 |
+
args,
|
| 134 |
+
add_bias_kv=add_bias_kv,
|
| 135 |
+
add_zero_attn=add_zero_attn,
|
| 136 |
+
)
|
| 137 |
+
self.nh = self.self_attn.num_heads
|
| 138 |
+
self.head_dim = self.self_attn.head_dim
|
| 139 |
+
|
| 140 |
+
self.activation_fn = F.relu
|
| 141 |
+
|
| 142 |
+
self.self_attn_layer_norm = nn.LayerNorm(self.embed_dim)
|
| 143 |
+
|
| 144 |
+
if no_encoder_attn:
|
| 145 |
+
self.encoder_attn = None
|
| 146 |
+
self.encoder_attn_layer_norm = None
|
| 147 |
+
else:
|
| 148 |
+
self.encoder_attn = self.build_encoder_attention(self.embed_dim, args)
|
| 149 |
+
self.encoder_attn_layer_norm = nn.LayerNorm(self.embed_dim)
|
| 150 |
+
|
| 151 |
+
self.ffn_layernorm = (
|
| 152 |
+
LayerNorm(args.decoder_ffn_embed_dim)
|
| 153 |
+
if getattr(args, "scale_fc", False)
|
| 154 |
+
else None
|
| 155 |
+
)
|
| 156 |
+
self.w_resid = (
|
| 157 |
+
nn.Parameter(
|
| 158 |
+
torch.ones(
|
| 159 |
+
self.embed_dim,
|
| 160 |
+
),
|
| 161 |
+
requires_grad=True,
|
| 162 |
+
)
|
| 163 |
+
if getattr(args, "scale_resids", False)
|
| 164 |
+
else None
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
self.fc1 = self.build_fc1(
|
| 168 |
+
self.embed_dim,
|
| 169 |
+
args.decoder_ffn_embed_dim,
|
| 170 |
+
)
|
| 171 |
+
self.fc2 = self.build_fc2(
|
| 172 |
+
args.decoder_ffn_embed_dim,
|
| 173 |
+
self.embed_dim,
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
self.final_layer_norm = nn.LayerNorm(self.embed_dim)
|
| 177 |
+
self.need_attn = True
|
| 178 |
+
|
| 179 |
+
def build_fc1(self, input_dim, output_dim):
|
| 180 |
+
return nn.Linear(input_dim, output_dim)
|
| 181 |
+
|
| 182 |
+
def build_fc2(self, input_dim, output_dim):
|
| 183 |
+
return nn.Linear(input_dim, output_dim)
|
| 184 |
+
|
| 185 |
+
def build_self_attention(
|
| 186 |
+
self, embed_dim, args, add_bias_kv=False, add_zero_attn=False
|
| 187 |
+
):
|
| 188 |
+
return MultiheadAttention(
|
| 189 |
+
embed_dim,
|
| 190 |
+
args.decoder_attention_heads,
|
| 191 |
+
dropout=args.attention_dropout,
|
| 192 |
+
add_bias_kv=add_bias_kv,
|
| 193 |
+
add_zero_attn=add_zero_attn,
|
| 194 |
+
self_attention=True,
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
def build_encoder_attention(self, embed_dim, args):
|
| 198 |
+
return MultiheadAttention(
|
| 199 |
+
embed_dim,
|
| 200 |
+
args.decoder_attention_heads,
|
| 201 |
+
kdim=args.encoder_embed_dim,
|
| 202 |
+
vdim=args.encoder_embed_dim,
|
| 203 |
+
dropout=args.attention_dropout,
|
| 204 |
+
encoder_decoder_attention=True,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
def residual_connection(self, x, residual):
|
| 208 |
+
return residual + x
|
| 209 |
+
|
| 210 |
+
def forward(
|
| 211 |
+
self,
|
| 212 |
+
x,
|
| 213 |
+
encoder_out: Optional[torch.Tensor] = None,
|
| 214 |
+
encoder_padding_mask: Optional[torch.Tensor] = None,
|
| 215 |
+
incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None,
|
| 216 |
+
prev_self_attn_state: Optional[List[torch.Tensor]] = None,
|
| 217 |
+
prev_attn_state: Optional[List[torch.Tensor]] = None,
|
| 218 |
+
self_attn_mask: Optional[torch.Tensor] = None,
|
| 219 |
+
self_attn_padding_mask: Optional[torch.Tensor] = None,
|
| 220 |
+
need_attn: bool = False,
|
| 221 |
+
need_head_weights: bool = False,
|
| 222 |
+
):
|
| 223 |
+
"""
|
| 224 |
+
Args:
|
| 225 |
+
x (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)`
|
| 226 |
+
encoder_padding_mask (ByteTensor, optional): binary
|
| 227 |
+
ByteTensor of shape `(batch, src_len)` where padding
|
| 228 |
+
elements are indicated by ``1``.
|
| 229 |
+
need_attn (bool, optional): return attention weights
|
| 230 |
+
need_head_weights (bool, optional): return attention weights
|
| 231 |
+
for each head (default: return average over heads).
|
| 232 |
+
|
| 233 |
+
Returns:
|
| 234 |
+
encoded output of shape `(seq_len, batch, embed_dim)`
|
| 235 |
+
"""
|
| 236 |
+
if need_head_weights:
|
| 237 |
+
need_attn = True
|
| 238 |
+
|
| 239 |
+
residual = x
|
| 240 |
+
x = self.self_attn_layer_norm(x)
|
| 241 |
+
if prev_self_attn_state is not None:
|
| 242 |
+
prev_key, prev_value = prev_self_attn_state[:2]
|
| 243 |
+
saved_state: Dict[str, Optional[Tensor]] = {
|
| 244 |
+
"prev_key": prev_key,
|
| 245 |
+
"prev_value": prev_value,
|
| 246 |
+
}
|
| 247 |
+
if len(prev_self_attn_state) >= 3:
|
| 248 |
+
saved_state["prev_key_padding_mask"] = prev_self_attn_state[2]
|
| 249 |
+
assert incremental_state is not None
|
| 250 |
+
self.self_attn._set_input_buffer(incremental_state, saved_state)
|
| 251 |
+
_self_attn_input_buffer = self.self_attn._get_input_buffer(incremental_state)
|
| 252 |
+
y = x
|
| 253 |
+
|
| 254 |
+
x, attn = self.self_attn(
|
| 255 |
+
query=x,
|
| 256 |
+
key=y,
|
| 257 |
+
value=y,
|
| 258 |
+
key_padding_mask=self_attn_padding_mask,
|
| 259 |
+
incremental_state=incremental_state,
|
| 260 |
+
need_weights=False,
|
| 261 |
+
attn_mask=self_attn_mask,
|
| 262 |
+
)
|
| 263 |
+
x = self.dropout_module(x)
|
| 264 |
+
x = self.residual_connection(x, residual)
|
| 265 |
+
|
| 266 |
+
if self.encoder_attn is not None and encoder_out is not None:
|
| 267 |
+
residual = x
|
| 268 |
+
x = self.encoder_attn_layer_norm(x)
|
| 269 |
+
if prev_attn_state is not None:
|
| 270 |
+
prev_key, prev_value = prev_attn_state[:2]
|
| 271 |
+
saved_state: Dict[str, Optional[Tensor]] = {
|
| 272 |
+
"prev_key": prev_key,
|
| 273 |
+
"prev_value": prev_value,
|
| 274 |
+
}
|
| 275 |
+
if len(prev_attn_state) >= 3:
|
| 276 |
+
saved_state["prev_key_padding_mask"] = prev_attn_state[2]
|
| 277 |
+
assert incremental_state is not None
|
| 278 |
+
self.encoder_attn._set_input_buffer(incremental_state, saved_state)
|
| 279 |
+
|
| 280 |
+
x, attn = self.encoder_attn(
|
| 281 |
+
query=x,
|
| 282 |
+
key=encoder_out,
|
| 283 |
+
value=encoder_out,
|
| 284 |
+
key_padding_mask=encoder_padding_mask,
|
| 285 |
+
incremental_state=incremental_state,
|
| 286 |
+
static_kv=True,
|
| 287 |
+
need_weights=need_attn or (not self.training and self.need_attn),
|
| 288 |
+
need_head_weights=need_head_weights,
|
| 289 |
+
)
|
| 290 |
+
x = self.dropout_module(x)
|
| 291 |
+
x = self.residual_connection(x, residual)
|
| 292 |
+
|
| 293 |
+
residual = x
|
| 294 |
+
x = self.final_layer_norm(x)
|
| 295 |
+
|
| 296 |
+
x = self.activation_fn(self.fc1(x))
|
| 297 |
+
if self.ffn_layernorm is not None:
|
| 298 |
+
x = self.ffn_layernorm(x)
|
| 299 |
+
x = self.fc2(x)
|
| 300 |
+
x = self.dropout_module(x)
|
| 301 |
+
if self.w_resid is not None:
|
| 302 |
+
residual = torch.mul(self.w_resid, residual)
|
| 303 |
+
x = self.residual_connection(x, residual)
|
| 304 |
+
return x, attn, None
|
model/esm/inverse_folding/util.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import json
|
| 7 |
+
import math
|
| 8 |
+
|
| 9 |
+
import biotite.structure
|
| 10 |
+
from biotite.structure.io import pdbx, pdb
|
| 11 |
+
from biotite.structure.residues import get_residues
|
| 12 |
+
from biotite.structure import filter_backbone
|
| 13 |
+
from biotite.structure import get_chains
|
| 14 |
+
from biotite.sequence import ProteinSequence
|
| 15 |
+
import numpy as np
|
| 16 |
+
from scipy.spatial import transform
|
| 17 |
+
from scipy.stats import special_ortho_group
|
| 18 |
+
import torch
|
| 19 |
+
import torch.nn as nn
|
| 20 |
+
import torch.nn.functional as F
|
| 21 |
+
import torch.utils.data as data
|
| 22 |
+
from typing import Sequence, Tuple, List
|
| 23 |
+
|
| 24 |
+
from onescience.datapipes.esm import BatchConverter
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def load_structure(fpath, chain=None):
|
| 28 |
+
"""
|
| 29 |
+
Args:
|
| 30 |
+
fpath: filepath to either pdb or cif file
|
| 31 |
+
chain: the chain id or list of chain ids to load
|
| 32 |
+
Returns:
|
| 33 |
+
biotite.structure.AtomArray
|
| 34 |
+
"""
|
| 35 |
+
if fpath.endswith('cif'):
|
| 36 |
+
with open(fpath) as fin:
|
| 37 |
+
pdbxf = pdbx.PDBxFile.read(fin)
|
| 38 |
+
structure = pdbx.get_structure(pdbxf, model=1)
|
| 39 |
+
elif fpath.endswith('pdb'):
|
| 40 |
+
with open(fpath) as fin:
|
| 41 |
+
pdbf = pdb.PDBFile.read(fin)
|
| 42 |
+
structure = pdb.get_structure(pdbf, model=1)
|
| 43 |
+
bbmask = filter_backbone(structure)
|
| 44 |
+
structure = structure[bbmask]
|
| 45 |
+
all_chains = get_chains(structure)
|
| 46 |
+
if len(all_chains) == 0:
|
| 47 |
+
raise ValueError('No chains found in the input file.')
|
| 48 |
+
if chain is None:
|
| 49 |
+
chain_ids = all_chains
|
| 50 |
+
elif isinstance(chain, list):
|
| 51 |
+
chain_ids = chain
|
| 52 |
+
else:
|
| 53 |
+
chain_ids = [chain]
|
| 54 |
+
for chain in chain_ids:
|
| 55 |
+
if chain not in all_chains:
|
| 56 |
+
raise ValueError(f'Chain {chain} not found in input file')
|
| 57 |
+
chain_filter = [a.chain_id in chain_ids for a in structure]
|
| 58 |
+
structure = structure[chain_filter]
|
| 59 |
+
return structure
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def extract_coords_from_structure(structure: biotite.structure.AtomArray):
|
| 63 |
+
"""
|
| 64 |
+
Args:
|
| 65 |
+
structure: An instance of biotite AtomArray
|
| 66 |
+
Returns:
|
| 67 |
+
Tuple (coords, seq)
|
| 68 |
+
- coords is an L x 3 x 3 array for N, CA, C coordinates
|
| 69 |
+
- seq is the extracted sequence
|
| 70 |
+
"""
|
| 71 |
+
coords = get_atom_coords_residuewise(["N", "CA", "C"], structure)
|
| 72 |
+
residue_identities = get_residues(structure)[1]
|
| 73 |
+
seq = ''.join([ProteinSequence.convert_letter_3to1(r) for r in residue_identities])
|
| 74 |
+
return coords, seq
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def load_coords(fpath, chain):
|
| 78 |
+
"""
|
| 79 |
+
Args:
|
| 80 |
+
fpath: filepath to either pdb or cif file
|
| 81 |
+
chain: the chain id
|
| 82 |
+
Returns:
|
| 83 |
+
Tuple (coords, seq)
|
| 84 |
+
- coords is an L x 3 x 3 array for N, CA, C coordinates
|
| 85 |
+
- seq is the extracted sequence
|
| 86 |
+
"""
|
| 87 |
+
structure = load_structure(fpath, chain)
|
| 88 |
+
return extract_coords_from_structure(structure)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def get_atom_coords_residuewise(atoms: List[str], struct: biotite.structure.AtomArray):
|
| 92 |
+
"""
|
| 93 |
+
Example for atoms argument: ["N", "CA", "C"]
|
| 94 |
+
"""
|
| 95 |
+
def filterfn(s, axis=None):
|
| 96 |
+
filters = np.stack([s.atom_name == name for name in atoms], axis=1)
|
| 97 |
+
sum = filters.sum(0)
|
| 98 |
+
if not np.all(sum <= np.ones(filters.shape[1])):
|
| 99 |
+
raise RuntimeError("structure has multiple atoms with same name")
|
| 100 |
+
index = filters.argmax(0)
|
| 101 |
+
coords = s[index].coord
|
| 102 |
+
coords[sum == 0] = float("nan")
|
| 103 |
+
return coords
|
| 104 |
+
|
| 105 |
+
return biotite.structure.apply_residue_wise(struct, struct, filterfn)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def get_sequence_loss(model, alphabet, coords, seq):
|
| 109 |
+
device = next(model.parameters()).device
|
| 110 |
+
batch_converter = CoordBatchConverter(alphabet)
|
| 111 |
+
batch = [(coords, None, seq)]
|
| 112 |
+
coords, confidence, strs, tokens, padding_mask = batch_converter(
|
| 113 |
+
batch, device=device)
|
| 114 |
+
|
| 115 |
+
prev_output_tokens = tokens[:, :-1].to(device)
|
| 116 |
+
target = tokens[:, 1:]
|
| 117 |
+
target_padding_mask = (target == alphabet.padding_idx)
|
| 118 |
+
logits, _ = model.forward(coords, padding_mask, confidence, prev_output_tokens)
|
| 119 |
+
loss = F.cross_entropy(logits, target, reduction='none')
|
| 120 |
+
loss = loss[0].cpu().detach().numpy()
|
| 121 |
+
target_padding_mask = target_padding_mask[0].cpu().numpy()
|
| 122 |
+
return loss, target_padding_mask
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def score_sequence(model, alphabet, coords, seq):
|
| 126 |
+
loss, target_padding_mask = get_sequence_loss(model, alphabet, coords, seq)
|
| 127 |
+
ll_fullseq = -np.sum(loss * ~target_padding_mask) / np.sum(~target_padding_mask)
|
| 128 |
+
# Also calculate average when excluding masked portions
|
| 129 |
+
coord_mask = np.all(np.isfinite(coords), axis=(-1, -2))
|
| 130 |
+
ll_withcoord = -np.sum(loss * coord_mask) / np.sum(coord_mask)
|
| 131 |
+
return ll_fullseq, ll_withcoord
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def get_encoder_output(model, alphabet, coords):
|
| 135 |
+
device = next(model.parameters()).device
|
| 136 |
+
batch_converter = CoordBatchConverter(alphabet)
|
| 137 |
+
batch = [(coords, None, None)]
|
| 138 |
+
coords, confidence, strs, tokens, padding_mask = batch_converter(
|
| 139 |
+
batch, device=device)
|
| 140 |
+
encoder_out = model.encoder.forward(coords, padding_mask, confidence,
|
| 141 |
+
return_all_hiddens=False)
|
| 142 |
+
# remove beginning and end (bos and eos tokens)
|
| 143 |
+
return encoder_out['encoder_out'][0][1:-1, 0]
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def rotate(v, R):
|
| 147 |
+
"""
|
| 148 |
+
Rotates a vector by a rotation matrix.
|
| 149 |
+
|
| 150 |
+
Args:
|
| 151 |
+
v: 3D vector, tensor of shape (length x batch_size x channels x 3)
|
| 152 |
+
R: rotation matrix, tensor of shape (length x batch_size x 3 x 3)
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
Rotated version of v by rotation matrix R.
|
| 156 |
+
"""
|
| 157 |
+
R = R.unsqueeze(-3)
|
| 158 |
+
v = v.unsqueeze(-1)
|
| 159 |
+
return torch.sum(v * R, dim=-2)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def get_rotation_frames(coords):
|
| 163 |
+
"""
|
| 164 |
+
Returns a local rotation frame defined by N, CA, C positions.
|
| 165 |
+
|
| 166 |
+
Args:
|
| 167 |
+
coords: coordinates, tensor of shape (batch_size x length x 3 x 3)
|
| 168 |
+
where the third dimension is in order of N, CA, C
|
| 169 |
+
|
| 170 |
+
Returns:
|
| 171 |
+
Local relative rotation frames in shape (batch_size x length x 3 x 3)
|
| 172 |
+
"""
|
| 173 |
+
v1 = coords[:, :, 2] - coords[:, :, 1]
|
| 174 |
+
v2 = coords[:, :, 0] - coords[:, :, 1]
|
| 175 |
+
e1 = normalize(v1, dim=-1)
|
| 176 |
+
u2 = v2 - e1 * torch.sum(e1 * v2, dim=-1, keepdim=True)
|
| 177 |
+
e2 = normalize(u2, dim=-1)
|
| 178 |
+
e3 = torch.cross(e1, e2, dim=-1)
|
| 179 |
+
R = torch.stack([e1, e2, e3], dim=-2)
|
| 180 |
+
return R
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def nan_to_num(ts, val=0.0):
|
| 184 |
+
"""
|
| 185 |
+
Replaces nans in tensor with a fixed value.
|
| 186 |
+
"""
|
| 187 |
+
val = torch.tensor(val, dtype=ts.dtype, device=ts.device)
|
| 188 |
+
return torch.where(~torch.isfinite(ts), val, ts)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def rbf(values, v_min, v_max, n_bins=16):
|
| 192 |
+
"""
|
| 193 |
+
Returns RBF encodings in a new dimension at the end.
|
| 194 |
+
"""
|
| 195 |
+
rbf_centers = torch.linspace(v_min, v_max, n_bins, device=values.device)
|
| 196 |
+
rbf_centers = rbf_centers.view([1] * len(values.shape) + [-1])
|
| 197 |
+
rbf_std = (v_max - v_min) / n_bins
|
| 198 |
+
v_expand = torch.unsqueeze(values, -1)
|
| 199 |
+
z = (values.unsqueeze(-1) - rbf_centers) / rbf_std
|
| 200 |
+
return torch.exp(-z ** 2)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def norm(tensor, dim, eps=1e-8, keepdim=False):
|
| 204 |
+
"""
|
| 205 |
+
Returns L2 norm along a dimension.
|
| 206 |
+
"""
|
| 207 |
+
return torch.sqrt(
|
| 208 |
+
torch.sum(torch.square(tensor), dim=dim, keepdim=keepdim) + eps)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def normalize(tensor, dim=-1):
|
| 212 |
+
"""
|
| 213 |
+
Normalizes a tensor along a dimension after removing nans.
|
| 214 |
+
"""
|
| 215 |
+
return nan_to_num(
|
| 216 |
+
torch.div(tensor, norm(tensor, dim=dim, keepdim=True))
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
class CoordBatchConverter(BatchConverter):
|
| 221 |
+
def __call__(self, raw_batch: Sequence[Tuple[Sequence, str]], device=None):
|
| 222 |
+
"""
|
| 223 |
+
Args:
|
| 224 |
+
raw_batch: List of tuples (coords, confidence, seq)
|
| 225 |
+
In each tuple,
|
| 226 |
+
coords: list of floats, shape L x 3 x 3
|
| 227 |
+
confidence: list of floats, shape L; or scalar float; or None
|
| 228 |
+
seq: string of length L
|
| 229 |
+
Returns:
|
| 230 |
+
coords: Tensor of shape batch_size x L x 3 x 3
|
| 231 |
+
confidence: Tensor of shape batch_size x L
|
| 232 |
+
strs: list of strings
|
| 233 |
+
tokens: LongTensor of shape batch_size x L
|
| 234 |
+
padding_mask: ByteTensor of shape batch_size x L
|
| 235 |
+
"""
|
| 236 |
+
self.alphabet.cls_idx = self.alphabet.get_idx("<cath>")
|
| 237 |
+
batch = []
|
| 238 |
+
for coords, confidence, seq in raw_batch:
|
| 239 |
+
if confidence is None:
|
| 240 |
+
confidence = 1.
|
| 241 |
+
if isinstance(confidence, float) or isinstance(confidence, int):
|
| 242 |
+
confidence = [float(confidence)] * len(coords)
|
| 243 |
+
if seq is None:
|
| 244 |
+
seq = 'X' * len(coords)
|
| 245 |
+
batch.append(((coords, confidence), seq))
|
| 246 |
+
|
| 247 |
+
coords_and_confidence, strs, tokens = super().__call__(batch)
|
| 248 |
+
|
| 249 |
+
# pad beginning and end of each protein due to legacy reasons
|
| 250 |
+
coords = [
|
| 251 |
+
F.pad(torch.tensor(cd), (0, 0, 0, 0, 1, 1), value=np.inf)
|
| 252 |
+
for cd, _ in coords_and_confidence
|
| 253 |
+
]
|
| 254 |
+
confidence = [
|
| 255 |
+
F.pad(torch.tensor(cf), (1, 1), value=-1.)
|
| 256 |
+
for _, cf in coords_and_confidence
|
| 257 |
+
]
|
| 258 |
+
coords = self.collate_dense_tensors(coords, pad_v=np.nan)
|
| 259 |
+
confidence = self.collate_dense_tensors(confidence, pad_v=-1.)
|
| 260 |
+
if device is not None:
|
| 261 |
+
coords = coords.to(device)
|
| 262 |
+
confidence = confidence.to(device)
|
| 263 |
+
tokens = tokens.to(device)
|
| 264 |
+
padding_mask = torch.isnan(coords[:,:,0,0])
|
| 265 |
+
coord_mask = torch.isfinite(coords.sum(-2).sum(-1))
|
| 266 |
+
confidence = confidence * coord_mask + (-1.) * padding_mask
|
| 267 |
+
return coords, confidence, strs, tokens, padding_mask
|
| 268 |
+
|
| 269 |
+
def from_lists(self, coords_list, confidence_list=None, seq_list=None, device=None):
|
| 270 |
+
"""
|
| 271 |
+
Args:
|
| 272 |
+
coords_list: list of length batch_size, each item is a list of
|
| 273 |
+
floats in shape L x 3 x 3 to describe a backbone
|
| 274 |
+
confidence_list: one of
|
| 275 |
+
- None, default to highest confidence
|
| 276 |
+
- list of length batch_size, each item is a scalar
|
| 277 |
+
- list of length batch_size, each item is a list of floats of
|
| 278 |
+
length L to describe the confidence scores for the backbone
|
| 279 |
+
with values between 0. and 1.
|
| 280 |
+
seq_list: either None or a list of strings
|
| 281 |
+
Returns:
|
| 282 |
+
coords: Tensor of shape batch_size x L x 3 x 3
|
| 283 |
+
confidence: Tensor of shape batch_size x L
|
| 284 |
+
strs: list of strings
|
| 285 |
+
tokens: LongTensor of shape batch_size x L
|
| 286 |
+
padding_mask: ByteTensor of shape batch_size x L
|
| 287 |
+
"""
|
| 288 |
+
batch_size = len(coords_list)
|
| 289 |
+
if confidence_list is None:
|
| 290 |
+
confidence_list = [None] * batch_size
|
| 291 |
+
if seq_list is None:
|
| 292 |
+
seq_list = [None] * batch_size
|
| 293 |
+
raw_batch = zip(coords_list, confidence_list, seq_list)
|
| 294 |
+
return self.__call__(raw_batch, device)
|
| 295 |
+
|
| 296 |
+
@staticmethod
|
| 297 |
+
def collate_dense_tensors(samples, pad_v):
|
| 298 |
+
"""
|
| 299 |
+
Takes a list of tensors with the following dimensions:
|
| 300 |
+
[(d_11, ..., d_1K),
|
| 301 |
+
(d_21, ..., d_2K),
|
| 302 |
+
...,
|
| 303 |
+
(d_N1, ..., d_NK)]
|
| 304 |
+
and stack + pads them into a single tensor of:
|
| 305 |
+
(N, max_i=1,N { d_i1 }, ..., max_i=1,N {diK})
|
| 306 |
+
"""
|
| 307 |
+
if len(samples) == 0:
|
| 308 |
+
return torch.Tensor()
|
| 309 |
+
if len(set(x.dim() for x in samples)) != 1:
|
| 310 |
+
raise RuntimeError(
|
| 311 |
+
f"Samples has varying dimensions: {[x.dim() for x in samples]}"
|
| 312 |
+
)
|
| 313 |
+
(device,) = tuple(set(x.device for x in samples)) # assumes all on same device
|
| 314 |
+
max_shape = [max(lst) for lst in zip(*[x.shape for x in samples])]
|
| 315 |
+
result = torch.empty(
|
| 316 |
+
len(samples), *max_shape, dtype=samples[0].dtype, device=device
|
| 317 |
+
)
|
| 318 |
+
result.fill_(pad_v)
|
| 319 |
+
for i in range(len(samples)):
|
| 320 |
+
result_i = result[i]
|
| 321 |
+
t = samples[i]
|
| 322 |
+
result_i[tuple(slice(0, k) for k in t.shape)] = t
|
| 323 |
+
return result
|
model/esm/msa_transformer.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
import torch.nn as nn
|
| 8 |
+
|
| 9 |
+
from onescience.modules.esm import (
|
| 10 |
+
AxialTransformerLayer,
|
| 11 |
+
LearnedPositionalEmbedding,
|
| 12 |
+
RobertaLMHead,
|
| 13 |
+
ESM1bLayerNorm,
|
| 14 |
+
ContactPredictionHead,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
from onescience.modules.attention import RowSelfAttention, ColumnSelfAttention
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class MSATransformer(nn.Module):
|
| 22 |
+
@classmethod
|
| 23 |
+
def add_args(cls, parser):
|
| 24 |
+
# fmt: off
|
| 25 |
+
parser.add_argument(
|
| 26 |
+
"--num_layers",
|
| 27 |
+
default=12,
|
| 28 |
+
type=int,
|
| 29 |
+
metavar="N",
|
| 30 |
+
help="number of layers"
|
| 31 |
+
)
|
| 32 |
+
parser.add_argument(
|
| 33 |
+
"--embed_dim",
|
| 34 |
+
default=768,
|
| 35 |
+
type=int,
|
| 36 |
+
metavar="N",
|
| 37 |
+
help="embedding dimension"
|
| 38 |
+
)
|
| 39 |
+
parser.add_argument(
|
| 40 |
+
"--logit_bias",
|
| 41 |
+
action="store_true",
|
| 42 |
+
help="whether to apply bias to logits"
|
| 43 |
+
)
|
| 44 |
+
parser.add_argument(
|
| 45 |
+
"--ffn_embed_dim",
|
| 46 |
+
default=3072,
|
| 47 |
+
type=int,
|
| 48 |
+
metavar="N",
|
| 49 |
+
help="embedding dimension for FFN",
|
| 50 |
+
)
|
| 51 |
+
parser.add_argument(
|
| 52 |
+
"--attention_heads",
|
| 53 |
+
default=12,
|
| 54 |
+
type=int,
|
| 55 |
+
metavar="N",
|
| 56 |
+
help="number of attention heads",
|
| 57 |
+
)
|
| 58 |
+
parser.add_argument(
|
| 59 |
+
"--dropout",
|
| 60 |
+
default=0.1,
|
| 61 |
+
type=float,
|
| 62 |
+
help="Dropout to apply."
|
| 63 |
+
)
|
| 64 |
+
parser.add_argument(
|
| 65 |
+
"--attention_dropout",
|
| 66 |
+
default=0.1,
|
| 67 |
+
type=float,
|
| 68 |
+
help="Dropout to apply."
|
| 69 |
+
)
|
| 70 |
+
parser.add_argument(
|
| 71 |
+
"--activation_dropout",
|
| 72 |
+
default=0.1,
|
| 73 |
+
type=float,
|
| 74 |
+
help="Dropout to apply."
|
| 75 |
+
)
|
| 76 |
+
parser.add_argument(
|
| 77 |
+
"--max_tokens_per_msa",
|
| 78 |
+
default=2 ** 14,
|
| 79 |
+
type=int,
|
| 80 |
+
help=(
|
| 81 |
+
"Used during inference to batch attention computations in a single "
|
| 82 |
+
"forward pass. This allows increased input sizes with less memory."
|
| 83 |
+
),
|
| 84 |
+
)
|
| 85 |
+
# fmt: on
|
| 86 |
+
|
| 87 |
+
def __init__(self, args, alphabet):
|
| 88 |
+
super().__init__()
|
| 89 |
+
self.args = args
|
| 90 |
+
self.alphabet_size = len(alphabet)
|
| 91 |
+
self.padding_idx = alphabet.padding_idx
|
| 92 |
+
self.mask_idx = alphabet.mask_idx
|
| 93 |
+
self.cls_idx = alphabet.cls_idx
|
| 94 |
+
self.eos_idx = alphabet.eos_idx
|
| 95 |
+
self.prepend_bos = alphabet.prepend_bos
|
| 96 |
+
self.append_eos = alphabet.append_eos
|
| 97 |
+
|
| 98 |
+
self.embed_tokens = nn.Embedding(
|
| 99 |
+
self.alphabet_size, self.args.embed_dim, padding_idx=self.padding_idx
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
if getattr(self.args, "embed_positions_msa", False):
|
| 103 |
+
emb_dim = getattr(self.args, "embed_positions_msa_dim", self.args.embed_dim)
|
| 104 |
+
self.msa_position_embedding = nn.Parameter(
|
| 105 |
+
0.01 * torch.randn(1, 1024, 1, emb_dim),
|
| 106 |
+
requires_grad=True,
|
| 107 |
+
)
|
| 108 |
+
else:
|
| 109 |
+
self.register_parameter("msa_position_embedding", None)
|
| 110 |
+
|
| 111 |
+
self.dropout_module = nn.Dropout(self.args.dropout)
|
| 112 |
+
self.layers = nn.ModuleList(
|
| 113 |
+
[
|
| 114 |
+
AxialTransformerLayer(
|
| 115 |
+
self.args.embed_dim,
|
| 116 |
+
self.args.ffn_embed_dim,
|
| 117 |
+
self.args.attention_heads,
|
| 118 |
+
self.args.dropout,
|
| 119 |
+
self.args.attention_dropout,
|
| 120 |
+
self.args.activation_dropout,
|
| 121 |
+
getattr(self.args, "max_tokens_per_msa", self.args.max_tokens),
|
| 122 |
+
)
|
| 123 |
+
for _ in range(self.args.layers)
|
| 124 |
+
]
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
self.contact_head = ContactPredictionHead(
|
| 128 |
+
self.args.layers * self.args.attention_heads,
|
| 129 |
+
self.prepend_bos,
|
| 130 |
+
self.append_eos,
|
| 131 |
+
eos_idx=self.eos_idx,
|
| 132 |
+
)
|
| 133 |
+
self.embed_positions = LearnedPositionalEmbedding(
|
| 134 |
+
self.args.max_positions,
|
| 135 |
+
self.args.embed_dim,
|
| 136 |
+
self.padding_idx,
|
| 137 |
+
)
|
| 138 |
+
self.emb_layer_norm_before = ESM1bLayerNorm(self.args.embed_dim)
|
| 139 |
+
self.emb_layer_norm_after = ESM1bLayerNorm(self.args.embed_dim)
|
| 140 |
+
self.lm_head = RobertaLMHead(
|
| 141 |
+
embed_dim=self.args.embed_dim,
|
| 142 |
+
output_dim=self.alphabet_size,
|
| 143 |
+
weight=self.embed_tokens.weight,
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
def forward(self, tokens, repr_layers=[], need_head_weights=False, return_contacts=False):
|
| 147 |
+
if return_contacts:
|
| 148 |
+
need_head_weights = True
|
| 149 |
+
|
| 150 |
+
assert tokens.ndim == 3
|
| 151 |
+
batch_size, num_alignments, seqlen = tokens.size()
|
| 152 |
+
padding_mask = tokens.eq(self.padding_idx) # B, R, C
|
| 153 |
+
if not padding_mask.any():
|
| 154 |
+
padding_mask = None
|
| 155 |
+
|
| 156 |
+
x = self.embed_tokens(tokens)
|
| 157 |
+
x += self.embed_positions(tokens.view(batch_size * num_alignments, seqlen)).view(x.size())
|
| 158 |
+
if self.msa_position_embedding is not None:
|
| 159 |
+
if x.size(1) > 1024:
|
| 160 |
+
raise RuntimeError(
|
| 161 |
+
"Using model with MSA position embedding trained on maximum MSA "
|
| 162 |
+
f"depth of 1024, but received {x.size(1)} alignments."
|
| 163 |
+
)
|
| 164 |
+
x += self.msa_position_embedding[:, :num_alignments]
|
| 165 |
+
|
| 166 |
+
x = self.emb_layer_norm_before(x)
|
| 167 |
+
|
| 168 |
+
x = self.dropout_module(x)
|
| 169 |
+
|
| 170 |
+
if padding_mask is not None:
|
| 171 |
+
x = x * (1 - padding_mask.unsqueeze(-1).type_as(x))
|
| 172 |
+
|
| 173 |
+
repr_layers = set(repr_layers)
|
| 174 |
+
hidden_representations = {}
|
| 175 |
+
if 0 in repr_layers:
|
| 176 |
+
hidden_representations[0] = x
|
| 177 |
+
|
| 178 |
+
if need_head_weights:
|
| 179 |
+
row_attn_weights = []
|
| 180 |
+
col_attn_weights = []
|
| 181 |
+
|
| 182 |
+
# B x R x C x D -> R x C x B x D
|
| 183 |
+
x = x.permute(1, 2, 0, 3)
|
| 184 |
+
|
| 185 |
+
for layer_idx, layer in enumerate(self.layers):
|
| 186 |
+
x = layer(
|
| 187 |
+
x,
|
| 188 |
+
self_attn_padding_mask=padding_mask,
|
| 189 |
+
need_head_weights=need_head_weights,
|
| 190 |
+
)
|
| 191 |
+
if need_head_weights:
|
| 192 |
+
x, col_attn, row_attn = x
|
| 193 |
+
# H x C x B x R x R -> B x H x C x R x R
|
| 194 |
+
col_attn_weights.append(col_attn.permute(2, 0, 1, 3, 4))
|
| 195 |
+
# H x B x C x C -> B x H x C x C
|
| 196 |
+
row_attn_weights.append(row_attn.permute(1, 0, 2, 3))
|
| 197 |
+
if (layer_idx + 1) in repr_layers:
|
| 198 |
+
hidden_representations[layer_idx + 1] = x.permute(2, 0, 1, 3)
|
| 199 |
+
|
| 200 |
+
x = self.emb_layer_norm_after(x)
|
| 201 |
+
x = x.permute(2, 0, 1, 3) # R x C x B x D -> B x R x C x D
|
| 202 |
+
|
| 203 |
+
# last hidden representation should have layer norm applied
|
| 204 |
+
if (layer_idx + 1) in repr_layers:
|
| 205 |
+
hidden_representations[layer_idx + 1] = x
|
| 206 |
+
x = self.lm_head(x)
|
| 207 |
+
|
| 208 |
+
result = {"logits": x, "representations": hidden_representations}
|
| 209 |
+
if need_head_weights:
|
| 210 |
+
# col_attentions: B x L x H x C x R x R
|
| 211 |
+
col_attentions = torch.stack(col_attn_weights, 1)
|
| 212 |
+
# row_attentions: B x L x H x C x C
|
| 213 |
+
row_attentions = torch.stack(row_attn_weights, 1)
|
| 214 |
+
result["col_attentions"] = col_attentions
|
| 215 |
+
result["row_attentions"] = row_attentions
|
| 216 |
+
if return_contacts:
|
| 217 |
+
contacts = self.contact_head(tokens, row_attentions)
|
| 218 |
+
result["contacts"] = contacts
|
| 219 |
+
|
| 220 |
+
return result
|
| 221 |
+
|
| 222 |
+
def predict_contacts(self, tokens):
|
| 223 |
+
return self(tokens, return_contacts=True)["contacts"]
|
| 224 |
+
|
| 225 |
+
@property
|
| 226 |
+
def num_layers(self):
|
| 227 |
+
return self.args.layers
|
| 228 |
+
|
| 229 |
+
def max_tokens_per_msa_(self, value: int) -> None:
|
| 230 |
+
"""The MSA Transformer automatically batches attention computations when
|
| 231 |
+
gradients are disabled to allow you to pass in larger MSAs at test time than
|
| 232 |
+
you can fit in GPU memory. By default this occurs when more than 2^14 tokens
|
| 233 |
+
are passed in the input MSA. You can set this value to infinity to disable
|
| 234 |
+
this behavior.
|
| 235 |
+
"""
|
| 236 |
+
for module in self.modules():
|
| 237 |
+
if isinstance(module, (RowSelfAttention, ColumnSelfAttention)):
|
| 238 |
+
module.max_tokens_per_msa = value
|
model/esm/pretrained.py
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
import re
|
| 7 |
+
import urllib
|
| 8 |
+
import warnings
|
| 9 |
+
from argparse import Namespace
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
import model.esm as esm
|
| 15 |
+
from onescience.datapipes.esm import Alphabet
|
| 16 |
+
from model.esm.esm2 import ESM2
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _has_regression_weights(model_name):
|
| 20 |
+
"""Return whether we expect / require regression weights;
|
| 21 |
+
Right now that is all models except ESM-1v, ESM-IF, and partially trained ESM2 models"""
|
| 22 |
+
return not ("esm1v" in model_name or "esm_if" in model_name or "270K" in model_name or "500K" in model_name)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def load_model_and_alphabet(model_name):
|
| 26 |
+
if model_name.endswith(".pt"): # treat as filepath
|
| 27 |
+
return load_model_and_alphabet_local(model_name)
|
| 28 |
+
else:
|
| 29 |
+
return load_model_and_alphabet_hub(model_name)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def load_hub_workaround(url):
|
| 33 |
+
try:
|
| 34 |
+
data = torch.hub.load_state_dict_from_url(url, progress=False, map_location="cpu")
|
| 35 |
+
except RuntimeError:
|
| 36 |
+
# Pytorch version issue - see https://github.com/pytorch/pytorch/issues/43106
|
| 37 |
+
fn = Path(url).name
|
| 38 |
+
data = torch.load(
|
| 39 |
+
f"{torch.hub.get_dir()}/checkpoints/{fn}",
|
| 40 |
+
map_location="cpu",
|
| 41 |
+
)
|
| 42 |
+
except urllib.error.HTTPError as e:
|
| 43 |
+
raise Exception(f"Could not load {url}, check if you specified a correct model name?")
|
| 44 |
+
return data
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def load_regression_hub(model_name):
|
| 48 |
+
url = f"https://dl.fbaipublicfiles.com/fair-esm/regression/{model_name}-contact-regression.pt"
|
| 49 |
+
regression_data = load_hub_workaround(url)
|
| 50 |
+
return regression_data
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _download_model_and_regression_data(model_name):
|
| 54 |
+
url = f"https://dl.fbaipublicfiles.com/fair-esm/models/{model_name}.pt"
|
| 55 |
+
model_data = load_hub_workaround(url)
|
| 56 |
+
if _has_regression_weights(model_name):
|
| 57 |
+
regression_data = load_regression_hub(model_name)
|
| 58 |
+
else:
|
| 59 |
+
regression_data = None
|
| 60 |
+
return model_data, regression_data
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def load_model_and_alphabet_hub(model_name):
|
| 64 |
+
model_data, regression_data = _download_model_and_regression_data(model_name)
|
| 65 |
+
return load_model_and_alphabet_core(model_name, model_data, regression_data)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def load_model_and_alphabet_local(model_location):
|
| 69 |
+
"""Load from local path. The regression weights need to be co-located"""
|
| 70 |
+
model_location = Path(model_location)
|
| 71 |
+
model_data = torch.load(str(model_location), map_location="cpu")
|
| 72 |
+
model_name = model_location.stem
|
| 73 |
+
if _has_regression_weights(model_name):
|
| 74 |
+
regression_location = str(model_location.with_suffix("")) + "-contact-regression.pt"
|
| 75 |
+
regression_data = torch.load(regression_location, map_location="cpu")
|
| 76 |
+
else:
|
| 77 |
+
regression_data = None
|
| 78 |
+
return load_model_and_alphabet_core(model_name, model_data, regression_data)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def has_emb_layer_norm_before(model_state):
|
| 82 |
+
"""Determine whether layer norm needs to be applied before the encoder"""
|
| 83 |
+
return any(k.startswith("emb_layer_norm_before") for k, param in model_state.items())
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def _load_model_and_alphabet_core_v1(model_data):
|
| 87 |
+
import model.esm as esm # since esm.inverse_folding is imported below, you actually have to re-import esm here
|
| 88 |
+
|
| 89 |
+
alphabet = esm.Alphabet.from_architecture(model_data["args"].arch)
|
| 90 |
+
|
| 91 |
+
if model_data["args"].arch == "roberta_large":
|
| 92 |
+
# upgrade state dict
|
| 93 |
+
pra = lambda s: "".join(s.split("encoder_")[1:] if "encoder" in s else s)
|
| 94 |
+
prs1 = lambda s: "".join(s.split("encoder.")[1:] if "encoder" in s else s)
|
| 95 |
+
prs2 = lambda s: "".join(
|
| 96 |
+
s.split("sentence_encoder.")[1:] if "sentence_encoder" in s else s
|
| 97 |
+
)
|
| 98 |
+
model_args = {pra(arg[0]): arg[1] for arg in vars(model_data["args"]).items()}
|
| 99 |
+
model_state = {prs1(prs2(arg[0])): arg[1] for arg in model_data["model"].items()}
|
| 100 |
+
model_state["embed_tokens.weight"][alphabet.mask_idx].zero_() # For token drop
|
| 101 |
+
model_args["emb_layer_norm_before"] = has_emb_layer_norm_before(model_state)
|
| 102 |
+
model_type = esm.ProteinBertModel
|
| 103 |
+
|
| 104 |
+
elif model_data["args"].arch == "protein_bert_base":
|
| 105 |
+
|
| 106 |
+
# upgrade state dict
|
| 107 |
+
pra = lambda s: "".join(s.split("decoder_")[1:] if "decoder" in s else s)
|
| 108 |
+
prs = lambda s: "".join(s.split("decoder.")[1:] if "decoder" in s else s)
|
| 109 |
+
model_args = {pra(arg[0]): arg[1] for arg in vars(model_data["args"]).items()}
|
| 110 |
+
model_state = {prs(arg[0]): arg[1] for arg in model_data["model"].items()}
|
| 111 |
+
model_type = esm.ProteinBertModel
|
| 112 |
+
elif model_data["args"].arch == "msa_transformer":
|
| 113 |
+
|
| 114 |
+
# upgrade state dict
|
| 115 |
+
pra = lambda s: "".join(s.split("encoder_")[1:] if "encoder" in s else s)
|
| 116 |
+
prs1 = lambda s: "".join(s.split("encoder.")[1:] if "encoder" in s else s)
|
| 117 |
+
prs2 = lambda s: "".join(
|
| 118 |
+
s.split("sentence_encoder.")[1:] if "sentence_encoder" in s else s
|
| 119 |
+
)
|
| 120 |
+
prs3 = lambda s: s.replace("row", "column") if "row" in s else s.replace("column", "row")
|
| 121 |
+
model_args = {pra(arg[0]): arg[1] for arg in vars(model_data["args"]).items()}
|
| 122 |
+
model_state = {prs1(prs2(prs3(arg[0]))): arg[1] for arg in model_data["model"].items()}
|
| 123 |
+
if model_args.get("embed_positions_msa", False):
|
| 124 |
+
emb_dim = model_state["msa_position_embedding"].size(-1)
|
| 125 |
+
model_args["embed_positions_msa_dim"] = emb_dim # initial release, bug: emb_dim==1
|
| 126 |
+
|
| 127 |
+
model_type = esm.MSATransformer
|
| 128 |
+
|
| 129 |
+
elif "invariant_gvp" in model_data["args"].arch:
|
| 130 |
+
import model.esm.inverse_folding
|
| 131 |
+
|
| 132 |
+
model_type = esm.inverse_folding.gvp_transformer.GVPTransformerModel
|
| 133 |
+
model_args = vars(model_data["args"]) # convert Namespace -> dict
|
| 134 |
+
|
| 135 |
+
def update_name(s):
|
| 136 |
+
# Map the module names in checkpoints trained with internal code to
|
| 137 |
+
# the updated module names in open source code
|
| 138 |
+
s = s.replace("W_v", "embed_graph.embed_node")
|
| 139 |
+
s = s.replace("W_e", "embed_graph.embed_edge")
|
| 140 |
+
s = s.replace("embed_scores.0", "embed_confidence")
|
| 141 |
+
s = s.replace("embed_score.", "embed_graph.embed_confidence.")
|
| 142 |
+
s = s.replace("seq_logits_projection.", "")
|
| 143 |
+
s = s.replace("embed_ingraham_features", "embed_dihedrals")
|
| 144 |
+
s = s.replace("embed_gvp_in_local_frame.0", "embed_gvp_output")
|
| 145 |
+
s = s.replace("embed_features_in_local_frame.0", "embed_gvp_input_features")
|
| 146 |
+
return s
|
| 147 |
+
|
| 148 |
+
model_state = {
|
| 149 |
+
update_name(sname): svalue
|
| 150 |
+
for sname, svalue in model_data["model"].items()
|
| 151 |
+
if "version" not in sname
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
else:
|
| 155 |
+
raise ValueError("Unknown architecture selected")
|
| 156 |
+
|
| 157 |
+
model = model_type(
|
| 158 |
+
Namespace(**model_args),
|
| 159 |
+
alphabet,
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
return model, alphabet, model_state
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def _load_model_and_alphabet_core_v2(model_data):
|
| 166 |
+
def upgrade_state_dict(state_dict):
|
| 167 |
+
"""Removes prefixes 'model.encoder.sentence_encoder.' and 'model.encoder.'."""
|
| 168 |
+
prefixes = ["encoder.sentence_encoder.", "encoder."]
|
| 169 |
+
pattern = re.compile("^" + "|".join(prefixes))
|
| 170 |
+
state_dict = {pattern.sub("", name): param for name, param in state_dict.items()}
|
| 171 |
+
return state_dict
|
| 172 |
+
|
| 173 |
+
cfg = model_data["cfg"]["model"]
|
| 174 |
+
state_dict = model_data["model"]
|
| 175 |
+
state_dict = upgrade_state_dict(state_dict)
|
| 176 |
+
alphabet = Alphabet.from_architecture("ESM-1b")
|
| 177 |
+
model = ESM2(
|
| 178 |
+
num_layers=cfg.encoder_layers,
|
| 179 |
+
embed_dim=cfg.encoder_embed_dim,
|
| 180 |
+
attention_heads=cfg.encoder_attention_heads,
|
| 181 |
+
alphabet=alphabet,
|
| 182 |
+
token_dropout=cfg.token_dropout,
|
| 183 |
+
)
|
| 184 |
+
return model, alphabet, state_dict
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def load_model_and_alphabet_core(model_name, model_data, regression_data=None):
|
| 188 |
+
if regression_data is not None:
|
| 189 |
+
model_data["model"].update(regression_data["model"])
|
| 190 |
+
|
| 191 |
+
if model_name.startswith("esm2"):
|
| 192 |
+
model, alphabet, model_state = _load_model_and_alphabet_core_v2(model_data)
|
| 193 |
+
else:
|
| 194 |
+
model, alphabet, model_state = _load_model_and_alphabet_core_v1(model_data)
|
| 195 |
+
|
| 196 |
+
expected_keys = set(model.state_dict().keys())
|
| 197 |
+
found_keys = set(model_state.keys())
|
| 198 |
+
|
| 199 |
+
if regression_data is None:
|
| 200 |
+
expected_missing = {"contact_head.regression.weight", "contact_head.regression.bias"}
|
| 201 |
+
error_msgs = []
|
| 202 |
+
missing = (expected_keys - found_keys) - expected_missing
|
| 203 |
+
if missing:
|
| 204 |
+
error_msgs.append(f"Missing key(s) in state_dict: {missing}.")
|
| 205 |
+
unexpected = found_keys - expected_keys
|
| 206 |
+
if unexpected:
|
| 207 |
+
error_msgs.append(f"Unexpected key(s) in state_dict: {unexpected}.")
|
| 208 |
+
|
| 209 |
+
if error_msgs:
|
| 210 |
+
raise RuntimeError(
|
| 211 |
+
"Error(s) in loading state_dict for {}:\n\t{}".format(
|
| 212 |
+
model.__class__.__name__, "\n\t".join(error_msgs)
|
| 213 |
+
)
|
| 214 |
+
)
|
| 215 |
+
if expected_missing - found_keys:
|
| 216 |
+
warnings.warn(
|
| 217 |
+
"Regression weights not found, predicting contacts will not produce correct results."
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
model.load_state_dict(model_state, strict=regression_data is not None)
|
| 221 |
+
|
| 222 |
+
return model, alphabet
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def esm1_t34_670M_UR50S():
|
| 226 |
+
"""34 layer transformer model with 670M params, trained on Uniref50 Sparse.
|
| 227 |
+
|
| 228 |
+
Returns a tuple of (Model, Alphabet).
|
| 229 |
+
"""
|
| 230 |
+
return load_model_and_alphabet_hub("esm1_t34_670M_UR50S")
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def esm1_t34_670M_UR50D():
|
| 234 |
+
"""34 layer transformer model with 670M params, trained on Uniref50 Dense.
|
| 235 |
+
|
| 236 |
+
Returns a tuple of (Model, Alphabet).
|
| 237 |
+
"""
|
| 238 |
+
return load_model_and_alphabet_hub("esm1_t34_670M_UR50D")
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def esm1_t34_670M_UR100():
|
| 242 |
+
"""34 layer transformer model with 670M params, trained on Uniref100.
|
| 243 |
+
|
| 244 |
+
Returns a tuple of (Model, Alphabet).
|
| 245 |
+
"""
|
| 246 |
+
return load_model_and_alphabet_hub("esm1_t34_670M_UR100")
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def esm1_t12_85M_UR50S():
|
| 250 |
+
"""12 layer transformer model with 85M params, trained on Uniref50 Sparse.
|
| 251 |
+
|
| 252 |
+
Returns a tuple of (Model, Alphabet).
|
| 253 |
+
"""
|
| 254 |
+
return load_model_and_alphabet_hub("esm1_t12_85M_UR50S")
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def esm1_t6_43M_UR50S():
|
| 258 |
+
"""6 layer transformer model with 43M params, trained on Uniref50 Sparse.
|
| 259 |
+
|
| 260 |
+
Returns a tuple of (Model, Alphabet).
|
| 261 |
+
"""
|
| 262 |
+
return load_model_and_alphabet_hub("esm1_t6_43M_UR50S")
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def esm1b_t33_650M_UR50S():
|
| 266 |
+
"""33 layer transformer model with 650M params, trained on Uniref50 Sparse.
|
| 267 |
+
This is our best performing model, which will be described in a future publication.
|
| 268 |
+
|
| 269 |
+
Returns a tuple of (Model, Alphabet).
|
| 270 |
+
"""
|
| 271 |
+
return load_model_and_alphabet_hub("esm1b_t33_650M_UR50S")
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def esm_msa1_t12_100M_UR50S():
|
| 275 |
+
warnings.warn(
|
| 276 |
+
"This model had a minor bug in the positional embeddings, "
|
| 277 |
+
"please use ESM-MSA-1b: esm.pretrained.esm_msa1b_t12_100M_UR50S()",
|
| 278 |
+
)
|
| 279 |
+
return load_model_and_alphabet_hub("esm_msa1_t12_100M_UR50S")
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def esm_msa1b_t12_100M_UR50S():
|
| 283 |
+
return load_model_and_alphabet_hub("esm_msa1b_t12_100M_UR50S")
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def esm1v_t33_650M_UR90S():
|
| 287 |
+
"""33 layer transformer model with 650M params, trained on Uniref90.
|
| 288 |
+
This is model 1 of a 5 model ensemble.
|
| 289 |
+
|
| 290 |
+
Returns a tuple of (Model, Alphabet).
|
| 291 |
+
"""
|
| 292 |
+
return load_model_and_alphabet_hub("esm1v_t33_650M_UR90S_1")
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def esm1v_t33_650M_UR90S_1():
|
| 296 |
+
"""33 layer transformer model with 650M params, trained on Uniref90.
|
| 297 |
+
This is model 1 of a 5 model ensemble.
|
| 298 |
+
|
| 299 |
+
Returns a tuple of (Model, Alphabet).
|
| 300 |
+
"""
|
| 301 |
+
return load_model_and_alphabet_hub("esm1v_t33_650M_UR90S_1")
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def esm1v_t33_650M_UR90S_2():
|
| 305 |
+
"""33 layer transformer model with 650M params, trained on Uniref90.
|
| 306 |
+
This is model 2 of a 5 model ensemble.
|
| 307 |
+
|
| 308 |
+
Returns a tuple of (Model, Alphabet).
|
| 309 |
+
"""
|
| 310 |
+
return load_model_and_alphabet_hub("esm1v_t33_650M_UR90S_2")
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
def esm1v_t33_650M_UR90S_3():
|
| 314 |
+
"""33 layer transformer model with 650M params, trained on Uniref90.
|
| 315 |
+
This is model 3 of a 5 model ensemble.
|
| 316 |
+
|
| 317 |
+
Returns a tuple of (Model, Alphabet).
|
| 318 |
+
"""
|
| 319 |
+
return load_model_and_alphabet_hub("esm1v_t33_650M_UR90S_3")
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def esm1v_t33_650M_UR90S_4():
|
| 323 |
+
"""33 layer transformer model with 650M params, trained on Uniref90.
|
| 324 |
+
This is model 4 of a 5 model ensemble.
|
| 325 |
+
|
| 326 |
+
Returns a tuple of (Model, Alphabet).
|
| 327 |
+
"""
|
| 328 |
+
return load_model_and_alphabet_hub("esm1v_t33_650M_UR90S_4")
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def esm1v_t33_650M_UR90S_5():
|
| 332 |
+
"""33 layer transformer model with 650M params, trained on Uniref90.
|
| 333 |
+
This is model 5 of a 5 model ensemble.
|
| 334 |
+
|
| 335 |
+
Returns a tuple of (Model, Alphabet).
|
| 336 |
+
"""
|
| 337 |
+
return load_model_and_alphabet_hub("esm1v_t33_650M_UR90S_5")
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def esm_if1_gvp4_t16_142M_UR50():
|
| 341 |
+
"""Inverse folding model with 142M params, with 4 GVP-GNN layers, 8
|
| 342 |
+
Transformer encoder layers, and 8 Transformer decoder layers, trained on
|
| 343 |
+
CATH structures and 12 million alphafold2 predicted structures from UniRef50
|
| 344 |
+
sequences.
|
| 345 |
+
|
| 346 |
+
Returns a tuple of (Model, Alphabet).
|
| 347 |
+
"""
|
| 348 |
+
return load_model_and_alphabet_hub("esm_if1_gvp4_t16_142M_UR50")
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
def esm2_t6_8M_UR50D():
|
| 352 |
+
"""6 layer ESM-2 model with 8M params, trained on UniRef50.
|
| 353 |
+
|
| 354 |
+
Returns a tuple of (Model, Alphabet).
|
| 355 |
+
"""
|
| 356 |
+
return load_model_and_alphabet_hub("esm2_t6_8M_UR50D")
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def esm2_t12_35M_UR50D():
|
| 360 |
+
"""12 layer ESM-2 model with 35M params, trained on UniRef50.
|
| 361 |
+
|
| 362 |
+
Returns a tuple of (Model, Alphabet).
|
| 363 |
+
"""
|
| 364 |
+
return load_model_and_alphabet_hub("esm2_t12_35M_UR50D")
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def esm2_t30_150M_UR50D():
|
| 368 |
+
"""30 layer ESM-2 model with 150M params, trained on UniRef50.
|
| 369 |
+
|
| 370 |
+
Returns a tuple of (Model, Alphabet).
|
| 371 |
+
"""
|
| 372 |
+
return load_model_and_alphabet_hub("esm2_t30_150M_UR50D")
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
def esm2_t33_650M_UR50D():
|
| 376 |
+
"""33 layer ESM-2 model with 650M params, trained on UniRef50.
|
| 377 |
+
|
| 378 |
+
Returns a tuple of (Model, Alphabet).
|
| 379 |
+
"""
|
| 380 |
+
return load_model_and_alphabet_hub("esm2_t33_650M_UR50D")
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def esm2_t36_3B_UR50D():
|
| 384 |
+
"""36 layer ESM-2 model with 3B params, trained on UniRef50.
|
| 385 |
+
|
| 386 |
+
Returns a tuple of (Model, Alphabet).
|
| 387 |
+
"""
|
| 388 |
+
return load_model_and_alphabet_hub("esm2_t36_3B_UR50D")
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def esm2_t48_15B_UR50D():
|
| 392 |
+
"""48 layer ESM-2 model with 15B params, trained on UniRef50.
|
| 393 |
+
If you have OOM while loading this model, please refer to README
|
| 394 |
+
on how to employ FSDP and ZeRO CPU offloading
|
| 395 |
+
|
| 396 |
+
Returns a tuple of (Model, Alphabet).
|
| 397 |
+
"""
|
| 398 |
+
return load_model_and_alphabet_hub("esm2_t48_15B_UR50D")
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def esmfold_v0():
|
| 402 |
+
"""
|
| 403 |
+
ESMFold v0 model with 3B ESM-2, 48 folding blocks.
|
| 404 |
+
This version was used for the paper (Lin et al, 2022). It was trained
|
| 405 |
+
on all PDB chains until 2020-05, to ensure temporal holdout with CASP14
|
| 406 |
+
and the CAMEO validation and test set reported there.
|
| 407 |
+
"""
|
| 408 |
+
import model.esm.esmfold.v1.pretrained
|
| 409 |
+
return esm.esmfold.v1.pretrained.esmfold_v0()
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
def esmfold_v1():
|
| 413 |
+
"""
|
| 414 |
+
ESMFold v1 model using 3B ESM-2, 48 folding blocks.
|
| 415 |
+
ESMFold provides fast high accuracy atomic level structure prediction
|
| 416 |
+
directly from the individual sequence of a protein. ESMFold uses the ESM2
|
| 417 |
+
protein language model to extract meaningful representations from the
|
| 418 |
+
protein sequence.
|
| 419 |
+
"""
|
| 420 |
+
import model.esm.esmfold.v1.pretrained
|
| 421 |
+
return esm.esmfold.v1.pretrained.esmfold_v1()
|
| 422 |
+
|
| 423 |
+
def esmfold_structure_module_only_8M():
|
| 424 |
+
"""
|
| 425 |
+
ESMFold baseline model using 8M ESM-2, 0 folding blocks.
|
| 426 |
+
ESM-2 here is trained out to 500K updates.
|
| 427 |
+
This is a model designed to test the capabilities of the language model
|
| 428 |
+
when ablated for number of parameters in the language model.
|
| 429 |
+
See table S1 in (Lin et al, 2022).
|
| 430 |
+
"""
|
| 431 |
+
import model.esm.esmfold.v1.pretrained
|
| 432 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_8M()
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
def esmfold_structure_module_only_8M_270K():
|
| 436 |
+
"""
|
| 437 |
+
ESMFold baseline model using 8M ESM-2, 0 folding blocks.
|
| 438 |
+
ESM-2 here is trained out to 270K updates.
|
| 439 |
+
This is a model designed to test the capabilities of the language model
|
| 440 |
+
when ablated for number of parameters in the language model.
|
| 441 |
+
See table S1 in (Lin et al, 2022).
|
| 442 |
+
"""
|
| 443 |
+
import model.esm.esmfold.v1.pretrained
|
| 444 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_8M_270K()
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
def esmfold_structure_module_only_35M():
|
| 448 |
+
"""
|
| 449 |
+
ESMFold baseline model using 35M ESM-2, 0 folding blocks.
|
| 450 |
+
ESM-2 here is trained out to 500K updates.
|
| 451 |
+
This is a model designed to test the capabilities of the language model
|
| 452 |
+
when ablated for number of parameters in the language model.
|
| 453 |
+
See table S1 in (Lin et al, 2022).
|
| 454 |
+
"""
|
| 455 |
+
import model.esm.esmfold.v1.pretrained
|
| 456 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_35M()
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
def esmfold_structure_module_only_35M_270K():
|
| 460 |
+
"""
|
| 461 |
+
ESMFold baseline model using 35M ESM-2, 0 folding blocks.
|
| 462 |
+
ESM-2 here is trained out to 270K updates.
|
| 463 |
+
This is a model designed to test the capabilities of the language model
|
| 464 |
+
when ablated for number of parameters in the language model.
|
| 465 |
+
See table S1 in (Lin et al, 2022).
|
| 466 |
+
"""
|
| 467 |
+
import model.esm.esmfold.v1.pretrained
|
| 468 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_35M_270K()
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
def esmfold_structure_module_only_150M():
|
| 472 |
+
"""
|
| 473 |
+
ESMFold baseline model using 150M ESM-2, 0 folding blocks.
|
| 474 |
+
ESM-2 here is trained out to 500K updates.
|
| 475 |
+
This is a model designed to test the capabilities of the language model
|
| 476 |
+
when ablated for number of parameters in the language model.
|
| 477 |
+
See table S1 in (Lin et al, 2022).
|
| 478 |
+
"""
|
| 479 |
+
import model.esm.esmfold.v1.pretrained
|
| 480 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_150M()
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def esmfold_structure_module_only_150M_270K():
|
| 484 |
+
"""
|
| 485 |
+
ESMFold baseline model using 150M ESM-2, 0 folding blocks.
|
| 486 |
+
ESM-2 here is trained out to 270K updates.
|
| 487 |
+
This is a model designed to test the capabilities of the language model
|
| 488 |
+
when ablated for number of parameters in the language model.
|
| 489 |
+
See table S1 in (Lin et al, 2022).
|
| 490 |
+
"""
|
| 491 |
+
import model.esm.esmfold.v1.pretrained
|
| 492 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_150M_270K()
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
def esmfold_structure_module_only_650M():
|
| 496 |
+
"""
|
| 497 |
+
ESMFold baseline model using 650M ESM-2, 0 folding blocks.
|
| 498 |
+
ESM-2 here is trained out to 500K updates.
|
| 499 |
+
This is a model designed to test the capabilities of the language model
|
| 500 |
+
when ablated for number of parameters in the language model.
|
| 501 |
+
See table S1 in (Lin et al, 2022).
|
| 502 |
+
"""
|
| 503 |
+
import model.esm.esmfold.v1.pretrained
|
| 504 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_650M()
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
def esmfold_structure_module_only_650M_270K():
|
| 508 |
+
"""
|
| 509 |
+
ESMFold baseline model using 650M ESM-2, 0 folding blocks.
|
| 510 |
+
ESM-2 here is trained out to 270K updates.
|
| 511 |
+
This is a model designed to test the capabilities of the language model
|
| 512 |
+
when ablated for number of parameters in the language model.
|
| 513 |
+
See table S1 in (Lin et al, 2022).
|
| 514 |
+
"""
|
| 515 |
+
import model.esm.esmfold.v1.pretrained
|
| 516 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_650M_270K()
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
def esmfold_structure_module_only_3B():
|
| 520 |
+
"""
|
| 521 |
+
ESMFold baseline model using 3B ESM-2, 0 folding blocks.
|
| 522 |
+
ESM-2 here is trained out to 500K updates.
|
| 523 |
+
This is a model designed to test the capabilities of the language model
|
| 524 |
+
when ablated for number of parameters in the language model.
|
| 525 |
+
See table S1 in (Lin et al, 2022).
|
| 526 |
+
"""
|
| 527 |
+
import model.esm.esmfold.v1.pretrained
|
| 528 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_3B()
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
def esmfold_structure_module_only_3B_270K():
|
| 532 |
+
"""
|
| 533 |
+
ESMFold baseline model using 3B ESM-2, 0 folding blocks.
|
| 534 |
+
ESM-2 here is trained out to 270K updates.
|
| 535 |
+
This is a model designed to test the capabilities of the language model
|
| 536 |
+
when ablated for number of parameters in the language model.
|
| 537 |
+
See table S1 in (Lin et al, 2022).
|
| 538 |
+
"""
|
| 539 |
+
import model.esm.esmfold.v1.pretrained
|
| 540 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_3B_270K()
|
| 541 |
+
|
| 542 |
+
|
| 543 |
+
def esmfold_structure_module_only_15B():
|
| 544 |
+
"""
|
| 545 |
+
ESMFold baseline model using 15B ESM-2, 0 folding blocks.
|
| 546 |
+
ESM-2 here is trained out to 270K updates.
|
| 547 |
+
The 15B parameter ESM-2 was not trained out to 500K updates
|
| 548 |
+
This is a model designed to test the capabilities of the language model
|
| 549 |
+
when ablated for number of parameters in the language model.
|
| 550 |
+
See table S1 in (Lin et al, 2022).
|
| 551 |
+
"""
|
| 552 |
+
import model.esm.esmfold.v1.pretrained
|
| 553 |
+
return esm.esmfold.v1.pretrained.esmfold_structure_module_only_15B()
|
model/esm/version.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
#
|
| 3 |
+
# This source code is licensed under the MIT license found in the
|
| 4 |
+
# LICENSE file in the root directory of this source tree.
|
| 5 |
+
|
| 6 |
+
version = "2.0.1"
|
model/openfold/__init__.py
ADDED
|
File without changes
|
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 model.openfold.primitives import Linear, LayerNorm
|
| 30 |
+
from model.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 model.openfold.primitives import Linear, LayerNorm
|
| 24 |
+
from model.openfold.dropout import DropoutRowwise, DropoutColumnwise
|
| 25 |
+
from model.openfold.msa import (
|
| 26 |
+
MSARowAttentionWithPairBias,
|
| 27 |
+
MSAColumnAttention,
|
| 28 |
+
MSAColumnGlobalAttention,
|
| 29 |
+
)
|
| 30 |
+
from model.openfold.outer_product_mean import OuterProductMean
|
| 31 |
+
from model.openfold.pair_transition import PairTransition
|
| 32 |
+
from model.openfold.triangular_attention import (
|
| 33 |
+
TriangleAttention,
|
| 34 |
+
TriangleAttentionStartingNode,
|
| 35 |
+
TriangleAttentionEndingNode,
|
| 36 |
+
)
|
| 37 |
+
from model.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 model.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 model.openfold.embedders import (
|
| 30 |
+
InputEmbedder,
|
| 31 |
+
InputEmbedderMultimer,
|
| 32 |
+
RecyclingEmbedder,
|
| 33 |
+
TemplateEmbedder,
|
| 34 |
+
TemplateEmbedderMultimer,
|
| 35 |
+
ExtraMSAEmbedder,
|
| 36 |
+
PreembeddingEmbedder,
|
| 37 |
+
)
|
| 38 |
+
from model.openfold.evoformer import EvoformerStack, ExtraMSAStack
|
| 39 |
+
from model.openfold.heads import AuxiliaryHeads
|
| 40 |
+
from model.openfold.structure_module import StructureModule
|
| 41 |
+
from model.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 model.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 model.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 model.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 model.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 model.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 model.openfold.primitives import LayerNorm, Attention
|
| 24 |
+
from model.openfold.dropout import (
|
| 25 |
+
DropoutRowwise,
|
| 26 |
+
DropoutColumnwise,
|
| 27 |
+
)
|
| 28 |
+
from model.openfold.pair_transition import PairTransition
|
| 29 |
+
from model.openfold.triangular_attention import (
|
| 30 |
+
TriangleAttentionStartingNode,
|
| 31 |
+
TriangleAttentionEndingNode,
|
| 32 |
+
)
|
| 33 |
+
from model.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 model.openfold.dropout import (
|
| 21 |
+
DropoutRowwise,
|
| 22 |
+
DropoutColumnwise,
|
| 23 |
+
)
|
| 24 |
+
from model.openfold.evoformer import (
|
| 25 |
+
EvoformerBlock,
|
| 26 |
+
EvoformerStack,
|
| 27 |
+
)
|
| 28 |
+
from model.openfold.outer_product_mean import OuterProductMean
|
| 29 |
+
from model.openfold.msa import (
|
| 30 |
+
MSARowAttentionWithPairBias,
|
| 31 |
+
MSAColumnAttention,
|
| 32 |
+
MSAColumnGlobalAttention,
|
| 33 |
+
)
|
| 34 |
+
from model.openfold.pair_transition import PairTransition
|
| 35 |
+
from model.openfold.primitives import Attention, GlobalAttention
|
| 36 |
+
from model.openfold.structure_module import (
|
| 37 |
+
InvariantPointAttention,
|
| 38 |
+
BackboneUpdate,
|
| 39 |
+
)
|
| 40 |
+
from model.openfold.template import TemplatePairStackBlock
|
| 41 |
+
from model.openfold.triangular_attention import (
|
| 42 |
+
TriangleAttentionStartingNode,
|
| 43 |
+
TriangleAttentionEndingNode,
|
| 44 |
+
)
|
| 45 |
+
from model.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 model.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 model.openfold.primitives import Linear, LayerNorm
|
| 24 |
+
from onescience.utils.openfold.chunk_utils import chunk_layer
|
| 25 |
+
from onescience.utils.openfold.precision_utils import is_fp16_enabled
|
| 26 |
+
from onescience.utils.openfold.tensor_utils import add, permute_final_dims
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class BaseTriangleMultiplicativeUpdate(nn.Module, ABC):
|
| 30 |
+
"""
|
| 31 |
+
Implements Algorithms 11 and 12.
|
| 32 |
+
"""
|
| 33 |
+
@abstractmethod
|
| 34 |
+
def __init__(self, c_z, c_hidden, _outgoing, bias=True):
|
| 35 |
+
"""
|
| 36 |
+
Args:
|
| 37 |
+
c_z:
|
| 38 |
+
Input channel dimension
|
| 39 |
+
c:
|
| 40 |
+
Hidden channel dimension
|
| 41 |
+
"""
|
| 42 |
+
super(BaseTriangleMultiplicativeUpdate, self).__init__()
|
| 43 |
+
self.c_z = c_z
|
| 44 |
+
self.c_hidden = c_hidden
|
| 45 |
+
self._outgoing = _outgoing
|
| 46 |
+
self.bias = bias
|
| 47 |
+
|
| 48 |
+
self.linear_g = Linear(self.c_z, self.c_z, bias=bias, init="gating")
|
| 49 |
+
self.linear_z = Linear(self.c_hidden, self.c_z, bias=bias, init="final")
|
| 50 |
+
|
| 51 |
+
self.layer_norm_in = LayerNorm(self.c_z)
|
| 52 |
+
self.layer_norm_out = LayerNorm(self.c_hidden)
|
| 53 |
+
|
| 54 |
+
self.sigmoid = nn.Sigmoid()
|
| 55 |
+
|
| 56 |
+
def _combine_projections(self,
|
| 57 |
+
a: torch.Tensor,
|
| 58 |
+
b: torch.Tensor,
|
| 59 |
+
_inplace_chunk_size: Optional[int] = None
|
| 60 |
+
) -> torch.Tensor:
|
| 61 |
+
if(self._outgoing):
|
| 62 |
+
a = permute_final_dims(a, (2, 0, 1))
|
| 63 |
+
b = permute_final_dims(b, (2, 1, 0))
|
| 64 |
+
else:
|
| 65 |
+
a = permute_final_dims(a, (2, 1, 0))
|
| 66 |
+
b = permute_final_dims(b, (2, 0, 1))
|
| 67 |
+
|
| 68 |
+
if(_inplace_chunk_size is not None):
|
| 69 |
+
# To be replaced by torch vmap
|
| 70 |
+
for i in range(0, a.shape[-3], _inplace_chunk_size):
|
| 71 |
+
a_chunk = a[..., i: i + _inplace_chunk_size, :, :]
|
| 72 |
+
b_chunk = b[..., i: i + _inplace_chunk_size, :, :]
|
| 73 |
+
a[..., i: i + _inplace_chunk_size, :, :] = (
|
| 74 |
+
torch.matmul(
|
| 75 |
+
a_chunk,
|
| 76 |
+
b_chunk,
|
| 77 |
+
)
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
p = a
|
| 81 |
+
else:
|
| 82 |
+
p = torch.matmul(a, b)
|
| 83 |
+
|
| 84 |
+
return permute_final_dims(p, (1, 2, 0))
|
| 85 |
+
|
| 86 |
+
@abstractmethod
|
| 87 |
+
def forward(self,
|
| 88 |
+
z: torch.Tensor,
|
| 89 |
+
mask: Optional[torch.Tensor] = None,
|
| 90 |
+
inplace_safe: bool = False,
|
| 91 |
+
_add_with_inplace: bool = False
|
| 92 |
+
) -> torch.Tensor:
|
| 93 |
+
"""
|
| 94 |
+
Args:
|
| 95 |
+
x:
|
| 96 |
+
[*, N_res, N_res, C_z] input tensor
|
| 97 |
+
mask:
|
| 98 |
+
[*, N_res, N_res] input mask
|
| 99 |
+
Returns:
|
| 100 |
+
[*, N_res, N_res, C_z] output tensor
|
| 101 |
+
"""
|
| 102 |
+
pass
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class TriangleMultiplicativeUpdate(BaseTriangleMultiplicativeUpdate):
|
| 106 |
+
"""
|
| 107 |
+
Implements Algorithms 11 and 12.
|
| 108 |
+
"""
|
| 109 |
+
def __init__(self, c_z, c_hidden, _outgoing=True, bias: bool=True):
|
| 110 |
+
"""
|
| 111 |
+
Args:
|
| 112 |
+
c_z:
|
| 113 |
+
Input channel dimension
|
| 114 |
+
c:
|
| 115 |
+
Hidden channel dimension
|
| 116 |
+
"""
|
| 117 |
+
super(TriangleMultiplicativeUpdate, self).__init__(c_z=c_z,
|
| 118 |
+
c_hidden=c_hidden,
|
| 119 |
+
_outgoing=_outgoing,
|
| 120 |
+
bias=bias
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
self.linear_a_p = Linear(self.c_z, self.c_hidden, bias=bias)
|
| 124 |
+
self.linear_a_g = Linear(self.c_z, self.c_hidden, bias=bias, init="gating")
|
| 125 |
+
self.linear_b_p = Linear(self.c_z, self.c_hidden, bias=bias,)
|
| 126 |
+
self.linear_b_g = Linear(self.c_z, self.c_hidden, bias=bias, init="gating")
|
| 127 |
+
|
| 128 |
+
def _inference_forward(self,
|
| 129 |
+
z: torch.Tensor,
|
| 130 |
+
mask: Optional[torch.Tensor] = None,
|
| 131 |
+
inplace_chunk_size: Optional[int] = None,
|
| 132 |
+
with_add: bool = True,
|
| 133 |
+
):
|
| 134 |
+
"""
|
| 135 |
+
Args:
|
| 136 |
+
z:
|
| 137 |
+
A [*, N, N, C_z] pair representation
|
| 138 |
+
mask:
|
| 139 |
+
A [*, N, N] pair mask
|
| 140 |
+
inplace_chunk_size:
|
| 141 |
+
Size of chunks used in the main computation. Increase to trade
|
| 142 |
+
memory for speed.
|
| 143 |
+
with_add:
|
| 144 |
+
If True, z is overwritten with (z + update). Otherwise, it is
|
| 145 |
+
overwritten with (update).
|
| 146 |
+
Returns:
|
| 147 |
+
A reference to the overwritten z
|
| 148 |
+
|
| 149 |
+
More memory-efficient, inference-only version of the forward function.
|
| 150 |
+
Uses in-place operations, fusion of the addition that happens after
|
| 151 |
+
this module in the Evoformer, a smidge of recomputation, and
|
| 152 |
+
a cache of overwritten values to lower peak memory consumption of this
|
| 153 |
+
module from 5x the size of the input tensor z to 2.5x its size. Useful
|
| 154 |
+
for inference on extremely long sequences.
|
| 155 |
+
|
| 156 |
+
It works as follows. We will make reference to variables used in the
|
| 157 |
+
default forward implementation below. Naively, triangle multiplication
|
| 158 |
+
attention requires the manifestation of 5 tensors the size of z:
|
| 159 |
+
1) z, the "square" input tensor, 2) a, the first projection of z,
|
| 160 |
+
3) b, the second projection of b, 4) g, a z-sized mask, and 5) a
|
| 161 |
+
z-sized tensor for intermediate computations. For large N, this is
|
| 162 |
+
prohibitively expensive; for N=4000, for example, z is more than 8GB
|
| 163 |
+
alone. To avoid this problem, we compute b, g, and all intermediate
|
| 164 |
+
tensors in small chunks, noting that the chunks required to compute a
|
| 165 |
+
chunk of the output depend only on the tensor a and corresponding
|
| 166 |
+
vertical and horizontal chunks of z. This suggests an algorithm that
|
| 167 |
+
loops over pairs of chunks of z: hereafter "columns" and "rows" of
|
| 168 |
+
z, even though each "column" and "row" in fact contains
|
| 169 |
+
inplace_chunk_size contiguous true columns and rows of z. Writing
|
| 170 |
+
output chunks to a new tensor would bring total memory consumption
|
| 171 |
+
down to 3x the size of z. However, more memory can be saved by writing
|
| 172 |
+
output chunks directly to z in-place. WLOG, we choose to write output
|
| 173 |
+
chunks vertically, overwriting the ith "column" of z at the end of
|
| 174 |
+
the ith iteration of the main loop. Despite this overwriting, the
|
| 175 |
+
ith column is always one column ahead of previously overwritten columns
|
| 176 |
+
and can be recovered directly from z. After the first iteration,
|
| 177 |
+
however, the ith row of z is always at least partially overwritten. For
|
| 178 |
+
this reason, we introduce the z-cache, a tensor one-half the size of
|
| 179 |
+
z. The z-cache initially contains the left half (2nd and 3rd quadrants)
|
| 180 |
+
of z. For 0 < i < N/2, the missing left part of the ith row of z is
|
| 181 |
+
recovered from this cache at the beginning of the ith iteration. Once i
|
| 182 |
+
exceeds n/2, the cache is "reoriented" to encompass the 3rd and 4th
|
| 183 |
+
quadrants of z instead. Though the 3rd quadrant of the original z is
|
| 184 |
+
entirely overwritten at this point, it can be recovered from the z-cache
|
| 185 |
+
itself. Thereafter, the ith row of z can be recovered in its entirety
|
| 186 |
+
from the reoriented z-cache. After the final iteration, z has been
|
| 187 |
+
completely overwritten and contains the triangular multiplicative
|
| 188 |
+
update. If with_add is True, it instead contains the sum of z and the
|
| 189 |
+
triangular multiplicative update. In either case, peak memory
|
| 190 |
+
consumption is just 2.5x the size of z, disregarding memory used for
|
| 191 |
+
chunks and other small variables.
|
| 192 |
+
"""
|
| 193 |
+
if mask is None:
|
| 194 |
+
mask = z.new_ones(z.shape[:-1])
|
| 195 |
+
|
| 196 |
+
mask = mask.unsqueeze(-1)
|
| 197 |
+
|
| 198 |
+
def compute_projection_helper(pair, mask, a=True):
|
| 199 |
+
if(a):
|
| 200 |
+
linear_g = self.linear_a_g
|
| 201 |
+
linear_p = self.linear_a_p
|
| 202 |
+
else:
|
| 203 |
+
linear_g = self.linear_b_g
|
| 204 |
+
linear_p = self.linear_b_p
|
| 205 |
+
|
| 206 |
+
pair = self.layer_norm_in(pair)
|
| 207 |
+
p = linear_g(pair)
|
| 208 |
+
p.sigmoid_()
|
| 209 |
+
p *= linear_p(pair)
|
| 210 |
+
p *= mask
|
| 211 |
+
p = permute_final_dims(p, (2, 0, 1))
|
| 212 |
+
return p
|
| 213 |
+
|
| 214 |
+
def compute_projection(pair, mask, a=True, chunked=True):
|
| 215 |
+
need_transpose = self._outgoing ^ a
|
| 216 |
+
if(not chunked):
|
| 217 |
+
p = compute_projection_helper(pair, mask, a)
|
| 218 |
+
if(need_transpose):
|
| 219 |
+
p = p.transpose(-1, -2)
|
| 220 |
+
else:
|
| 221 |
+
# This computation is chunked so as not to exceed our 2.5x
|
| 222 |
+
# budget with a large intermediate tensor
|
| 223 |
+
linear_g = self.linear_a_g if a else self.linear_b_g
|
| 224 |
+
#c = linear_g.bias.shape[-1]
|
| 225 |
+
if self.bias:
|
| 226 |
+
c = linear_g.bias.shape[-1]
|
| 227 |
+
else:
|
| 228 |
+
c = linear_g.weight.shape[0]
|
| 229 |
+
out_shape = pair.shape[:-3] + (c,) + pair.shape[-3:-1]
|
| 230 |
+
p = pair.new_zeros(out_shape)
|
| 231 |
+
for i in range(0, pair.shape[-3], inplace_chunk_size):
|
| 232 |
+
pair_chunk = pair[..., i: i + inplace_chunk_size, :, :]
|
| 233 |
+
mask_chunk = mask[..., i: i + inplace_chunk_size, :, :]
|
| 234 |
+
pair_chunk = compute_projection_helper(
|
| 235 |
+
pair[..., i: i + inplace_chunk_size, :, :],
|
| 236 |
+
mask[..., i: i + inplace_chunk_size, :, :],
|
| 237 |
+
a,
|
| 238 |
+
)
|
| 239 |
+
if(need_transpose):
|
| 240 |
+
pair_chunk = pair_chunk.transpose(-1, -2)
|
| 241 |
+
p[..., i: i + inplace_chunk_size] = pair_chunk
|
| 242 |
+
else:
|
| 243 |
+
p[..., i: i + inplace_chunk_size, :] = pair_chunk
|
| 244 |
+
|
| 245 |
+
del pair_chunk
|
| 246 |
+
|
| 247 |
+
return p
|
| 248 |
+
|
| 249 |
+
# We start by fully manifesting a. In addition to the input, this
|
| 250 |
+
# brings total memory consumption to 2x z (disregarding size of chunks)
|
| 251 |
+
# [*, N, N, c]
|
| 252 |
+
a = compute_projection(z, mask, True, chunked=True)
|
| 253 |
+
#if bias==True:
|
| 254 |
+
# a = compute_projection(z, mask, True, True, chunked=True)
|
| 255 |
+
#else:
|
| 256 |
+
# a = compute_projection(z, mask, False, True, chunked=True)
|
| 257 |
+
|
| 258 |
+
if(inplace_chunk_size is not None):
|
| 259 |
+
n = a.shape[-1]
|
| 260 |
+
half_n = n // 2 + n % 2
|
| 261 |
+
row_dim = -3
|
| 262 |
+
col_dim = -2
|
| 263 |
+
b_chunk_dim = row_dim if self._outgoing else col_dim
|
| 264 |
+
|
| 265 |
+
def empty_slicer(t):
|
| 266 |
+
return [slice(None) for _ in t.shape]
|
| 267 |
+
|
| 268 |
+
def slice_tensor(t, start, end, dim):
|
| 269 |
+
# Slices start:end from the dim dimension of t
|
| 270 |
+
s = empty_slicer(t)
|
| 271 |
+
s[dim] = slice(start, end)
|
| 272 |
+
return t[s]
|
| 273 |
+
|
| 274 |
+
def flip_z_cache_(z_cache, z):
|
| 275 |
+
# "Reorient" the z_cache (see below), filling it with quadrants
|
| 276 |
+
# 3---recovered from the z_cache---and 4---recovered from z---
|
| 277 |
+
# of the input tensor z.
|
| 278 |
+
quadrant_3 = slice_tensor(
|
| 279 |
+
z_cache, half_n, None, row_dim
|
| 280 |
+
)
|
| 281 |
+
z_cache = z_cache.transpose(row_dim, col_dim)
|
| 282 |
+
|
| 283 |
+
# If n is odd, we need to shrink the z_cache by one row
|
| 284 |
+
z_cache = z_cache[..., :(n // 2), :, :]
|
| 285 |
+
|
| 286 |
+
# Move the 3rd quadrant of z into the
|
| 287 |
+
first_half_slicer = empty_slicer(z_cache)
|
| 288 |
+
first_half_slicer[col_dim] = slice(0, half_n)
|
| 289 |
+
z_cache[first_half_slicer] = quadrant_3
|
| 290 |
+
|
| 291 |
+
# Get the fourth quadrant of z
|
| 292 |
+
quadrant_4 = slice_tensor(z, half_n, None, row_dim)
|
| 293 |
+
quadrant_4 = slice_tensor(
|
| 294 |
+
quadrant_4, half_n, None, col_dim
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
# Insert said quadrant into the rotated z-cache
|
| 298 |
+
quadrant_3_slicer = empty_slicer(z_cache)
|
| 299 |
+
quadrant_3_slicer[col_dim] = slice(half_n, None)
|
| 300 |
+
|
| 301 |
+
z_cache[quadrant_3_slicer] = quadrant_4
|
| 302 |
+
|
| 303 |
+
return z_cache
|
| 304 |
+
|
| 305 |
+
# Initialize the z cache to the left half of z.
|
| 306 |
+
z_cache_shape = list(z.shape)
|
| 307 |
+
z_cache_shape[col_dim] = half_n
|
| 308 |
+
z_cache = z.new_zeros(z_cache_shape)
|
| 309 |
+
z_cache_slicer = empty_slicer(z_cache)
|
| 310 |
+
z_cache_slicer[col_dim] = slice(0, half_n)
|
| 311 |
+
z_cache.copy_(z[z_cache_slicer])
|
| 312 |
+
z_cache_rotated = False
|
| 313 |
+
|
| 314 |
+
# We need to reorient the z-cache at the halfway point, and we
|
| 315 |
+
# don't want a single chunk to straddle that point. We contract one
|
| 316 |
+
# of the chunks in the middle to address that problem.
|
| 317 |
+
i_range = list(range(0, half_n, inplace_chunk_size))
|
| 318 |
+
initial_offsets = [
|
| 319 |
+
i_2 - i_1 for i_1, i_2 in zip(i_range, i_range[1:] + [half_n])
|
| 320 |
+
]
|
| 321 |
+
after_half = list(range(half_n, n, inplace_chunk_size))
|
| 322 |
+
after_half_offsets = [inplace_chunk_size for _ in after_half]
|
| 323 |
+
combined_range_with_offsets = zip(
|
| 324 |
+
i_range + after_half, initial_offsets + after_half_offsets
|
| 325 |
+
)
|
| 326 |
+
for i, offset in combined_range_with_offsets:
|
| 327 |
+
if(not z_cache_rotated and i >= half_n):
|
| 328 |
+
z_cache = flip_z_cache_(z_cache, z)
|
| 329 |
+
z_cache_rotated = True
|
| 330 |
+
|
| 331 |
+
z_chunk_b = slice_tensor(
|
| 332 |
+
z, i, i + offset, b_chunk_dim,
|
| 333 |
+
)
|
| 334 |
+
mask_chunk = slice_tensor(
|
| 335 |
+
mask, i, i + offset, b_chunk_dim,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
z_chunk_b = z_chunk_b.clone()
|
| 339 |
+
if(b_chunk_dim == col_dim):
|
| 340 |
+
z_chunk_b = slice_tensor(
|
| 341 |
+
z, i, i + offset, col_dim
|
| 342 |
+
)
|
| 343 |
+
else: # b_chunk_dim == row_dim
|
| 344 |
+
# In this case, the b-dimension (b_chunk_dim) is partially
|
| 345 |
+
# overwritten at the end of each iteration. We need to
|
| 346 |
+
# restore the missing component from the z-cache.
|
| 347 |
+
if(not z_cache_rotated):
|
| 348 |
+
z_chunk_slicer = empty_slicer(z_chunk_b)
|
| 349 |
+
z_chunk_slicer[col_dim] = slice(0, half_n)
|
| 350 |
+
z_chunk_b[z_chunk_slicer] = slice_tensor(
|
| 351 |
+
z_cache, i, i + offset, row_dim,
|
| 352 |
+
)
|
| 353 |
+
else:
|
| 354 |
+
z_cache_offset = i - half_n
|
| 355 |
+
z_chunk_b = slice_tensor(
|
| 356 |
+
z_cache,
|
| 357 |
+
z_cache_offset, z_cache_offset + offset,
|
| 358 |
+
row_dim
|
| 359 |
+
)
|
| 360 |
+
b_chunk = compute_projection(z_chunk_b, mask_chunk, a=False, chunked=False)
|
| 361 |
+
#if bias==True:
|
| 362 |
+
# b_chunk = compute_projection(
|
| 363 |
+
# z_chunk_b, mask_chunk, True, a=False, chunked=False
|
| 364 |
+
# )
|
| 365 |
+
#else:
|
| 366 |
+
# b_chunk = compute_projection(z_chunk_b, mask_chunk, False, a=False, chunked=False)
|
| 367 |
+
del z_chunk_b
|
| 368 |
+
|
| 369 |
+
x_chunk = torch.matmul(
|
| 370 |
+
a,
|
| 371 |
+
b_chunk,
|
| 372 |
+
)
|
| 373 |
+
x_chunk = permute_final_dims(x_chunk, (1, 2, 0))
|
| 374 |
+
x_chunk = self.layer_norm_out(x_chunk)
|
| 375 |
+
x_chunk = self.linear_z(x_chunk)
|
| 376 |
+
|
| 377 |
+
# The g dimension (col_dim) is parallel to and ahead of the
|
| 378 |
+
# overwrites in z. We can extract the g chunk normally.
|
| 379 |
+
z_chunk_g = slice_tensor(
|
| 380 |
+
z, i, i + offset, col_dim
|
| 381 |
+
)
|
| 382 |
+
g_chunk = self.linear_g(self.layer_norm_in(z_chunk_g))
|
| 383 |
+
g_chunk.sigmoid_()
|
| 384 |
+
del z_chunk_g
|
| 385 |
+
|
| 386 |
+
x_chunk *= g_chunk
|
| 387 |
+
|
| 388 |
+
# Write the columns into z in-place
|
| 389 |
+
z_slicer = empty_slicer(z)
|
| 390 |
+
z_slicer[col_dim] = slice(i, i + offset)
|
| 391 |
+
if(with_add):
|
| 392 |
+
z[z_slicer] += x_chunk
|
| 393 |
+
else:
|
| 394 |
+
z[z_slicer] = x_chunk
|
| 395 |
+
else:
|
| 396 |
+
b = compute_projection(z, mask, False, False)
|
| 397 |
+
#if bias==True:
|
| 398 |
+
# b = compute_projection(z, mask, True, False, False)
|
| 399 |
+
#else:
|
| 400 |
+
# b = compute_projection(z, mask, False, False, False)
|
| 401 |
+
x = torch.matmul(a, b)
|
| 402 |
+
x = self.layer_norm_out(x)
|
| 403 |
+
x = self.linear_z(x)
|
| 404 |
+
g = self.linear_g(z)
|
| 405 |
+
g.sigmoid_()
|
| 406 |
+
x *= g
|
| 407 |
+
if(with_add):
|
| 408 |
+
z += x
|
| 409 |
+
else:
|
| 410 |
+
z = x
|
| 411 |
+
|
| 412 |
+
return z
|
| 413 |
+
|
| 414 |
+
def forward(self,
|
| 415 |
+
z: torch.Tensor,
|
| 416 |
+
mask: Optional[torch.Tensor] = None,
|
| 417 |
+
inplace_safe: bool = False,
|
| 418 |
+
_add_with_inplace: bool = False,
|
| 419 |
+
_inplace_chunk_size: Optional[int] = 256,
|
| 420 |
+
) -> torch.Tensor:
|
| 421 |
+
"""
|
| 422 |
+
Args:
|
| 423 |
+
x:
|
| 424 |
+
[*, N_res, N_res, C_z] input tensor
|
| 425 |
+
mask:
|
| 426 |
+
[*, N_res, N_res] input mask
|
| 427 |
+
Returns:
|
| 428 |
+
[*, N_res, N_res, C_z] output tensor
|
| 429 |
+
"""
|
| 430 |
+
if(inplace_safe):
|
| 431 |
+
x = self._inference_forward(
|
| 432 |
+
z,
|
| 433 |
+
mask,
|
| 434 |
+
inplace_chunk_size=_inplace_chunk_size,
|
| 435 |
+
with_add=_add_with_inplace,
|
| 436 |
+
)
|
| 437 |
+
return x
|
| 438 |
+
|
| 439 |
+
if mask is None:
|
| 440 |
+
mask = z.new_ones(z.shape[:-1])
|
| 441 |
+
|
| 442 |
+
mask = mask.unsqueeze(-1)
|
| 443 |
+
|
| 444 |
+
z = self.layer_norm_in(z)
|
| 445 |
+
a = mask
|
| 446 |
+
a = a * self.sigmoid(self.linear_a_g(z))
|
| 447 |
+
a = a * self.linear_a_p(z)
|
| 448 |
+
b = mask
|
| 449 |
+
b = b * self.sigmoid(self.linear_b_g(z))
|
| 450 |
+
b = b * self.linear_b_p(z)
|
| 451 |
+
|
| 452 |
+
# Prevents overflow of torch.matmul in combine projections in
|
| 453 |
+
# reduced-precision modes
|
| 454 |
+
a_std = a.std()
|
| 455 |
+
b_std = b.std()
|
| 456 |
+
if(is_fp16_enabled() and a_std != 0. and b_std != 0.):
|
| 457 |
+
a = a / a.std()
|
| 458 |
+
b = b / b.std()
|
| 459 |
+
|
| 460 |
+
if(is_fp16_enabled()):
|
| 461 |
+
with torch.cuda.amp.autocast(enabled=False):
|
| 462 |
+
x = self._combine_projections(a.float(), b.float())
|
| 463 |
+
else:
|
| 464 |
+
x = self._combine_projections(a, b)
|
| 465 |
+
|
| 466 |
+
del a, b
|
| 467 |
+
x = self.layer_norm_out(x)
|
| 468 |
+
x = self.linear_z(x)
|
| 469 |
+
g = self.sigmoid(self.linear_g(z))
|
| 470 |
+
x = x * g
|
| 471 |
+
|
| 472 |
+
return x
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
class TriangleMultiplicationOutgoing(TriangleMultiplicativeUpdate):
|
| 476 |
+
"""
|
| 477 |
+
Implements Algorithm 11.
|
| 478 |
+
"""
|
| 479 |
+
__init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=True)
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
class TriangleMultiplicationIncoming(TriangleMultiplicativeUpdate):
|
| 483 |
+
"""
|
| 484 |
+
Implements Algorithm 12.
|
| 485 |
+
"""
|
| 486 |
+
__init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=False)
|
| 487 |
+
|
| 488 |
+
class ProtenixTriangleMultiplicationOutgoing(TriangleMultiplicativeUpdate):
|
| 489 |
+
"""
|
| 490 |
+
Implements Algorithm 11.
|
| 491 |
+
"""
|
| 492 |
+
__init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=True, bias=False)
|
| 493 |
+
|
| 494 |
+
class ProtenixTriangleMultiplicationIncoming(TriangleMultiplicativeUpdate):
|
| 495 |
+
"""
|
| 496 |
+
Implements Algorithm 12.
|
| 497 |
+
"""
|
| 498 |
+
__init__ = partialmethod(TriangleMultiplicativeUpdate.__init__, _outgoing=False, bias=False)
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
class FusedTriangleMultiplicativeUpdate(BaseTriangleMultiplicativeUpdate):
|
| 502 |
+
"""
|
| 503 |
+
Implements Algorithms 11 and 12.
|
| 504 |
+
"""
|
| 505 |
+
|
| 506 |
+
def __init__(self, c_z, c_hidden, _outgoing=True):
|
| 507 |
+
"""
|
| 508 |
+
Args:
|
| 509 |
+
c_z:
|
| 510 |
+
Input channel dimension
|
| 511 |
+
c:
|
| 512 |
+
Hidden channel dimension
|
| 513 |
+
"""
|
| 514 |
+
super(FusedTriangleMultiplicativeUpdate, self).__init__(c_z=c_z,
|
| 515 |
+
c_hidden=c_hidden,
|
| 516 |
+
_outgoing=_outgoing)
|
| 517 |
+
|
| 518 |
+
self.linear_ab_p = Linear(self.c_z, self.c_hidden * 2)
|
| 519 |
+
self.linear_ab_g = Linear(self.c_z, self.c_hidden * 2, init="gating")
|
| 520 |
+
|
| 521 |
+
def _inference_forward(self,
|
| 522 |
+
z: torch.Tensor,
|
| 523 |
+
mask: Optional[torch.Tensor] = None,
|
| 524 |
+
_inplace_chunk_size: Optional[int] = None,
|
| 525 |
+
with_add: bool = True,
|
| 526 |
+
):
|
| 527 |
+
"""
|
| 528 |
+
Args:
|
| 529 |
+
z:
|
| 530 |
+
A [*, N, N, C_z] pair representation
|
| 531 |
+
mask:
|
| 532 |
+
A [*, N, N] pair mask
|
| 533 |
+
with_add:
|
| 534 |
+
If True, z is overwritten with (z + update). Otherwise, it is
|
| 535 |
+
overwritten with (update).
|
| 536 |
+
Returns:
|
| 537 |
+
A reference to the overwritten z
|
| 538 |
+
"""
|
| 539 |
+
if mask is None:
|
| 540 |
+
mask = z.new_ones(z.shape[:-1])
|
| 541 |
+
|
| 542 |
+
mask = mask.unsqueeze(-1)
|
| 543 |
+
|
| 544 |
+
def compute_projection_helper(pair, mask):
|
| 545 |
+
p = self.linear_ab_g(pair)
|
| 546 |
+
p.sigmoid_()
|
| 547 |
+
p *= self.linear_ab_p(pair)
|
| 548 |
+
p *= mask
|
| 549 |
+
|
| 550 |
+
return p
|
| 551 |
+
|
| 552 |
+
def compute_projection(pair, mask):
|
| 553 |
+
p = compute_projection_helper(pair, mask)
|
| 554 |
+
left = p[..., :self.c_hidden]
|
| 555 |
+
right = p[..., self.c_hidden:]
|
| 556 |
+
|
| 557 |
+
return left, right
|
| 558 |
+
|
| 559 |
+
z_norm_in = self.layer_norm_in(z)
|
| 560 |
+
a, b = compute_projection(z_norm_in, mask)
|
| 561 |
+
x = self._combine_projections(a, b, _inplace_chunk_size=_inplace_chunk_size)
|
| 562 |
+
x = self.layer_norm_out(x)
|
| 563 |
+
x = self.linear_z(x)
|
| 564 |
+
g = self.linear_g(z_norm_in)
|
| 565 |
+
g.sigmoid_()
|
| 566 |
+
x *= g
|
| 567 |
+
if (with_add):
|
| 568 |
+
z += x
|
| 569 |
+
else:
|
| 570 |
+
z = x
|
| 571 |
+
|
| 572 |
+
return z
|
| 573 |
+
|
| 574 |
+
def forward(self,
|
| 575 |
+
z: torch.Tensor,
|
| 576 |
+
mask: Optional[torch.Tensor] = None,
|
| 577 |
+
inplace_safe: bool = False,
|
| 578 |
+
_add_with_inplace: bool = False,
|
| 579 |
+
_inplace_chunk_size: Optional[int] = 256
|
| 580 |
+
) -> torch.Tensor:
|
| 581 |
+
"""
|
| 582 |
+
Args:
|
| 583 |
+
x:
|
| 584 |
+
[*, N_res, N_res, C_z] input tensor
|
| 585 |
+
mask:
|
| 586 |
+
[*, N_res, N_res] input mask
|
| 587 |
+
Returns:
|
| 588 |
+
[*, N_res, N_res, C_z] output tensor
|
| 589 |
+
"""
|
| 590 |
+
if (inplace_safe):
|
| 591 |
+
x = self._inference_forward(
|
| 592 |
+
z,
|
| 593 |
+
mask,
|
| 594 |
+
_inplace_chunk_size=_inplace_chunk_size,
|
| 595 |
+
with_add=_add_with_inplace,
|
| 596 |
+
)
|
| 597 |
+
return x
|
| 598 |
+
|
| 599 |
+
if mask is None:
|
| 600 |
+
mask = z.new_ones(z.shape[:-1])
|
| 601 |
+
|
| 602 |
+
mask = mask.unsqueeze(-1)
|
| 603 |
+
|
| 604 |
+
z = self.layer_norm_in(z)
|
| 605 |
+
ab = mask
|
| 606 |
+
ab = ab * self.sigmoid(self.linear_ab_g(z))
|
| 607 |
+
ab = ab * self.linear_ab_p(z)
|
| 608 |
+
|
| 609 |
+
a = ab[..., :self.c_hidden]
|
| 610 |
+
b = ab[..., self.c_hidden:]
|
| 611 |
+
|
| 612 |
+
# Prevents overflow of torch.matmul in combine projections in
|
| 613 |
+
# reduced-precision modes
|
| 614 |
+
a_std = a.std()
|
| 615 |
+
b_std = b.std()
|
| 616 |
+
if (is_fp16_enabled() and a_std != 0. and b_std != 0.):
|
| 617 |
+
a = a / a.std()
|
| 618 |
+
b = b / b.std()
|
| 619 |
+
|
| 620 |
+
if (is_fp16_enabled()):
|
| 621 |
+
with torch.cuda.amp.autocast(enabled=False):
|
| 622 |
+
x = self._combine_projections(a.float(), b.float())
|
| 623 |
+
else:
|
| 624 |
+
x = self._combine_projections(a, b)
|
| 625 |
+
|
| 626 |
+
del a, b
|
| 627 |
+
x = self.layer_norm_out(x)
|
| 628 |
+
x = self.linear_z(x)
|
| 629 |
+
g = self.sigmoid(self.linear_g(z))
|
| 630 |
+
x = x * g
|
| 631 |
+
|
| 632 |
+
return x
|
| 633 |
+
|
| 634 |
+
|
| 635 |
+
class FusedTriangleMultiplicationOutgoing(FusedTriangleMultiplicativeUpdate):
|
| 636 |
+
"""
|
| 637 |
+
Implements Algorithm 11.
|
| 638 |
+
"""
|
| 639 |
+
__init__ = partialmethod(FusedTriangleMultiplicativeUpdate.__init__, _outgoing=True)
|
| 640 |
+
|
| 641 |
+
|
| 642 |
+
class FusedTriangleMultiplicationIncoming(FusedTriangleMultiplicativeUpdate):
|
| 643 |
+
"""
|
| 644 |
+
Implements Algorithm 12.
|
| 645 |
+
"""
|
| 646 |
+
__init__ = partialmethod(FusedTriangleMultiplicativeUpdate.__init__, _outgoing=False)
|
| 647 |
+
|
weight/esm-main/scripts/atlas/v0/full/tarballs/tm_.90_1_plddt_.70_.80.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_.90_1_plddt_.70_.80_00.tar.gz
|
weight/esm-main/scripts/atlas/v0/full/tarballs/tm_0_.40_plddt_0_.40.txt
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_00.tar.gz
|
| 2 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_01.tar.gz
|
| 3 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_02.tar.gz
|
| 4 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_03.tar.gz
|
| 5 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_04.tar.gz
|
| 6 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_05.tar.gz
|
| 7 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_06.tar.gz
|
| 8 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_07.tar.gz
|
| 9 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_08.tar.gz
|
| 10 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_09.tar.gz
|
| 11 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_10.tar.gz
|
| 12 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_11.tar.gz
|
| 13 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_12.tar.gz
|
| 14 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_13.tar.gz
|
| 15 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_14.tar.gz
|
| 16 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_15.tar.gz
|
| 17 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_16.tar.gz
|
| 18 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_17.tar.gz
|
| 19 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_18.tar.gz
|
| 20 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_19.tar.gz
|
| 21 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_20.tar.gz
|
| 22 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_21.tar.gz
|
| 23 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_22.tar.gz
|
| 24 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_23.tar.gz
|
| 25 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_24.tar.gz
|
| 26 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_25.tar.gz
|
| 27 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_26.tar.gz
|
| 28 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_27.tar.gz
|
| 29 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_28.tar.gz
|
| 30 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_29.tar.gz
|
| 31 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_30.tar.gz
|
| 32 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_31.tar.gz
|
| 33 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_32.tar.gz
|
| 34 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_33.tar.gz
|
| 35 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_34.tar.gz
|
| 36 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_35.tar.gz
|
| 37 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_36.tar.gz
|
| 38 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_37.tar.gz
|
| 39 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_38.tar.gz
|
| 40 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_39.tar.gz
|
| 41 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_40.tar.gz
|
| 42 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_41.tar.gz
|
| 43 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_42.tar.gz
|
| 44 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_43.tar.gz
|
| 45 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_44.tar.gz
|
| 46 |
+
https://dl.fbaipublicfiles.com/esmatlas/v0/full/tarballs/tm_0_.40_plddt_0_.40_45.tar.gz
|
weight/esm-main/scripts/atlas/v2023_02/full/esm2_embeddings/tm_.70_.80_plddt_.70_.80.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_00.npz
|
| 2 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_01.npz
|
| 3 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_02.npz
|
| 4 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_03.npz
|
| 5 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_04.npz
|
| 6 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_05.npz
|
| 7 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_06.npz
|
| 8 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_07.npz
|
| 9 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_08.npz
|
| 10 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_09.npz
|
| 11 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_10.npz
|
| 12 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_11.npz
|
| 13 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.70_.80_12.npz
|
weight/esm-main/scripts/atlas/v2023_02/full/esm2_embeddings/tm_.70_.80_plddt_.80_.90.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.80_.90_00.npz
|
| 2 |
+
https://dl.fbaipublicfiles.com/esmatlas/v2023_02/full/lm_reps/tm_.70_.80_plddt_.80_.90_01.npz
|