wuxing0105 commited on
Commit
709e90e
·
verified ·
1 Parent(s): 1597470

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +64 -10
  2. README.md +283 -0
  3. configuration.json +2 -0
  4. model/README.md +10 -0
  5. model/__init__.py +2 -0
  6. model/__pycache__/__init__.cpython-311.pyc +0 -0
  7. model/esm/__init__.py +13 -0
  8. model/esm/__pycache__/__init__.cpython-311.pyc +0 -0
  9. model/esm/__pycache__/esm1.cpython-311.pyc +0 -0
  10. model/esm/__pycache__/esm2.cpython-311.pyc +0 -0
  11. model/esm/__pycache__/msa_transformer.cpython-311.pyc +0 -0
  12. model/esm/__pycache__/pretrained.cpython-311.pyc +0 -0
  13. model/esm/__pycache__/version.cpython-311.pyc +0 -0
  14. model/esm/esm1.py +200 -0
  15. model/esm/esm2.py +152 -0
  16. model/esm/esmfold/__init__.py +0 -0
  17. model/esm/esmfold/v1/categorical_mixture.py +43 -0
  18. model/esm/esmfold/v1/misc.py +309 -0
  19. model/esm/esmfold/v1/pretrained.py +197 -0
  20. model/esm/inverse_folding/__init__.py +8 -0
  21. model/esm/inverse_folding/features.py +352 -0
  22. model/esm/inverse_folding/gvp_encoder.py +56 -0
  23. model/esm/inverse_folding/gvp_transformer_encoder.py +184 -0
  24. model/esm/inverse_folding/gvp_utils.py +68 -0
  25. model/esm/inverse_folding/multichain_util.py +152 -0
  26. model/esm/inverse_folding/transformer_decoder.py +228 -0
  27. model/esm/inverse_folding/transformer_layer.py +304 -0
  28. model/esm/inverse_folding/util.py +323 -0
  29. model/esm/msa_transformer.py +238 -0
  30. model/esm/pretrained.py +553 -0
  31. model/esm/version.py +6 -0
  32. model/openfold/__init__.py +0 -0
  33. model/openfold/dropout.py +78 -0
  34. model/openfold/embedders.py +984 -0
  35. model/openfold/evoformer.py +1219 -0
  36. model/openfold/heads.py +267 -0
  37. model/openfold/model.py +591 -0
  38. model/openfold/msa.py +476 -0
  39. model/openfold/outer_product_mean.py +159 -0
  40. model/openfold/pair_transition.py +99 -0
  41. model/openfold/primitives.py +902 -0
  42. model/openfold/structure_module.py +1252 -0
  43. model/openfold/template.py +693 -0
  44. model/openfold/torchscript.py +215 -0
  45. model/openfold/triangular_attention.py +168 -0
  46. model/openfold/triangular_multiplicative_update.py +647 -0
  47. weight/esm-main/scripts/atlas/v0/full/tarballs/tm_.90_1_plddt_.70_.80.txt +1 -0
  48. weight/esm-main/scripts/atlas/v0/full/tarballs/tm_0_.40_plddt_0_.40.txt +46 -0
  49. weight/esm-main/scripts/atlas/v2023_02/full/esm2_embeddings/tm_.70_.80_plddt_.70_.80.txt +13 -0
  50. 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
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bin.* filter=lfs diff=lfs merge=lfs -text
5
  *.bz2 filter=lfs diff=lfs merge=lfs -text
 
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
 
11
  *.model filter=lfs diff=lfs merge=lfs -text
12
  *.msgpack filter=lfs diff=lfs merge=lfs -text
 
 
13
  *.onnx filter=lfs diff=lfs merge=lfs -text
14
  *.ot filter=lfs diff=lfs merge=lfs -text
15
  *.parquet filter=lfs diff=lfs merge=lfs -text
16
  *.pb filter=lfs diff=lfs merge=lfs -text
17
+
 
 
18
  *.pth filter=lfs diff=lfs merge=lfs -text
19
  *.rar filter=lfs diff=lfs merge=lfs -text
 
20
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
21
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
22
  *.tflite filter=lfs diff=lfs merge=lfs -text
23
  *.tgz filter=lfs diff=lfs merge=lfs -text
 
24
  *.xz filter=lfs diff=lfs merge=lfs -text
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
+ *.zstandard filter=lfs diff=lfs merge=lfs -text
27
+ *.tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *.db* filter=lfs diff=lfs merge=lfs -text
29
+ *.ark* filter=lfs diff=lfs merge=lfs -text
30
+ **/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
31
+ **/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
32
+ **/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
33
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
34
+ *.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