diff --git a/.gitignore b/.gitignore index 6a3e68da..f7e9c036 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -**/.DS_Store \ No newline at end of file +**/.DS_Store +*.pyc diff --git a/af/README.md b/af/README.md index 0d95345d..da2eaf5e 100644 --- a/af/README.md +++ b/af/README.md @@ -1,4 +1,4 @@ -# AfDesign (v1.0.5) +# AfDesign (v1.0.6) ### Google Colab Open In Colab @@ -16,6 +16,7 @@ Minor changes changes include renaming intra_pae/inter_con to pae/con and inter_ - **11July2022** - v1.0.3 - Improved homo-oligomeric support. RMSD and dgram losses have been refactored to automatically save aligned coordinates. Multimeric coordinates now saved with chain identifiers. - **23July2022** - v1.0.4 - Adding support for openfold weights. To enable set `mk_afdesign_model(..., use_openfold=True)`. - **31July2022** - v1.0.5 - Refactoring to add support for swapping batch features without recompile. Allowing for implementation of [AF2Rank](https://github.com/sokrypton/ColabDesign/blob/main/af/examples/AF2Rank.ipynb)! +- **19Aug2022** - v1.0.6 - Adding support for alphafold-multimer. To enable set `mk_afdesign_model(..., use_multimer=True)`. For multimer mode, multiple recycles maybe needed! ### setup ```bash @@ -23,7 +24,7 @@ pip install git+https://github.com/sokrypton/ColabDesign.git # download alphafold weights mkdir params -curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params +curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params # download openfold weights (optional) for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1 @@ -97,14 +98,15 @@ model.opt["weights"]["pae"] = 0.0 #### How do I control number of recycles used during design? ```python model = mk_afdesign_model(num_recycles=1, recycle_mode="average") -# if recycle_mode in ["average","last","sample"] the number of recycles can change during optimization +# if recycle_mode in ["average",last","sample","first"] the number of recycles can change during optimization model.set_opt(num_recycles=1) ``` - `num_recycles` - number of recycles to use during design (for denovo proteins we find 0 is often enough) - `recycle_mode` - optimizing across all recycles can be tricky, we experiment with a couple of ways: - - *last* - use loss from last recycle. (Not recommended, unless you increase number optimization) - - *sample* - Same as *last* but each iteration a different number of recycles are used. (Previous default). - - *average* - compute loss at each recycle and average gradients. (Default; Recommended). + - *last* - use loss from last recycle. (Default) + - *average* - compute loss at each recycle and average gradients. (Previous default from v.1.0.5) + - *sample* - Same as *last* but each iteration a different number of recycles are used. + - *first* - use loss from first recycle. - *add_prev* - average the outputs (dgram, plddt, pae) across all recycles before computing loss. - *backprop* - use loss from last recycle, but backprop through all recycles. @@ -122,17 +124,6 @@ model.set_opt(num_models=1) #### Can I use OpenFold model params for design instead of AlphaFold? ```python model = mk_afdesign_model(use_openfold=True, use_alphafold=False) -# OR -model.set_opt(use_openfold=True, use_alphafold=False) -``` -#### How is contact defined? How do I change it? -By default, 2 [con]tacts per positions are optimized to be within cβ-cβ < 14.0Å and sequence seperation ≥ 9. This can be changed with: -```python -model.set_opt(con=dict(cutoff=8, seqsep=5, num=1)) -``` -For interface: -```python -model.set_opt(i_con=dict(...)) ``` #### For binder hallucination, can I specify the site I want to bind? ```python @@ -142,12 +133,6 @@ model.prep_inputs(..., hotspot="1-10,15,3") ```python model.prep_inputs(..., chain="A,B") ``` -#### Can I design homo-oligomers? -```python -model.prep_inputs(..., copies=2) -# specify interface specific contact and/or pae loss -model.set_weights(i_con=1, i_pae=0) -``` #### For fixed backbone design, how do I force the sequence to be the same for homo-dimer optimization? ```python model.prep_inputs(pdb_filename="6Q40.pdb", chain="A,B", copies=2, homooligomer=True) @@ -168,14 +153,13 @@ model.restart(seed=0) - `design_hard()` - optimize *one_hot(logits)* inputs (discrete) - For complex topologies, we find directly optimizing one_hot encoded sequence `design_hard()` to be very challenging. -To get around this problem, we propose optimizing in 2 or 3 stages. - - `design_2stage()` - *soft* → *hard* +To get around this problem, we propose optimizing in 3 stages. - `design_3stage()` - *logits* → *soft* → *hard* + #### What are all the different losses being optimized? - general losses - *pae* - minimizes the predicted alignment error - *plddt* - maximizes the predicted LDDT - - *msa_ent* - minimize entropy for MSA design (see example at the end of notebook) - *pae* and *plddt* values are between 0 and 1 (where lower is better for both) - fixbb specific losses @@ -184,18 +168,26 @@ To get around this problem, we propose optimizing in 2 or 3 stages. - we find *dgram_cce* loss to be more stable for design (compared to *fape*) - hallucination specific losses - - *con* - maximize number of contacts. (We find just minimizing *plddt* results in single long helix, -and maximizing *pae* results in a two helix bundle. To encourage compact structures we add a `con` term) + - *con* - maximize `1` contacts per position. `model.set_opt("con",num=1)` - binder specific losses - - *i_pae* - minimize PAE interface of the proteins - - *pae* - minimize PAE within binder - - *i_con* - maximize number of contacts at the interface of the proteins - - *con* - maximize number of contacts within binder + - *pae* - minimize PAE at interface and within binder + - *con* - - maximize `2` contacts per binder position, within binder. `model.set_opt("con",num=2)` + - *i_con* - maximize `1` contacts per binder position `model.set_opt("i_con",num=1)` - partial hallucination specific losses - *sc_fape* - sidechain-specific fape +#### How is contact defined? How do I change it? +By default, 2 [con]tacts per positions are optimized to be within cβ-cβ < 14.0Å and sequence seperation ≥ 9. This can be changed with: +```python +model.set_opt(con=dict(cutoff=8, seqsep=5, num=1)) +``` +For interface: +```python +model.set_opt(i_con=dict(...)) +``` + # Advanced FAQ #### loss during Gradient descent is too jumpy, can I do some kind of greedy search towards the end? Gradient descent updates multiple positions each iteration, which can be a little too aggressive during hard (discrete) mode. diff --git a/af/design.ipynb b/af/design.ipynb index 90e3a32b..61e380c1 100644 --- a/af/design.ipynb +++ b/af/design.ipynb @@ -16,7 +16,7 @@ "id": "OA2k3sAYuiXe" }, "source": [ - "#AfDesign (v1.0.5)\n", + "#AfDesign (v1.0.6)\n", "Backprop through AlphaFold for protein design.\n", "\n", "**WARNING**\n", @@ -42,7 +42,7 @@ " ln -s /usr/local/lib/python3.7/dist-packages/colabdesign colabdesign\n", " # download params\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" @@ -246,7 +246,9 @@ }, "source": [ "# binder hallucination\n", - "For a given protein target and protein binder length, generate/hallucinate a protein binder sequence AlphaFold thinks will bind to the target structure. To do this, we minimize PAE and maximize number of contacts at the interface and within the binder, and we maximize pLDDT of the binder." + "For a given protein target and protein binder length, generate/hallucinate a protein binder sequence AlphaFold thinks will bind to the target structure.\n", + "To do this, we minimize PAE and maximize number of contacts at the interface and within the binder, and we maximize pLDDT of the binder.\n", + "By default, AlphaFold-ptm with residue index offset hack is used. To enable AlphaFold-multimer set: mk_afdesign_model(use_multimer=True).\n" ] }, { @@ -275,12 +277,6 @@ "outputs": [], "source": [ "af_model.restart()\n", - "\n", - "# settings we find work best for helical peptide binder hallucination\n", - "af_model.set_weights(plddt=0.1, pae=0.1, i_pae=1.0, con=0.1, i_con=0.5)\n", - "af_model.set_opt(con=dict(binary=True, cutoff=21.6875, num=af_model._binder_len, seqsep=0))\n", - "af_model.set_opt(i_con=dict(binary=True, cutoff=21.6875, num=af_model._binder_len))\n", - "\n", "af_model.design_3stage(100,100,10)" ] }, @@ -373,8 +369,7 @@ "af_model.prep_inputs(pdb_filename=get_pdb(\"6MRR\"),\n", " chain=\"A\",\n", " pos=\"3-30,33-68\", # define positions to contrain\n", - " length=100, # total length if different from input pdb\n", - " fix_seq=False) # set True to constrain sequence in the specified positions\n", + " length=100) # total length if different from input pdb\n", "\n", "af_model.rewire(loops=[36]) # set loop length between segments " ], @@ -452,4 +447,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/af/examples/2stage_binder_hallucination.ipynb b/af/examples/2stage_binder_hallucination.ipynb deleted file mode 100644 index 0202d96d..00000000 --- a/af/examples/2stage_binder_hallucination.ipynb +++ /dev/null @@ -1,281 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "view-in-github", - "colab_type": "text" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OA2k3sAYuiXe" - }, - "source": [ - "# AfDesign - two-stage binder hallucination\n", - "For a given protein target and protein binder length, generate/hallucinate a protein binder sequence AlphaFold thinks will bind to the target structure. To do this, we minimize PAE and maximize number of contacts at the interface and within the binder, and we maximize pLDDT of the binder.\n", - "\n", - "**WARNING**\n", - "1. This notebook is in active development and was designed for demonstration purposes only.\n", - "2. Using AfDesign as the only \"loss\" function for design might be a bad idea, you may find adversarial sequences (aka. sequences that trick AlphaFold)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "id": "-AXy0s_4cKaK" - }, - "outputs": [], - "source": [ - "#@title install\n", - "%%bash\n", - "if [ ! -d params ]; then\n", - " pip -q install git+https://github.com/sokrypton/ColabDesign.git\n", - " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", - " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", - " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", - "fi" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "cellView": "form", - "id": "Vt7G_nbNeSQ3" - }, - "outputs": [], - "source": [ - "#@title #import libraries\n", - "import warnings\n", - "warnings.simplefilter(action='ignore', category=FutureWarning)\n", - "\n", - "import os\n", - "from colabdesign import mk_afdesign_model, clear_mem\n", - "from IPython.display import HTML\n", - "from google.colab import files\n", - "import numpy as np\n", - "\n", - "#########################\n", - "def get_pdb(pdb_code=\"\"):\n", - " if pdb_code is None or pdb_code == \"\":\n", - " upload_dict = files.upload()\n", - " pdb_string = upload_dict[list(upload_dict.keys())[0]]\n", - " with open(\"tmp.pdb\",\"wb\") as out: out.write(pdb_string)\n", - " return \"tmp.pdb\"\n", - " else:\n", - " os.system(f\"wget -qnc https://files.rcsb.org/view/{pdb_code}.pdb\")\n", - " return f\"{pdb_code}.pdb\"" - ] - }, - { - "cell_type": "code", - "source": [ - "#@title # Prep Inputs\n", - "pdb = \"4N5T\" #@param {type:\"string\"}\n", - "chain = \"A\" #@param {type:\"string\"}\n", - "binder_len = 50#@param {type:\"integer\"}\n", - "hotspot = \"\" #@param {type:\"string\"}\n", - "if hotspot == \"\": hotspot = None\n", - "\n", - "x = {\"pdb_filename\":pdb, \"chain\":chain, \"binder_len\":binder_len, \"hotspot\":hotspot}\n", - "if \"x_prev\" not in dir() or x != x_prev:\n", - " x[\"pdb_filename\"] = get_pdb(x[\"pdb_filename\"])\n", - " \n", - " clear_mem()\n", - " model = mk_afdesign_model(protocol=\"binder\")\n", - " model.prep_inputs(**x)\n", - "\n", - " pre_model = mk_afdesign_model(protocol=\"hallucination\")\n", - " pre_model.prep_inputs(length=binder_len)\n", - "\n", - " x_prev = x\n", - " print(\"target length:\", model._target_len)\n", - " print(\"binder length:\", model._binder_len)" - ], - "metadata": { - "cellView": "form", - "id": "HSgE99WALOE-" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#@title #stage 1 - Pre-hallucinate binder scaffold\n", - "#@markdown ---\n", - "#@markdown ####Weights\n", - "#@markdown - Minimizing `pae` or maximizing `plddt` often results in a single helix.\n", - "#@markdown To avoid this, we start with a random sequence and instead try to optimize \n", - "#@markdown defined `num`ber of `con`tacts per position. \n", - "pae = 0.1 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", - "plddt = 0.1 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", - "helix = 0.0 \n", - "con = 1.0 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", - "#@markdown ####Contact Definition\n", - "#@markdown - The contact definition is based on Cb-Cb diststance `cutoff`. To avoid \n", - "#@markdown biasing towards helical contact, only contacts with sequence seperation > \n", - "#@markdown `seqsep` are considered.\n", - "\n", - "seqsep = 9 #@param [\"0\",\"5\",\"9\"] {type:\"raw\"}\n", - "cutoff = \"14\" #@param [\"8\", \"14\", \"max\"]\n", - "num = \"2\" #@param [\"1\", \"2\", \"3\", \"4\", \"8\", \"max\"]\n", - "binary = True #@param {type:\"boolean\"}\n", - "if cutoff == \"max\": cutoff = 21.6875\n", - "if num == \"max\": num = binder_len\n", - "\n", - "pre_opt = {\"con\":{\"seqsep\":int(seqsep),\"cutoff\":float(cutoff),\"num\":int(num),\n", - " \"binary\":binary}}\n", - "pre_weights = {\"con\":float(con),\"helix\":float(helix),\n", - " \"pae\":float(pae),\"plddt\":float(plddt)}\n", - "\n", - "# pre-design with gumbel initialization and softmax activation\n", - "pre_model.restart(mode=\"gumbel\", opt=pre_opt, weights=pre_weights)\n", - "pre_model.design_soft(50)\n", - "save_seq = np.asarray(pre_model.aux[\"seq\"][\"pseudo\"])\n", - "\n", - "# refine\n", - "pre_model.restart(seq=save_seq, opt=pre_opt, weights=pre_weights, keep_history=True)\n", - "pre_model.design(50, soft=0.0, e_soft=1.0)\n", - "save_seq = np.asarray(pre_model.aux[\"seq\"][\"pseudo\"])" - ], - "metadata": { - "cellView": "form", - "id": "TX1aPX0fXa6D" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#@markdown ## display pre-hallucinated binder scaffold {run: \"auto\"}\n", - "color = \"pLDDT\" #@param [\"chain\", \"pLDDT\", \"rainbow\"]\n", - "show_sidechains = False #@param {type:\"boolean\"}\n", - "show_mainchains = False #@param {type:\"boolean\"}\n", - "pre_model.plot_pdb(show_sidechains=show_sidechains,\n", - " show_mainchains=show_mainchains,\n", - " color=color)" - ], - "metadata": { - "cellView": "form", - "id": "gz7wRJaGXs9e" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "HTML(pre_model.animate())" - ], - "metadata": { - "id": "5OJdtq8trTB4" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#@title #state 2 - binder design\n", - "#@markdown ---\n", - "#@markdown ####interface Weights\n", - "i_pae = 1.0 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", - "i_con = 0.5 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", - "weights = {\"i_pae\":float(i_pae),\n", - " \"i_con\":float(i_con),\n", - " **pre_weights}\n", - "\n", - "#@markdown ####interface Contact Definition\n", - "cutoff = \"max\" #@param [\"8\", \"14\", \"max\"]\n", - "num = \"max\" #@param [\"1\", \"2\", \"4\", \"8\", \"max\"]\n", - "binary = True #@param {type:\"boolean\"}\n", - "if cutoff == \"max\": cutoff = 21.6875\n", - "if num == \"max\": num = binder_len\n", - "\n", - "opt = {\"i_con\":{\"cutoff\":float(cutoff),\"num\":int(num),\n", - " \"binary\":binary},\n", - " **pre_opt}\n", - "\n", - "model.restart(seq=save_seq, opt=opt, weights=weights)\n", - "model.design_3stage(100,100,10)" - ], - "metadata": { - "cellView": "form", - "id": "eCGc3J663NGz" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "#@markdown ## display hallucinated binder {run: \"auto\"}\n", - "color = \"chain\" #@param [\"chain\", \"pLDDT\", \"rainbow\"]\n", - "show_sidechains = False #@param {type:\"boolean\"}\n", - "show_mainchains = False #@param {type:\"boolean\"}\n", - "model.save_pdb(f\"{model.protocol}.pdb\")\n", - "model.plot_pdb(show_sidechains=show_sidechains,\n", - " show_mainchains=show_mainchains,\n", - " color=color)" - ], - "metadata": { - "cellView": "form", - "id": "Ec0BnP1ehH5w" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "L2E9Tn2Acchj" - }, - "outputs": [], - "source": [ - "HTML(model.animate())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "YSKWYu0_GlUH" - }, - "outputs": [], - "source": [ - "model.get_seqs()" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "collapsed_sections": [ - "q4qiU9I0QHSz" - ], - "name": "2stage_binder_hallucination.ipynb", - "provenance": [], - "include_colab_link": true - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/af/examples/AF2Rank.ipynb b/af/examples/AF2Rank.ipynb index c2e0d402..2682cf09 100644 --- a/af/examples/AF2Rank.ipynb +++ b/af/examples/AF2Rank.ipynb @@ -26,7 +26,7 @@ "colab_type": "text" }, "source": [ - "\"Open" + "\"Open" ] }, { @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "cellView": "form", "id": "zk6_tVpg9Bdi" @@ -61,7 +61,7 @@ "\n", " # alphafold params\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", "\n", " # download openfold weights (optional)\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2\n", @@ -108,7 +108,7 @@ " return o\n", " \n", "def plot_me(scores, x=\"tm_i\", y=\"composite\", \n", - " title=None, diag=False, scale_axis=True, dpi=100):\n", + " title=None, diag=False, scale_axis=True, dpi=100, **kwargs):\n", " def rescale(a,amin=None,amax=None): \n", " a = np.copy(a)\n", " if amin is None: amin = a.min()\n", @@ -122,14 +122,16 @@ " x_vals = np.array([k[x] for k in scores])\n", " y_vals = np.array([k[y] for k in scores])\n", " c = rescale(np.array([k[\"plddt\"] for k in scores]),0.5,0.9)\n", - " plt.scatter(x_vals, y_vals, c=c*0.75, s=5, vmin=0, vmax=1, cmap=\"gist_rainbow\")\n", + " plt.scatter(x_vals, y_vals, c=c*0.75, s=5, vmin=0, vmax=1, cmap=\"gist_rainbow\",\n", + " **kwargs)\n", " if diag:\n", " plt.plot([0,1],[0,1],color=\"black\")\n", " \n", " labels = {\"tm_i\":\"TMscore of Input\",\n", - " \"tm_o\":\"TMscore of Ouput\",\n", + " \"tm_o\":\"TMscore of Output\",\n", " \"tm_io\":\"TMscore between Input and Output\",\n", " \"ptm\":\"Predicted TMscore (pTM)\",\n", + " \"i_ptm\":\"Predicted interface TMscore (ipTM)\",\n", " \"plddt\":\"Predicted LDDT (pLDDT)\",\n", " \"composite\":\"Composite\"}\n", "\n", @@ -138,19 +140,31 @@ " if x in labels: plt.xlim(-0.1, 1.1)\n", " if y in labels: plt.ylim(-0.1, 1.1)\n", " \n", - " plt.show()\n", " print(spearmanr(x_vals,y_vals).correlation)\n", "\n", - "\n", "class af2rank:\n", - " def __init__(self, pdb, chain=None):\n", - " self.model = mk_af_model(protocol=\"fixbb\", use_templates=True,\n", - " use_alphafold=True, use_openfold=True)\n", - " self.model.prep_inputs(pdb, chain=chain)\n", + " def __init__(self, pdb, chain=None, model_name=\"model_1_ptm\", model_names=None):\n", + " self.args = {\"pdb\":pdb, \"chain\":chain,\n", + " \"use_multimer\":(\"multimer\" in model_name),\n", + " \"model_name\":model_name,\n", + " \"model_names\":model_names}\n", + " self.reset()\n", + "\n", + " def reset(self):\n", + " self.model = mk_af_model(protocol=\"fixbb\",\n", + " use_templates=True,\n", + " use_multimer=self.args[\"use_multimer\"],\n", + " use_alphafold=True, use_openfold=True,\n", + " debug=False,\n", + " model_names=self.args[\"model_names\"])\n", + " \n", + " self.model.prep_inputs(self.args[\"pdb\"], chain=self.args[\"chain\"])\n", " self.model.set_seq(mode=\"wildtype\")\n", " self.wt_batch = copy_dict(self.model._inputs[\"batch\"])\n", + " self.wt = self.model._wt_aatype\n", "\n", " def set_pdb(self, pdb, chain=None):\n", + " if chain is None: chain = self.args[\"chain\"]\n", " self.model.prep_inputs(pdb, chain=chain)\n", " self.model.set_seq(mode=\"wildtype\")\n", " self.wt = self.model._wt_aatype\n", @@ -183,51 +197,54 @@ " return score\n", " \n", " def predict(self, pdb=None, seq=None, chain=None, \n", - " input_template=True, \n", - " rm_tm_seq=True, rm_tm_sc=True, recycles=1,\n", - " iterations=1, model_name=\"model_1_ptm\",\n", - " tm_aatype=21, save_pdb=False,\n", - " output_dir=\"tmp\",output_pdb=None,\n", - " extras=None, verbose=True):\n", + " input_template=True, model_name=None,\n", + " rm_seq=True, rm_sc=True, rm_ic=False,\n", + " recycles=1, iterations=1,\n", + " output_pdb=None, extras=None, verbose=True):\n", + " \n", + " if model_name is not None:\n", + " self.args[\"model_name\"] = model_name\n", + " if \"multimer\" in model_name: \n", + " if not self.args[\"use_multimer\"]:\n", + " self.args[\"use_multimer\"] = True\n", + " self.reset()\n", + " else:\n", + " if self.args[\"use_multimer\"]:\n", + " self.args[\"use_multimer\"] = False\n", + " self.reset()\n", " \n", " if pdb is not None: self.set_pdb(pdb, chain)\n", " if seq is not None: self.set_seq(seq)\n", "\n", " # set template sequence\n", - " tm_aatype = np.full_like(self.wt, tm_aatype) if rm_tm_seq else self.wt\n", - " self.model.opt[\"template\"][\"aatype\"] = tm_aatype\n", + " self.model._inputs[\"batch\"][\"aatype\"] = self.wt\n", "\n", " # set other options\n", - " self.model.set_opt(template=dict(dropout=not input_template),\n", - " rm_template_seq=True, rm_template_sc=rm_tm_sc,\n", - " num_recycles=recycles)\n", + " self.model.set_opt(\n", + " template=dict(dropout=not input_template,\n", + " rm_ic=rm_ic,\n", + " rm_sc=rm_sc,\n", + " rm_seq=rm_seq),\n", + " num_recycles=recycles)\n", " \n", " # \"manual\" recycles using templates\n", " ini_atoms = self.model._inputs[\"batch\"][\"all_atom_positions\"].copy()\n", " for i in range(iterations):\n", - " self.model.predict(models=[model_name], verbose=False)\n", + " self.model.predict(models=self.args[\"model_name\"], verbose=False)\n", " if i < iterations - 1:\n", " self.model._inputs[\"batch\"][\"all_atom_positions\"] = self.model.aux[\"atom_positions\"]\n", " else:\n", " self.model._inputs[\"batch\"][\"all_atom_positions\"] = ini_atoms\n", " \n", - " if save_pdb:\n", - " os.makedirs(output_dir, exist_ok=True)\n", - " if output_pdb is None:\n", - " if pdb is None:\n", - " aatype = np.array(self.model._params[\"seq\"])[0].argmax(-1)\n", - " diff = np.where(aatype != self.wt)[0]\n", - " seq_diff = np.array(list(seq))[diff]\n", - " output_pdb = \"_\".join([f\"{i}{a}\" for i,a in zip(diff, seq_diff)]) + \".pdb\"\n", - " else:\n", - " output_pdb = os.path.basename(pdb)\n", - " self.model.save_pdb(os.path.join(output_dir, output_pdb))\n", - "\n", " score = self._get_score()\n", - " if extras is not None: score.update(extras)\n", + " if extras is not None:\n", + " score.update(extras)\n", + "\n", + " if output_pdb is not None:\n", + " self.model.save_pdb(output_pdb)\n", " \n", " if verbose:\n", - " print_list = [\"tm_i\",\"tm_o\",\"tm_io\",\"composite\",\"ptm\",\"plddt\",\"fitness\",\"id\"]\n", + " print_list = [\"tm_i\",\"tm_o\",\"tm_io\",\"composite\",\"ptm\",\"i_ptm\",\"plddt\",\"fitness\",\"id\"]\n", " print_score = lambda k: f\"{k} {score[k]:.4f}\" if isinstance(score[k],float) else f\"{k} {score[k]}\"\n", " print(*[print_score(k) for k in print_list if k in score])\n", " \n", @@ -237,38 +254,46 @@ "cellView": "form", "id": "1o-_Rl4hFfkR" }, - "execution_count": 2, + "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ - "#@markdown ### settings\n", - "seq_relacement = \"gap\" #@param [\"gap\", \"X\", \"A\", \"none\"]\n", - "mask_sidechains = True #@param {type:\"boolean\"}\n", + "#@markdown ### **settings**\n", "recycles = 1 #@param [\"0\", \"1\", \"2\", \"3\", \"4\"] {type:\"raw\"}\n", "iterations = 1 \n", - "model_name = \"model_1_ptm\" #@param [\"model_1_ptm\", \"model_2_ptm\", \"openfold_model_ptm_1\", \"openfold_model_ptm_2\"]\n", + "\n", + "# decide what model to use\n", + "model_mode = \"alphafold\" #@param [\"alphafold\", \"alphafold-multimer\", \"openfold\"]\n", + "model_num = 1 #@param [\"1\", \"2\", \"3\", \"4\", \"5\"] {type:\"raw\"}\n", + "\n", + "if model_mode == \"alphafold\":\n", + " model_name = f\"model_{model_num}_ptm\"\n", + "if model_mode == \"alphafold-multimer\":\n", + " model_name = f\"model_{model_num}_multimer_v2\"\n", + "if model_mode == \"openfold\":\n", + " model_name = f\"openfold_model_ptm_{model_num}\"\n", + "\n", "save_output_pdbs = False #@param {type:\"boolean\"}\n", "\n", - "tm_aa = -1\n", - "if seq_relacement == \"A\": tm_aa = 0\n", - "if seq_relacement == \"gap\": tm_aa = 21\n", - "if seq_relacement == \"X\": tm_aa = 20\n", + "#@markdown ### **advanced**\n", + "mask_sequence = True #@param {type:\"boolean\"}\n", + "mask_sidechains = True #@param {type:\"boolean\"}\n", + "mask_interchain = False #@param {type:\"boolean\"}\n", "\n", - "SETTINGS = {\"rm_tm_seq\":seq_relacement != \"none\",\n", - " \"rm_tm_sc\":not mask_sidechains,\n", + "SETTINGS = {\"rm_seq\":mask_sequence,\n", + " \"rm_sc\":mask_sidechains,\n", + " \"rm_ic\":mask_interchain,\n", " \"recycles\":int(recycles),\n", " \"iterations\":int(iterations),\n", - " \"model_name\":model_name, \n", - " \"tm_aatype\":tm_aa, \n", - " \"save_pdb\":save_output_pdbs}" + " \"model_name\":model_name}" ], "metadata": { "cellView": "form", "id": "6G7XWsStB1sB" }, - "execution_count": 3, + "execution_count": null, "outputs": [] }, { @@ -295,12 +320,12 @@ "\n", "# setup model\n", "clear_mem()\n", - "af = af2rank(NATIVE_PATH, CHAIN)" + "af = af2rank(NATIVE_PATH, CHAIN, model_name=SETTINGS[\"model_name\"])" ], "metadata": { "id": "iDCRJjdSIG0g" }, - "execution_count": 4, + "execution_count": 42, "outputs": [] }, { @@ -313,16 +338,16 @@ "colab": { "base_uri": "https://localhost:8080/" }, - "id": "nKGf21nk6Ofx", - "outputId": "3c800748-71ac-4480-b1b7-25456109c929" + "id": "UCUZxJdbBjZt", + "outputId": "5d290158-bcc1-4f02-f3a9-af13d1946248" }, - "execution_count": 5, + "execution_count": 43, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ - "tm_i 1.0000 tm_o 0.6650 tm_io 0.6650 composite 0.2399 ptm 0.5467 plddt 0.6599\n" + "tm_i 1.0000 tm_o 0.6650 tm_io 0.6650 composite 0.2399 ptm 0.5467 i_ptm 0.0000 plddt 0.6599\n" ] } ] @@ -337,11 +362,16 @@ "\n", "# score the decoy sctructures\n", "for decoy_pdb in os.listdir(DECOY_DIR):\n", - " decoy_path = os.path.join(DECOY_DIR, decoy_pdb)\n", - " SCORES.append(af.predict(pdb=decoy_path, **SETTINGS, extras={\"id\":decoy_path}))" + " input_pdb = os.path.join(DECOY_DIR, decoy_pdb)\n", + " if save_output_pdbs:\n", + " output_pdb = os.path.join(\"tmp\",decoy_pdb)\n", + " else:\n", + " output_pdb = None\n", + " SCORES.append(af.predict(pdb=input_pdb, output_pdb=output_pdb,\n", + " **SETTINGS, extras={\"id\":decoy_pdb}))" ], "metadata": { - "id": "ye1ScsVBajSo" + "id": "ChgI637YCArk" }, "execution_count": null, "outputs": [] @@ -357,29 +387,29 @@ "base_uri": "https://localhost:8080/", "height": 497 }, - "id": "Ouy6R-DoC-zO", - "outputId": "01b3fb93-ebfb-4416-8641-43b4da802c37" + "id": "ZUEaAlP5CR8h", + "outputId": "7322efa2-96d6-4866-d1ad-8c4d46a771f8" }, - "execution_count": 7, + "execution_count": null, "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.9286667300616703\n" + ] + }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], - "image/png": "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\n" + "image/png": "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\n" }, "metadata": { "needs_background": "light" } - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "0.9286664636709675\n" - ] } ] }, @@ -395,28 +425,28 @@ "height": 497 }, "id": "yi_ztCc3kRXy", - "outputId": "531e7145-e65f-4042-b367-940a30ff2f2f" + "outputId": "6d6cd607-5818-4ed8-be63-f192816030aa" }, - "execution_count": 8, + "execution_count": null, "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.9143006449691364\n" + ] + }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAe8AAAHPCAYAAAB6JVL7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdd5wU9f348dd79wp3wB3g0Ts2EI2KvWM3do3d6FeNPdGo0SQaTYzRaPxZY42xomKJMUbsoqIoloCoiFjoIL3dcVzf/fz+eH/mdm7Zu9s7ri28nzz2we3MZ2Y/O7s77/nUEeccxhhjjMkckfbOgDHGGGOaxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZZqMM3iJyvYhsNFPHichZIuJEZOdG0m1U73tTEfp8h7R3XjKFiAzxx+ys9s5Le0j1/jva739T/4xaW5sFbxHpIiJ/FpE3RGSVfagbJxE50n/GK0WkQkS+F5HbRGSzFGkniMjX9eynyH9HrvfPXZqP0aGTRvCIich8EfmPiOwQeo0g3ZX15OHKIKiGAmxjj7ktcySNaRsicnF7nYtFZG6av6uzfPrg+cP17O+mUJqiNn0zbSyrDV+rCPgjMB/4Ehjdiq91I3BLK+6/o2rX9y0itwG/QT/fvwGrgFHAr4BTRORA59x3zdz9GUnPzwQOTrF8BpDn/34GeA2IAiOAi4Cfisjuzrkvmvj6H6R4rYeBz4CHQstKm7hf0zzz0M+5ur0z0oE09/d/MbACeLxFc5Oey4AuoeeHA6cCl/s8BSaF/q4AfiYiFzvnqpL2d6pf36kV8tqhtGXwXgz0dc4t8dW//2utF3LO1QA1rbX/VEREgE7OufK2fN2w9njfARE5FQ3czwGnO+dioXWPA+8B/xKRUT6fTeKceyrp9XYHDk5e7tcN8X9+Hl4vIh8BL6NB/IImvv5sYHbS6zwIzE6VB9O6nN5RqaK989FUrXmeaM/ff3M5514KPxeRPmgAfsk5N7eezd4AjgZ+Cvw3tO2ewFDg38DPWiO/G0JE8p1zZS21vzarNnfOVTrnlqST1lelvOKrQCeLSLmITBOR0X798f55hYhMEZEdk7ZP2fYjIj8Xkc9EpExEVovIByJySGh9oYgMF5HCJuTxUBGZDJTjA4KInC0i74rIMhGpFJFvROSiBvaxt89XhYjMFpEz03j97n6bhSKydX3v21cf3Ssix4rI1z4/00XksBT7DI53hYjMEpEL6juWKfwJWA2cHw7cAM65z9CS+HbACWnsq7W86/8f2l4ZEJGR/rtR7j+7a6nndygiPxWRiSKyTkTWisirIjIyRbrhIvK8iCz3+/1ORG5KSrOjiLwuIiUiUioi7/gLoGD9MP9duTzF/vf06071z7uKyF3++1vpv+dvi8ioRt7745KiWaGe7+3BIvKhiKzx+f1ORP4aWp+qzfdxn7a/iLzk/14u2mwTTdr/ZiLypD8ea0TkCRHZPnmf9byPoAllXxH5h2gTUYmIjBGR7klpGzpPdPPHcYE/jjNF5HciEknaRzf/3oqDvALd0jmOfnm95z3/eYwE9pNEdfOEpNdusTy2kB/RmrDTkpafDkwD1muKE5EtReTfIrLEn98WisizknSub+hYhdJcLHoOrRSRRSJyn4h0S0ozQfR8u5PfRxnwV78uV7QJeabfxwIRuVVEcptyENqy5N1UWwBjgX8ATwFXAuNE5EL0INzv010NPC8iWzvn4vXtTET+BFyPVr/8EagCdgMOAN7yyY4DHgPOJr0qpK3Rqtl/AP8Egirhi4DpaCmvBjgKuF9EIs65+1K8zxeAR4AngHOAx0VkinNuej3vpQh4G+gB7Oecm9VIPvcGjkeP2VrgUuDfIjLIObfS73NH9Ip2MRqIo+hxWt7YQRCRLf2xeNw5V1JPsjHAn4EjgWcb22cr2dz/v7I9Xly0VPEe+ru7BVgHnI+e0JPTnoF+H94Efgfko9+rD0Vkx6BUIiI/ASai1ccPAXPR93kU8AefZqRPUwLc6tNeAEwQkf2cc58652aL1kycDtyZlJ3T0e9NUMp5EL0Iuxf4BtgM/Y6NAD5v7vEJvfeRwCvAV+h3sBL9neyVxuZR9Jh9ip4zDkJrhGYBD/j9R4BxwK5+2bfAMejxbop7gTXoeWVr9PMZLCKjXd17La93nhCRfOB9oL9fPh/YE7gZ6ItWJwcl9f+ix/dBtFnouHTzmsZ57zLgHrS5J7jgW+q3bZM8NtNY4G4R6eKcKxWRLOBE4A6SqsxFJAf9TuSi73WJf09HohcYxT5dozFCtA/On4Dx6Hcn+Nx3EZG9nHPhZpzNgNfR891TwFL/3XsZPVYPocdqO7SZYCvg2LSPgHOuzR/AzoADzqpn/Vy/fo/QskP8sjJgUGj5+X756NCy6/E1a/75FkAMeBGIJL2WhP4+q6F81ZPHQ1Osy0ux7A1gVj372Ce0rCdaHXhbinztDPRBryxnAYOT9lfnfftlDj35bR5a9hO//FehZS+jwaRf0nGrTt5nivd2jN/fZY2kKwamhJ5PAL6uJ22R3+f19ay/t758AUP8tn/0++kN7IcGFgccn5Tuynr2c6VfP6Se9aXoBUu63/s7/f52Tfq814RfB20DXA08lLR9b5/2odCy99GgPCgpbfh7/R//HRgWWtbXb/d+it/S8NCybPQC7vHQsjXAvem+79B2jwNzUyyv871Fg4IDihrYV/DZnZW0fwdcl5T2c2By6PnxPt2vQ8siwDvJ+6zntc/y6SYD2aHlV/nlR4eWzSXFeQK41n9/tkxafjN6wT8w6bd1VShNFC15Jr//5OOY7nnva2BCivfZ4nlM4zvS2G/Oob/97v47/XO//HAgDgwOjkPw/QF28M9PaOB1Gz1W6G+1Er0QiITW/9Lv/+zQsgl+2QVJ+/q5f529k5Zf4NPvme6x6shDxb5xzn0cev6p//9d59z8FMuHNbCvY9Ef5w0uqXTu/JHzfz/unBPn3ONp5nGOc+7N5IUu1J4lWhVfhJ5khyVX06Dvc2Jo2+VoCT7V+xng95MN7Oucm5dmPse7UOncOfcVeuIe5vMYRUsoLznnFoXSzUSvHBvT1f+/tpF0a4GCNPPcEv6MBp4l6I9pc+B3zrkX2zAPYYcDnzhtRgBqP++nk9IdjJYInhHtdV/kv0Mx9Pu+P4CI9AT2BR5N+k3Ufq/9Z3sI+tnODq1fjJZe9haR4DN5Hr1wPD20q0PRC6Bwu/4aYDcR6df0Q5CWNf7/Y5KrZ9P0YNLzidT9PR2GXpT+M1jgzwvJtWKNecjVLWk9gAa1w5PSpTpPnOjztTrpMx6PBr59fbrD/T4fCOU1hpYgG5PWea8BbZHHZnHOrUYLRKf6RacBk+o5Jxb7/w/1tQmppHOsDgJygLuS0vwTPZ8ekbTPSrQmN+xEtLT9bdIxDZr09q8nf+vpyNXmySejYq2dYUFSuuCD6U79Nkevyr5psdypOakWisheaODYA63uDCskkWdIep/ealK/nyfRH8kIl2b/gTRfoxfac3dminSpliULgnbXBlPp+mVp7C8snZNMfR4C/oV+9muA6c65ymbsZ0PyEDaYxMVmWHIP/C39/+8mJ/SCpokgIKUcbuf1RL+DqXr5z0BPWAPRY7NGRMahJ8LrfJrT0TbGcF5+i1aJLhCRKWiP/jHhi4MN9BxwLtqb/xYReQctEb2QfGJNocJfEIUl/54GA4vd+p2H0vmuh/0QfuK0+nYxWisQluo8sSVaA1Zfs1Qv/3+Q1+RRDOmM2tjQ815b5HFDjAWeFJFBaPD9bapEzrk5InIHcAVwuohMRGsan3LOBefidI7VYP9/nfflnKsSkdmh9YEf3fq94bdEm5caO6aN6sjBO9bE5dJaGWlAqrbKzdHqt2/RL8sCtO3kcLRdI7kk0ZT38yI6ROrXaFt/ulr7mM3w//+kvgQiMhgtdYd/HBUkhnUlyw+laa4fnHPjG1gf7Ls189AcwXfkDLTWIFlr9igeA5wo2nN3Gtqr9/5w0HTOPe9PgMehpfqrgN+JyPHOuYZqauq7CKrTmcw5Vy4i+6KlkCPQkvLJwLsicohL6hCZpKF17SVVz/II2m/l1nq2+b71spO2jp7Hl9HS7RNoe/bz9SV0zv1GdNTLMeh39u/A1aLDRhe2Uv7q+9ynobEhleTCab06cvBuSbPQg7YN0NTxvU11FPpFOjpclSkiaVeHNOAetHRwg4gUO+daakz3MjRAbZFiXapldTjnvheR74FjReTXzrlU1edBD/pXQsvmAQeISJ5bf+jM1qE0rWU52odi63rWb+3Xr6hnfVPNI1GqTn6dsKCJY1kjFx9BSXfbBtI09B6Ho6WN8AnjDb/N6WgtQT5a41OHr3a/H+2I2QttV/4DDTezrCZ1D+TkEktQjf2Of1whItegHar2R6ttN8Q8YH9Zf+hOo9/1JFuiHRABnYgK7UvwWhrbzgK6NPL5Bnk9MOiYFVpe33c2+TXSOe/Vd1HVFnlsNn+R9xLajvy6c67B36lzbhoaOG/0F6cfAReibfvpHKvgXLQ1oWGjvkPcUNL7Xs4CtgfeSbPpol4duc27Jb2EnqT+mGKIg4T+TnuoWAOCK/86+0V7sG8w59xfgNuAmyXF8LNm7jOGfvGODbdjisgW6FjKdNyAVk0+KOsPy9kJ7TH9NToGM/Aa2n5/QVL6CNqDswo9ebcK/77fAo7yVW/hPAxCL8TeaqSk1xSvAbuLyK6h1+lJ3TZm0A4xJcA1IpKdvBO/TdBe/gFwTor8i08TvMdjJDT9qoj0RqvHP3ShEQJOxwo/A5yEdsya5vtIBNtFk38fzrllwCL0orUhs4BC30M+2F9ftAQfznuPFNsGJ9QmDaepx5vo9+680GtG0I5HTXF+0udzEVogSqefyPPAHiJyaPIK0WFXQcHqNb/Pi0Lro8AlabxGWuc9tKNqqouqtsjjhroNbaL8S30JRKQglNfANPTYBN+ndI7VePScdGnS8fsF2hz6ahr5fR7t6X5e8goRyRORzmnsA2jjkreI/Ar9kgQB4igRGeD/vifU/tCinHMzRce9XgdMFJEX0eqWXdCTTlAF3dShYqm8hX7A40TkH2jP4fPQ0m3f5r6HMOfcVf4Eep+IrHUtM0nI9Wh10kci8gBalfkrNODu0MB2QZ6eFpFd0Cr9bUTkabSkNQod/rYS7e0Z7uAzDj1ed/qANgkt6R2NDgu6NkX7ZUu7BvgE+FxEgqFWQ0j0vL6mBV/rVrQq/A0RuZvEULF5hJocnHMl/sLsSZ+vZ9HS8CC0Gvkj9LMBHfb3YSj/c3z+jyDxuV2LdoL7UETuR6vdL0BPXKnaCcf4/e6PXnSFdQUWisgL6Ex6pWhHnl3QIVkNeRYd7/8fEfk7ieFv36Pfk8AffbX5q/7Y9EJnAVvo3+uGegmdGe92f4H6LfqdCy4a0i0R5QDviMjzaGnsYp+/l9PY9v/513zFV+dOATqTmAthCFrjMw79vG/xF1/foL3lGy1gNOG8NwW4SHTOgZlojc+7bZHHDeWc+xL9HjbkAOBeEfkX+l3LQn+HMXxhIp1j5ZxbLiI3o0PF3hCRl0l87v+jbqfO+jyJXhg/6GtjP0LPtcP98kPRUQxpvfk2e5AYNpHqMSQp3SsptnckDVEhxXAfUgyZ8svPRqv3KtCpOycAB4XWn0XThoqtl0e/7ij0C1WOnkx/61873fc5gdDQjVC+dg4ti6AdNqqBY+p736mOWei1H09adoA/PpXoj/gX6JVteRM+42PQgLzKH+cf/D5SDvtBA8if0HbzCjQYfIzO0tbQ66QzVCzlELAU6YejgWWpP55L0dLn8Ea2a9JQMb/Ndv7zLUeD0bXoxc16w2PQKYTfQDvblfvP5DFgp6R0I9H+EKt9um/RXrPhNME4/rXoRcO7hIZipsjn1+jJrX/S8hz0IuQLtHag1P99UZrv/2C01FPp83l68vfWfw9fQjvKVfr/xxIaskT9Q8VKU7xmqt9FEdrLv8Qf38fQMcwOOLmR93CWT7cvOv55lT+uTwE9UvzO6jtPdEHnrPjBv8/l6Mn8N9QdgtYDvaAq9nkdQ2L401kNvU+/vLHzXm+0OavE73NCa+Uxje9HWkPFGtnH9dQdKjYUnUdjJvr7WIl+/w9s6rHyaX6Jnq+q0D4p9wPdktJMoP5hsNloTPg69DqT0aGtBekeq2D82kZFRP6CXiltKm36rca3KY10zqVqqzUbKRGZCqxyzh3Y3nlpKyJyLDomfm/n3EcNpDsLDfa7OOfSKyUZ08I21jbvvrRcJ6NNhojkJT3fEu0lP6FdMmTahei9B3ZAS08bpRTf9aCNtoQWmCXOmNa2UZVMRWQY2m59InV7NZv0zPZtW8GYxaDTWH1DRcxGRES2BXZCq0QXo+OtN1b3+AD+Mdp0czxabX6Na8ebCxmTro0qeKNtUH9CS4r1jaMz9QtmLOqDtm99jJ7MfmhwK7OxOAFtd/sOONU5l3F37WqCd9GLlCPRubBnApc45+5t11wZk6aNss3bGGOM2ZhtrG3exhhjzEbLgrcxxhiTYTa2Nu9G+Zlx+tH4HbCMMcZs/LoCi1yGtSFvcsEbDdytNRG9McaYzDMAnQwoY2yKwXstwIIFCygoaMtbSxtjjOlISkpKGDhwIGRgTeymGLwBKCgosOBtjDEmI1mHNWOMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMowFb2OMMSbDWPA2xhhjMky7Bm8R2VdExonIIhFxInJsGtuMFpHPRaRSRGaKyFltkFVjjDGmw2jvkndn4Evgl+kkFpGhwKvAe8AOwF3AwyJyaKvl0BhjjOlgstrzxZ1zrwOvA4hIOptcCMxxzv3GP58hInsDlwNvtkomjTHGmA6mvUveTbUHMD5p2Zt+uTHGGLNJaNeSdzP0AZYmLVsKFIhInnOuPHkDEckFckOLurZi/owxxphWl2kl7+a4GigOPRa2b3aMMcaYDZNpwXsJ0DtpWW+gJFWp27sZKAw9BrRe9owxxpjWl2nV5h8DhyctO9gvT8k5VwlUBs/T7BhnjDHGdFjtPc67i4jsICI7+EVD/fNBfv3NIjImtMmDwDARuVVEhovIxcBJwJ1tnHVjjDGm3bR3tfnOwFT/ALjD/32Df94XGBQkds7NAY5AS9tfAr8BznXO2TAxY4wxmwxxzrV3HtqUiBQAxcXFxRQUFLR3dowxxrSTkpISCgsLAQqdcyXtnZ+maO+StzHGGGOayIK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2EseBtjjDEZxoK3McYYk2HaPXiLyC9FZK6IVIjIpyKyayPpLxOR70SkXEQWiMidItKprfJrjDHGtLd2Dd4icjJwB/BnYBTwJfCmiPSqJ/1pwC0+/QjgF8DJwF/bJMPGGGNMB9DeJe8rgH865x5zzn0DXAiUAefUk35P4CPn3Fjn3Fzn3FvAM0CDpXVjjDFmY9JuwVtEcoCdgPHBMudc3D/fo57NJgE7BVXrIjIMOBx4rXVza4wxxnQcWe342kVAFFiatHwpMDzVBs65sSJSBHwoIoLm/0HnXL3V5iKSC+SGFnXdoFwbY4wx7ay9q82bRERGA9cAF6Nt5McDR4jIdQ1sdjVQHHosbOVsGmOMMa2qPUveK4AY0DtpeW9gST3b/AV40jn3sH8+TUQ6Aw+JyE2+2j3ZzWinuEBXLIAbY4zJYO1W8nbOVQFTgAODZSIS8c8/rmezfCA5QMeCzet5nUrnXEnwANZuUMaNMcaYdtaeJW/QEvETIjIZ+Ay4DOgMPAYgImOAH51zV/v044ArRGQq8CmwBVoaH+eciyXv3BhjjNkYtWvwds49JyI9gRuAPsAXwGHOuaAT2yDqlrRvBJz/vz+wHA3of2izTBtjjDHtTJxz7Z2HNiUiBUBxcXExBQUF7Z0dY4wx7aSkpITCwkKAQt+smjEyqre5McaYjqW6GjaxMmCHYMHbGGNMk5WXw1FnQM4gGLILfDm9vXO0abHgbYwxHdjipfDWB/p/e6muhiPPgKwB+hi2Gxx4Mrzyjq7/cTGcc3n75W9T1N69zY0xxtRj0mQ46DQor4C8TvDus7D7qLbPx+9vglfHJ57PWQRzFqNzZDqIxWHJ8rbP16asScFbREYApwD7AIPRcdfLganAm8C/nXOVLZ1JY4zZFN1yP1RW6d+VVfC3B+A//9ywfZaVw9wFMGsu9O0D7/0P+hTBtsPhidegewFcfgp0yYOIr5v9eIrfOButrw3GAIl/ng0nH7Nh+TJNk1bwFpFRwK3A3sBH6Bjr/wDlQA9gW+Am4B4RuRW4y4K4McZsmJycxOxTAuTmNH9fM+fBLifCmpVo8I2iEcAH4Gg3Tecc3PcCLF8J2dmw/ZYwbxXQM5SRKFCNTjgdATrD+M91W0k5XZZpaWkNFROROcD/A8Y659Y0kG4P4NfAVw3dLKQ92VAxY0ym+Po7GH0SrFwNRT3g/edhm62at69tjoAZs4AqNAB3RkvSoPNU5vnl9YUE59O50PMqNIh31r9nvw5DBzQvf+0hk4eKpVttvpVzrrqxRM65j4GPRSS7sbTGGGMatu3WMO9jmLMAhg2C/Lzm72vpSv+Hr+YmKMU7NBIU+XVBgK4CaoAKny4ocdeEnuf6Zeg+utk9G9tMWr3N0wncG5LeGGNMap3zNYhvSOAGOPmnJOrgo6EVwTKHRoSuQCFaTZ4bStMJ7eWUXDTzndby86B74Ybl0aQv7Q5rInJmOumcc2Oanx1jjDEtYflauO4lWFIMv9gH7vsjDOgDr0yCpYtg9hoSVeC5aOAORwQHdENL6DESndRygRK/vkbX5eTCS3e2xbsygbSnRxWROFCKflz1dUlwzrkeLZS3VmFt3saYTcEeN8P/5kI8Dk7g0oPgsYmwtgJyHVQuR7scB6Vq0FJ1Nom27zhabR6uSw2q1GNAAZADq5+Abp3b5n21pE2hzRtgBnqv7aeAR51zX7VOlowxxqSjtArml8CwbtApdDaPxeGT2f5JISDw93eoLT1XVpEI1KABOQjWQU90B6QaMxQE+xxNd9bozAzcmS7t4O2cGykiuwHnAB+IyEzgEeDpTLtiMcaYTDd5CRz8PKyphP5dYOKpMNQP94pGYHgf+LaUum3apPgbEuO184AyEp3UstFSdxaJjmxdIdIJ7j0VBhbB4e0waYxp4vSozrlPnXMXAH2BvwMnAYtF5GkRyW14a2OMMS3ld+9DiZ/AZck6uPnTxLrV62BWFRp0s4EuaBV3oBtaIg8EgVv88u5oh7Xg0R3YDOin+9lmKFx0GBy5c2IiF9O2mjU9qnOuHBgjInOBP6Ozrv2K1JUsxhhjWlhVrO7dvKpiib///jFU56Jn+E5oUM5DA7Cv7gZgHTDLrxefPjyBCz59NokhYsDFo1v4zZgma/I1k4j0F5FrROQH4Fngf8BI59zqFs+dMcaYlP64J2T5M3h+Nlyxs/5dUQN/+QYtLYe7FkfRQB4eJtYZGA5s5dNH/Ppo0ra+9B7JhTG/gItGt/z7MU3TlKFiJwFnA/uh85j/BnjVORdrcENjjDF1/FACn6yEHbrDdt2at4+Dh8DM8+DblbBDL+jVGVaWQ9GTPkHQSzxo0IyQepxQsKwnid7nRWigrwCpgNEDIC8brtofRm/ZvPyaltXUoWLzgaeBem9O55z7e8tkrXXYUDFjTHuauAwOfBeq4xpP/70PHDtww/dbUQN5Y/2TOLAGPWP3RItpUdava42jHdECeWgAD/nkNNit/4bnryPaVIaKzUf7Gp7WQBqHdmQzxhiTwn3f61Au0Nh5x7ctE7yPm0jijO6AXmhgXoBWe+ejJWr83wKsCO0ggnZkC8ZwA4cPhV37bXjeTMtrylCxIa2YD2OMSVs8DjEH2dHG03Y03XL8nbccRATmOdh9EhzdC36/uS5rqvcWwxvh+tBgH73RqvMo2tu8Cu20FrqbGGv8/4X+/35ABdy8HVy5o90lrKNKu8OaiLwrIs1snTHGmIZ9uhS2exb6Pw53fll/uhenQuFvoNOlcPkLdXtcZ4I/bQdb+Rt4dOoCC2vg0zXwh+/hoflN319lDA56m7od0SAxlnsztEQdzI25lkRw7wz5A4AineKUTrpsxyFw1ahEhzjT8TTloxlN4j40xhjTYmJxOPwVmL4KFpXBFR/BxEXrp6ushp8/DqWVEHdw17vwzndtnt1mizsYVwr7bQtPHAj9uiamDI8KTC5u+j6nLId4MJFKOIA7tNSdjQbvzUAKoLAIrRYX6J0HM4+AqlPg8f2AAojkwdQaGLN4A96oaXXNGudtjDEtafIKWBXMEuFLhT8Uwz5J7a3l1foIW7621bPXZM754dL+vTxaAmPXwuoq+HwVZDl4EDikK8wu87fKdjB6s4b3u7IKzp8GU0vgyF5w+wjYuhuJDmqd0V8BxgMAACAASURBVADeGS1hR+DCzeGleRCNwu37wOh+8PxiKMiC0/pBdgSWVsE1c/U1gouJJxfD2dbe3WE1NXhvIyJ9Gkpgc54bY5rq2bkk7jPtJxUZvxLOdnXbXLvlw8k7wXNT9PnA7nDYyDbPboPGFcOZ86E0Dpf1hH0L4BfL/EoH5EPNOo2xmxXCdV3h61L4aU84vZFgefHX8N9lGujvnQcD8+CqYTCyD0xfht46qsD/7+DUwXDfrvDAbnX3c8mQxN/flsOob+peFAkwpBOmA2tq8H6H1CMFHYn70GRgFxJjTHvqnov2gA6dkZ6ZD5dsDXv0rJv26bPhZztCcTkctz10z2/LnDasKg4nz4Ny395823L40elJMQaJWczQEu7IznBZH9/5O42OYdPWauAG7dj2ja91OG84XFahJf2Yg99vC+cOhc3TuGHILcuhPIJeNMWBGijMhVttPHeH1tTgvRuwvDUyYozZdF02Am5dAOuSRtqWp5gCKhqBEzvozTAmlycCd6BI6g6l7gGUZEFNAdxSCNdWwLYCb+ZCv0YC+HF9YMYsyBKocVp1DnDpFtA5Ch+ugMkxuKUcbv8WnhgCpzZQFX93CTwB2qmtitpeUOf0hiLr4dShNTV4z3fOLWs8mTHGpK9rNpQJibtYAUO6wD692jNXTfdkCYk6SDTIlkcSiwSI5oPrDUS1dhtghoPrquGRRgLmDVtB/05aAj+0CI71jZgicO4wyCuEJ+bosmoH582Dk3ukHn5W4eDKNaEFOSC5cFwe3LiRTsqyMbEOa8aYdicCowphqvie08DDe2pnqkySHwHJAud7ffXLhVmxxKgtJ7DKJaq+A3FguV/2eQ38uwoGR+CcXL0ACEQFLh5c/+uvi9d9XhnXfac6jEGnurBHhsDZXRt6h6ajaMpP4320YsUYY1rcyzvDyX1h317w1M5wYFHj23QkX+GY1iOOy3a1N/eY76AiOMtGgAKI5aFjrkN36RLgoiwdorV7MdxS7rigqopDKyspWe/m2/U7sTsMC5Xer+5bN/iH5UXg+tBtQXfJgVPSaCM3HUPac5uvt6HIzsAI/3SGc25yi+WqFdnc5saYlrYOxyAca5yO5aYKWCJQIxxW6HizBpxIYmYztC383q5QLLBbBLaPwPVlcGM5xDqXQictK21BhCkUUpCyr/D61sbgvbXQKwt279J4+ulVsDoOu+ZCziY2m9qmMrc5ACIyAHgG2AsdWQjQTUQmAac45xa2YP6MMabDmwusAg3McbR+vLdAvIa382u07Lw2B6q1GC5A3wicnF13P8OiEJN4beAGmEmc16ni5NrbgzWsaxSObsJcmCOtY1pGak6L0sNot5IRzrkezrkeaAk84tcZY0yrWRmH96phcXKDbTuaTAUQ14bkqqD46iC/hpig0bpLFd0iWtOZB9yZoor65znwyxzRRnLn0H7qjtWhOvbHKGE0P3I2S1mO3ZF5U9XkanMRKQf2dM5NTVq+EzDROdeBRl2uz6rNjcksc3HcXg3Pl0Klg0onVKDDkl8rgP2zG9tD6xvIEhZSAS4CFQMBgaQSNMDBsRzuchH6R6CwgaLTI5RzLrMIgjdk8Tf6sgW5/AydNzZChP3I412sa3hzbVLV5iRuMJcsCqSYjdgYY5pnNo7tnaN0rS+Nhtp9K9E24v0L69u6bcRxLKEMcCAxiK6BWHetOo+RmLbKCUeKsE0a01hNZQWJwWUCxLiO5fQki6CPeJw4n1DRCu/IZILmVJtfBdzjO6wBtZ3X7gaubKmMGWPMC8A6hwbCpA5bgusQ0zmuJk5NuEd4zirIWQHZpSAzIV4K8Wp2caX8Ks37a35DJXWDtxAFfgx3UQe2ttG+m6zmBO/HgR2AT0WkUkQqgU+BUcCjIrIqeLRgPo0xm6A++Lid5fANwQSjph2Ov+ZDBXFcE4ZThb1FGb2ZSydmczUriRHnZSp4inKKiTOHCk7nW47ga95idcp99CDCSLLrXkhEHWRVQaQzRGYikekcF6lI+17dR9KZRJW5vre/04cuSJ1LmLvIsPF0psU057LtshbPhTHGpHA68J7A4wUxqFinbco5FYDgIjHOjSxmOovIJYd/sxNH0L3B/Tkcz7OML1jLAXTnBIop9aH/FtbwGTHe9dXSQ4kQZy4LqSQOvMUanmE4EylhWzpzNr3IQhCE8fTjBlbzApUsJxutK3dk48glwpF053L6pvWeL2cGdzEPgAj5xOkElLKGQv7LAC5gCaXEuY4i9sMGZm+qmj3OO1NZhzVjMs8lrOZe1gFZ4EqAtb4WfR5BybQTeZRzeL37iOH4P+bzNMuJsJY4DkieriyHRJeeGmBO0vrEQO196cIERtYpC39PjIMoYQGOwcCHFDAgjTKSw/EQ83iJJbzBSrRSNNhvZ6CKCLCK/Si0qvIWs9F3WBORzs65denutKnpjTGmId9SDTiozoKarXRhdCFkLwfRU00F5fTnB46jK3fRm6ykNvIzWMkzRIDexOlBhHkMIMZ8ogiwGRHWEPXt1xXAMnIRqqjBUY4G9URf3Q8oZSblDCKXXF9pvhVR5tGNSqBTI5OqxHE8zWy+pYQqsriNOaEttIe50jbvOI5YM5sHzMYn3Uu4mSJyN/CEc25xqgQiIsBBwBXAB8DNLZNFY8ym7njyGR+vgZpQG29sAGT96IN3BOK7sYg+3OccKyIlPCuFPMpC/sgPZBNlLkNDe8wmTmf+jyib05NVxFnECu7gKzRYZiM4KqlBB9gEHcUGQG07c5zh/BeAm9ie3zGCpZRRQA75KQfk1HUdX/BXphFFiJGHkBMKzcFfWWgVfDWXM5QeaezXbBrSDd6jgb8C14vIl8BkdFhYBdAd2AbYA/2G3wz8o8VzaozZZF1IZyqIckXS8q1cFwrpwxS3DfFIoe+gLTwX70r3yDz+ITN8GIyjHcAS1dEDcFzNEPKIMpViRvGD36veA0y3K6HOJOQsRu+fWQN8R9yvu5opvMK3fMRCOhHlOX5KV7L5jKUMoDMF5LAHfSkiMQ3Gk8wCIOZvou3Irn1t/T8PPUWXcgKbcTt2g22T0KQ2bxEZBJwI7IM2FuUBK4CpwJvA6865Dj3lj7V5G5OZnIPTquBZP7PaoMg6vsjJBYnSI7k6OS4gX4AsQ09TOUA5MBQN4EvpQwmT2JUCstmbd/mWcJNnFyLkEWcVsDK8Y85jO4qp4vnaYI/fp95tRIDOZFNKNYmLBsgnl1zyKCPGJWzHJFYziWW167PIooauPp/BzUIdBfRgGkcyiA49/1VGyuQ2b+uwZozJGHEHH8U1TO4T0TtmVePIc9XEiOq9RXEQWwmR+XSVUtYSR2cej6NV0EHLcgEwj550ZYXvvpYYmlXDnozgc9ZRwTdocA7aoXsAq6lbIhd02hgVIZhKpTq0JOr3UQ0IWzOM72rXV6Cd0mLEWQ0EM89oXmdwMsMb6Ulvmi6Tg3eG3S3XGLMpiwjsE4X9o4lbXWYjbCdfARXgasDNhugqkC50pwfwPVpBuMo/SkMPWE5l0jjxOFDNJEqoYCmw1G8f8+vmotXptXfpRkvLicnWt6YbkTod1oJTbZl/3TK+o8YvjwD5aKe0GOGJWQI6g5sxCTbmwJh2NKsczvse5lXAeX3hdwN94dE0yU304Sh5kTg5wPa1y+fXqfIGDcBVaGAM1tVQ91S41KdZBqxFg2vML4+SCMTVaHV8+A4pGviLgTiVfr/V/v841AbhbNYvO0WAYv/anWvzNJxu7Ebvxg+C2aRY8DamHR0/Haav09Bw9RwYkQ/H2KRZTbKMNfyDh+nDMlYzivI6a9dvJ44gdCOHVVShgbsTGmAjaHCthtrOY93RgBpcUVVD7a05g6AcptXui1iLltCr/P4HooE+UAlUIuT6Un8M7dWu1ehbEWU/tmQUfTiFLcizU7VJYtXmxrSj78qovaljBPjGakeb7Fzu5BWmsIjBlFMDzA6trTsXeA9y2ZciPuAI/sVeRJhOovo77h/hQClosA5PzRpFO8E1NLP6IrQqPQas88/XhtY7hJlczubsSA7wHYn2cfgXx/EQB3IhI+mW5n28zabFgrcx7ejIzRJdmQQ41PokNdnXzCVOf7QDGsB84CO0nboG7WCWy0H0pIQfmcCXHMWTnMjDxCkFfiARwCX0d5hWh2uJfCSwNTkMh+BuYnUeNWhpPbyfKv+8onZ/3VnCZfRnLHsQCV1kdCWXLdlsA46I2RQ0K3iLyD4i8pSIfCwi/f2yM0Rk75bNnjEbtyeHw41D4bx+8Idt4fE4PF+uw6IAxlHBJZTwMGW1/aFNXceyZ+hZHK1+/hqd2lSIUMh2bMk3zKPGt0/PoRjoCvRE274/Bv7n/14W2l8MvQgoQzupLQXGAdOpQtBe4TP9NjGfZrr/O9x5Qecg78RC4EvgS4pZyJ28xHB68gwnsh292Y0BvM3/kWeTsZhGNLkhRUR+BjwJPA3sSKIBqBC4BhqYXNgYU0deFK4eBPeug0tK9EZU95RBSSF0z6/gBFYToYY4cW4hmyPJ43oK6WaVZrVu5Vz68F9uYQmrmQn86Ncsoge9WMWHTKMbOeyftGUMPX0VoT3A+6ATsHRCA39X9GIgFy0556Gl5wg6tUVwUxCHlrQX0YU8SilH6ISjhkEUsT8/4QB25gA25yiu5QtKgWwcsdoLspPYjpPYrjUOj9lINecMcC1woXPuPMKNNFpPNapFcmXMJuZFX5saVJ7+pwL+SwVQTpxyoIpZlHEPazmRFe2Uy44piyi/5XiWcAE9qDtUdxVrgGHAjlTVVnFD3WruCLAlGsSr0VnUQEvbc9GSd7gUnYMG9KUkblxSCiynlPkI5YyiD/swlPnM5Qle5u+MYTM6cS5H+fwMBYbyM/ZruQNhNinNCd5bo3OXJysGum1YdozZNI3ISnR/igDDs6ArcbTEF8zSFSNOnPdDk4GYhByibEWfpKVRoL//uwpYyxlsjXYeC4Z4ObRTWQXwOPAy8BYwhURbdbJq4DOovcd3Re0aB0zjOyYyFR1XvoIpTOU1/scrzCXiKzyFLMbwRbPfr9m0NSd4LwG2SLF8b+p280yLiPxSROaKSIWIfCoiuzaSvpuI3Ccii0WkUkS+FxGrqjcZ7eaucEIn6B+BUzvBDV2gW1JPaQ02a9nCqszrmMx0tuck+nMIe1JEXu3xCQ/pSvQoF8qBz9HAWg7MAL5CO64Vh/a8DFgOLKRuJeMav9z5dT+iwT9RqtdS/lr0oks7qr3CRMqoqjMhTHmd/RqTvuYMHvwncLeInIN+K/uJyB7AbcBfmrIjETkZuAO4EPgUuAx4U0S2ds4tS5E+B3gb/VWdgP5qBqO/JmMyVkEEnk3qaV5UWxavRqtw9aYaRdjddgMOx5H8muWsJk4Vd3A9icaHInR8dRe0vBEFZjOGqWhgHY92MKtCx4P3QqvEw6XtSv/8a7Q3+zq/rBOJsk8liZueBGPFF5O4+5gqZh1XcyyTmEsNcXLI4tfs24JHw2xKmhO8b0G/oe+g3/gP0G/vbc65e5q4ryuAfzrnHgMQkQuBI4Bz/OskOwcd97Gncy64ZJ3b1DdgTCY4lwKuYDbUacfNothuUFGrnAqW1s6Utpa647qDoWLBOOoitBNaAUIuWawmwmoq6UGiaj0HDfJB7/JstEd6b/Q0V0Zi2FjUL8vjYHbhbb4l0fgRJTFzm94l7EwO4zBG8C2/ZxqL2ZmBDLCWRtNMTQreIhIF9gLuA/4fejnbBfjGOVfa0LYp9pUD7ETovt/OubiIjEdvL5rK0eiYjvtE5Bi07mos8LeOfjczY5rqK9aRqMZNzPC1W52ZujZt+eRxMLsznk/JopwcynEIFeQQrw2kNWgwzQW6I9TgKKSaPchlnb97GCSOcRQ9tQTLtidRyu6JVrFXEL6oWs6wUJpsoBd9yWUtNRTQk2v4P45FR9JuThGbY9PomQ3TpODtnIuJyFvACOfcGuCbDXjtIvRXsjRp+VJgeD3bDAMOQIepHY5ePNyP/lr+nGoDEcmFOlMUdW1+lo1pO5uRTaI3dHDzi2re4nuWMYBedGrX/HUUL3EHf+J2HuaPBPfhzqKCktqJToLjuI4uTCCLlTiirGMf4hQRYQXx2sAd3PUrMIi6M6l19c/LQ+kdM5kG9CW4ABAiTOdFutfeHcyYltWcni9fo0G0PUTQ9u7znXNTnHPPATehbeb1uRotvgSPha2eS2NawHA6M6R2jHHQ9uqYRznn83n7Zm4DzWc1f+ZtbuN91m5g7/l88tiJwQTt1AJEcAhZ/lk3BOjEl0Rrq9hj5PE/oizHsYZEFXs52pUmuH1ouEIxmIO8ipFs6ZdVARWUsgidHEbTOVawhoy6w6TJMM1p874WuE1ErkPHUtTpPdOEe6IG99hLvl1Ob7RHeyqLgeqkKvIZQB8RyXHOVaXY5ma0U1ygKxbATYYYz85swftokEj4sk6v6MyyknXszD2sogwHvMA0JnExkVBZopoaZjCXPmxGr0buY72MRazhR7LIIk4cQcgim04sRXDEWUINNQhCDCgj20+nmoMG5xx0vHYe1CmBVwCfoAF9V79uKm9wO7dzH9OpJjEzveYkaA8/iN0YwoANPlbG1Kc5wfs1///L1B0AGUwK3NBs/bWcc1UiMgU4EHgJQEQi/vm99Wz2EXCaiEScc8Egza2AxfUEbpxzevueIJN2v0WTQTYnnwvpwYMsIxzA15G5XTwmMpfloWv+T1nAj5Qw0HfeWsNa9uZipjOHbKKM5XpOWG92NHiQv3EPN/ihX45uQF9GMoq9eIyHai8FxHccq6AAYZUP3Hr/bOhOH8qoxNWO2FbBBC4QTGcK+ZzIaRzKfhzLGSRuLZrwVy5mIP04iSMQ7FxjWk9zqs33Dz0OCD2C501xB3CeiPyfiIwAHkDnHAx6n48RkZtD6R9Ae5vfLSJbicgR6JSs9zXjfRiTET5nFvpTzUdLiZ1YTjlv1c7ulVmGhErSAuSTzWahHvQPM44ZfhBJNTEu5a719jGVT/kbv6eastpx07lAJQu4gqtCgVsHeDnycER9x7RcwiXsanLpC+TX3ngkG9brFKhToHajmjhxhjGYCCVIaHKW/diVq/klP+c4cqxToWllTS55O+feb6kXd849JyI9gRvQiYW/AA5zzgWd2AYRutO9c26BiBwK3Il2+fwRuBv4W0vlyZiOoJgKfs44PmA+pdSglUfBPaFjwEquYDxfc95621ZSTZQIWelVgrW5HejHvRzDDYynMzk8yPHkh4JdTe1NPVzoeV2Lffty3ao/IYs8DmZvuqP9xTdHh8NEibMIvfwpoxgtA6gCSvyr5TCAYiCHJRRQU3u/b4A42cBYHmZ7RvI3ruM4fkowfMzh2KZ2uJn6hC+5lr8TI871XMx+7NLMI2bM+sQ513iq5I1EugG/AEb4RdOBR51zHb4hTkQKgOLi4mIKCgoaTW9Me/g1b3MfU4jVhqcoWobMRcczr6CAXIr5Q+02Dsdv+Re38wbZRHmQMzmbfdo+8xtoGavZjfOZy2IE4WF+xzkcWSfNGlZxCCNZyVKycWQBOeTzPYPpxXzyWec7qSVu2jmPHlRT7G8I0p0IOfRjLV0oo5IcZtOPQubTjzhVwEy6+z3E6cQKosQQhCM4hmd4kRFswyy+rS3Dj2Esp3Cqz18JAzmIMioARy45zOZN+tgQsQ6lpKSEwsJCgMIm9NfqEJpzV7GdgTfRXhyf+cVXAH8QkUOcc5ndDdaYDmAOa0KBG4QYjnK0f6hO67mWdUxlITv6jlEf8gO38QYAVdRwDo9xGNvSt5EOXx1NL7rzNWP4HzMYQC+2SNHxqxs9GMcU/stY8ujMg0xgEpOAxcwhmz7kUERVbX/vMvIoJ58sViFA1Ldwr6WAtfSimC5EqaEfcQrRwWK5rCZGHpvRlTJcbRXgzuyKIHzCx/yGy5nJD5zISZzMKbX5m81CSimrfV5OJd8xx4K3aTHN6bB2J9pZ7TznXA2AiGQBDwN3gc33Z8yGOoVtGMdMImi7kaMMoQSorg0jArzKjNrgPb92GFSwFl5iChdxUFtmvUV0Jo/RjKKGGFfxAq8yjVEM4l5OZQUreZvP2IpBnM+VzGAWk7iL8LCuJeTTj6raLn415OPIWq+H7VoKqfG3+tyWeXTxy3PQdrzt6c5zzOLP/IHP+ITRHMhlXAVAN7rxiHbPWc9WDKEnPVhFMeDoSme2Y6sWPkpmU9ac4L0zocAN4JyrEZFbgcktljNjNmGnMZIedOIq3uIbfiRONQ5HFuXEqMaRR5woW4ZKcgczkuRbXo7lfS7kwIzt+XwX73A7b+OA71nGGkp5l39T7geQ3Mal/JxDkTq3+1BZviW7M9CDSpawGTH6kMdSOvuq9nwWEKMTeVTTNdS2LugsUjUsIgLczO1NyncX8vmQMdzCw8RxXMlZ9LAJW0wLak5v8xK0I1mygWhjnDGmBRzG5lzCKOK1M34toYbFOFYgLOIy9uIkdqhN34sCfs+h6G0qlwMr+JCv+LzpN/vrML7iRyL+wiNGnE+YRVVo/vK7eY63eZ1R9EWI0oly8iijB5WU4Wp7CgyglG1ZQVeidMORjZ78KoEeVNCXGJ2TXrsL0IfBZNeZoLGuB3mY3dmfUzmLJUmTRW7FEB7lRh7jRubzGQ/wR6bxaQscFWOaV/J+DnhERK4EJvlle6FznT/TUhkzxsB57EoFNbzEVN7je4LqcEcN85ixXom6KxG0xTYIcDV12l4zzWGM5Ek+IUqEOHG2o4gPfLNBhAhCNedwJllE6UKstn99HA3M2eho7AiwDavog87qFAhGc+f5tBH0yEWAznTjel6vt9biVd7gIi4DYDKfs5Afmcjb66W7n+t4hJuIEOURbuKfvM+Ofp5zY5qrOcH7SvT7Pia0fTU6Bvv3LZQvYww6/OlS9mIrsnmP8bVLQXiFj9dLn0cOJN0H/C0msx/btXpeW8Np7IoAb/ENP2EAv2APTmIlb/IJ/ShiIGUsBmLE1isfB/f+WoMG4+6w3t2zo+hJrAYN3tr6rYPxbuMdBtYOqFnfZD4nSoQYcWLEmMIXKdO9whifnxhRshjPCxa8zQZrcrW5c67KOfdr9Lewg3/0cM5d7mczM8Y0g8PxMl9yK2/yVdIMviMYVGdCEKgiixincD63ci81PmCPoC+hqREAx4LaO2S1vFu4k+4MYSjb8x4T09rmex7nObbkOUYwh3GNpj+VXXmMs7icgyigM29wNxVMZD4vszs/IUKkznxowf+5JO79NReYCvzg12Wh1eK7M5zjuZBin1aIUEghR3AC73ITE3kgRWu6Gs0+xIgT8f8OYL+U6fozlIivE4gTox9DGn3PxjSmyeO8RaQQiDrnViUt7wHUdPSxcjbO23RUN/M61/ASEYQoESZyFbsxtHb9i0zkTG5hHeVEEOLMJ0IpDsfVXMpFnMOOHMcKqqC2I5vjBf7Az+oJLBtiIpPYlyMArSEooCvLmUk22fVu8yNv8yaH1IbDGHAME8hhLXlsQ6d67nlUTTXPcx+LmMMB/Iyd/KCWdazjEi5mIh8wkhEsZyGz+IHDqWAZMB/tg16DXtL0ZRDP8Bof8h9yyOVozieLLL5lEr3Zgr4M5T9cyXvc4fPoOIWH2CvFZDgA/+UVxvI8gxnEtfyWAtY/pyxkNr/nZObxHQfwM67lH2TbDGwdQiaP825O8H4dGOecuz9p+YXA0c65w1swfy3OgrfpqIZwNfPQa+IoEX7JaO7m5Dpp4sSZzWKu4jpe5jXiPsTszPYcwk/5Gw8RI44OdsphX/bkPe6uc9OPlvIUz3FG0g39VjGH7n6OcoAFLGAVq6ikjNu5kTVMZw/m1fZ4zQG2pBNa7s1iK/5Dd46kmjJW8Bk92J5cuvNnzuVlHiVCFEecR5jI9uyZMl/lLOdheuHQHrQCjOJWurM7O7AruaEK9oVM5yZ2o5p1RMjlt0zgX1zAIr4CtCQ+ipM5i7EtddhMB5LJwbs5v+jdgPdSLJ/g1xmzUSnG8Rg1POsHabWW/nQn6n+ScRz9Q0EwECHCFvRnd3aszUmECDuzQ+22AEIV/enMO9zZKoEb4CBG04NuvuOYcAgH0C00HOpe7mcwW7ADu7APo3mbN5jMfB4kMeuZ3nE7GI0d40f+wiImMoYuvM7+PEUPJvMA3zOWI3EcSg29gYm8Wm++8ujJrlxfu+cSYAK/pZi3WMyrfM8jVPgx8Q9zKnHWEQUiVPIoZzGUPRB/zByOQTatqemAmtNhLbee7YL+HsZsNEpx7EIVP/hQeQQRxpHdKuOmH+VMjuMBfmAZR7M9lzZwn5/fcBHFlPAWE9iZHbiNP1FKOWN5hVnMJ5ts/sENZDXrJ56ePvTmc97nSZ6nGwWcy5m1x6Waaq7gqtr24io/RUoOjkpgFfkMpQt9+AlVtWUBIUIeb3Ai4VnLJ3Mp2/k2fQfsQZyBDG4wb7vxJ3IZxrOcWbvsXW5kMno/4E78kl3YjRV8V+cmoPksZQfilLErq4izDYczmks39FAZ0+KaU23+HvC1c+6SpOX3AT9xznXoyZSt2tw0xSvEOCqpj/J8chnYzpOeTOVz3uUdfsL2HMwhtcsrqWIGsxhAb4pCN99oa1VU8f/bu+84Ker7j+Ovz+4VDjiOKh1Bqg1RVMSu2I0l+lOxRiWaWBI1YtdYojGY2KMmxhp7SYJiI8EuTRFpIkqRIuVox9Gu7n5/f8zc3d5yd9zulb3h3s/fY37uzH5n5jMb9j77nfmWVrQtb0gH0IooLYDWtGEmP5JDDkUsYy6HUsxiHNk4+rGIGRSVP3H2as/55ceF74GdOZXTuYqhHF5tDN/zAc9yfPl6Id4z8DLZQGf/2N7koHAChhEGonTg5+zKm3X8JKQpC/Jt82R+lt8KTDCzvYAP/W0jgP0g5q+IyA6gY1ySToMqmiQ1rk/4mOM5mihRHI6H+SuXcQUAmWQwbxtp5AAAIABJREFUpIbuTY0lgwzu4S5u4GYAdmUQQ+lFiO8ZwC4s4ytyOIpMerIX31PEj0zlSIqYQRsca6moe+cxgBA/ADAFb2T3hbzFR4zll/TjTN6lTRVDj/ZjBP04ikVM8GfvNmJr9JvwusqEgRbszv60x5hIWVe79bVoCS+SKsl0FZsIDAeWAWcCJwEL8GrdtesrIhIQBxDiFv9pcgvgGdLJSXGt+yVeACi/Jf0P/l7nY77CI5zJYK7iJFbG1E8L2MIsJrGa5Qkf8wgO4EJOZBSn8REf0JuVwI98zwT+zHEs9RuFhcggjS4UsQJvYk6vRpwBrKU7M/mBOXg15HWUDaziXf33LOR9DuA1OvAxJ1PMBgBKWcMmXudULuFQurIXcCBe7aKsxtIKSCfMbgzk98xmf35HRTe7MFkMSviaRRpLUg/EnHMzgHPrORaRJulu0vm9Pzt2uAmMEd6VbuWJO0yYHvSs0/E+4x3+zFUA/MhcRnMaLzGNXH7ilwxnNT8RJp17eYPDOKVWx1zIPM7lSKJ+MryUH8jhm/L3vbHKv6AXgwFIow1t2J98f6LCEF43slz/R0MJsAjvVvdmKurPHXEU+zOELec9vuEW9uFWvmdPClhHPrEDszh6A78mhy8YTH/S6UgPDuBuDKM9p7Az95LLP8hkZ/rxZKIfpUijSbjmbWb7mNmeMeunmNlYM/ujmanzouyQMrAmkbgBruMGjuJowoTZjd15mL9uU2YL+SxiBoUxQ6M+yeOM4CB+yfmsiRm45QdmlrdIjxBhAXMAeJPHWMtKf3spj3J9tTFtIZ/pfMA0JvASj/ICj1JCCRH//2bzDWm0rLRPsX8e8G5p78sHtOcUthJiDZDD/v7ZK5zGoezNUDoBIzC6xbzniLCJhSzhKjawjiIqknwaXs/3dkAb8hlGJ/6PcRzN82T7P34Moyc3si8L2ZOPyKJftdcrkmrJ1Lz/DvwJmG1mu+CNdf5v4AygJfiD/YpIg8gmm3f4AIfbptX7ZjbyCNfwIS+QTgkd6MgYJvINc/md/1x8GlPJZRXj/HG4h3EUT/B7fxQwx3COBqh0bItbj7WO5VzPfuSykiVUJMwMKjqBGZDJ1vK5srsAebwPQB4/MIe/k04rhvAMexGiiPW0pjcrGcUnPAfAUE7i14wlRIj1fMNy3iOPBUziOYrxRkzbn1NZxZXlsWX6MWRDefQRYCP/ZjJ9OZT7av25izQlySTvAVA+iO8ZwKfOuXPM7CDgVZS8RRpFVcn0Wn7GjErDlK7lZg6lN78gTLi8Jvy1f3saoA8DuJ83+Ji36UxPLuQGAM7gSsbzMqtYQpg0ruIvVcbxIU+TTy6biG0OBq3IJJN0trCZTLy+3XtR9kMgTCt6spVcXmcYJf6EhIsYy0i+IdPv4/5rnuFYriRCKX3Zr/wOQXv2pj178zI/ZyshHFHygFV8Rzhmas8QXgPD9PIRzL0Yw8C6SlOUiARLMsnbqLjdfhTwjv96GcRMLiwijaqQrZUSt9fhCXLJJcQ7RIhgGEaIwxgBwL+4m9f5PeA4idGcxx/K9+9EN17jOxYxh870ogOdqzxvGhk4Kv8xCRGiMz0YyeX8mWsJk8ZMSjmcvdnEbKArQ7iHlUwub2QGsI7ZrONriphNC3rQgWPYhaHVXvNKZuD85+pGiJ94hGHAWrxUHSaHrlxGC3qyxL/z4D1Lh4M4uZafrEjTk0w/74/wEvUE4GlgN+fcAjM7DHjeOde73qOsR+rnLTsqh+NkerGGFTiitKFyo5YjuII55NKbPtzAbRSynivjJsm4j5ns7Dciq60tbOBWDmMxs1hNiI04OtKFh/g3gxnGWJ5jJp8xlCPpRSce5CQilJJBS67iWT5lJOAwQqTRkr6k+/Vo6MNNDOCP1Z77Pa5mMo9gGI4oxwC98RJ0HjCIWWT5M6ptZjoLeZp1bKEbxzGQsxpksB0JjiD3804meQ8GXgJ6AQ845+70tz8KdHDOnVPvUdYjJW/ZkS1kDmO4jCXMxbG+PDUZIUYxhjMYXV72J+ZyLbtX2v//uIq2tGVvzqNjNQ22NrCYCVzPVtayP79hED8nQimrWEhbOtOCbML+LFpb2cDjHMcSptKaTqTRgyXMwOEIEWY4Z3M4x/Ald5JOawYwnLU8SVmXrRBZHF3DfOQRSpnKoyznDboyudJPkU10YCA/EaJFoh+jNBPNKnlXeyCzFkDEORc/ZW6TouQtydrKtyzlGkrJpyuj6cAZqQ6pWg7HGE5lCm9TALSiLU8wg84xw4o6HPdxMtP9J1/taEcOeYQIk0lrrmEOOfTY5riPM4g8FuKIAMYv+Zqu7F1lHO9yO+O5G0cUI0QJrdnCZhxRQoQ5lIsZFdMl6yee5lt+6a8ZmXSjPfcyg2doQy+OYgyt6bLNeYqZxWqG4o0KHwXa04lPacEedfgUZUcX5ORdbwMfO+cKt19KJJgcpczjGErIBSIs4CyyGEjLBG8xN7RC8nmfG/mWt8hnJX2BdvTnPJ6ulLjBa/B2HWOZxQSK2Mi/OdMfgyxCIfnM53/sy0WV9ilhC+v90c48jpVMqzZ5F7DBv6UNjijtyAHS2cw62tGdU7ilUvluXMBa3ieXf5FGDh25hP/445MbYfKYz0VM2uY8GQymEx+yhWcI0Zk23ESoioldRHYUtU7eZraoNuWcc1VPyCsSYKWsp4QVMVscW5nd5JL3m4ziW/6DI+p3/II85jOFQynlDvpxe6XyIcIM4ViiRPiA9hSyobwBWLuYucTLpNOKTuzGOr7zB4oJ0YPh1cZzIJcwmacpZgsAJ3Ib+3E+efxEB3qRFjevdYh0hvAmEbZihPiU7uWDmjoirOCras9VRHeM0eSwu55lyw4vkZp3b2AJ8DKwukGiEWmi0uhIFrtTwDwAjDRa15C0UmUJk8qTr4PyxGfAQu6kF78hI2bCklm8yCfcRhqZHM2tTOQJCljPIVxL3yom/SilAKOEsN8prCv70ynuuXmsbuzBLcxlIZ+xEwPZ2Z9es/N2BkAJ05IIs+nK+vLuZwb0qOY2+DRuZhb3AtCLUzmC5yjht0SYRJgjyeRhTM++ZQdS62feZnYGcDFwOPA+8AzwnnMuWtN+TY2eeUuySshlOfcQIZ/OXE7rBp6+PkoJIdIT2ucVzmEWr5Un8FK8ccIH4/Xj7MKBtOR42nIT61jAE+xKWe/sMJmMZBx9GFE+n3W8hbzHG5xYadtlLCGHXoldXC041rPFdSOvtIjCKGwIw25p39CKIZXKbWUVr9K10rYRnEoHxuG1Ow+Rzo1kck+9xyjB1iyeeTvn3gDeMLPuwIXAg8DfzewF4Gnn3PyGCVGkaUinM715pMHPU8pWpnI6uXxAS3ZmOO+QU8uGV6fxJG3oxlp+oCcHEOUtSvmSTkBbjI1MYg6TCPMWJexB7LAqEYp4iWMYxGn8H29Wees5iw6V1o0QpeUTdtYvoz1ZpWfQuuRF7+5BBGAupA2JK7ltBSTCQigfrCVKNGYoVpEdQTKzii13zt3jnOsPnAMMA+aZWbt6j06kGVrIw+TyXwC28hPTGVXp/WI28Q1/Ygo3sqFS4zHIpDUn8hd+wdscyc2MYBIHMIX2DGI1jq+A5cBSprGC5/ypMiubx7/JZWaVsXVlfw7gRiCEYeQQ5T0GM5cHAFjBl3zBXczjjfLJU+oiXOolXS/GEETe3aZMS7qye8zAjt05lm7l8yal+f//hDrHItKUJNXa3O8W9n94t9GHAW9ADZ0xRaTWilhd3kLbqw/nlr/ncLzHCaxiEobxLU9wFnNpTffyMltZRR5zac+eZNGJtgxjEcXkUzZAqMfwpjk18JuTVQiz7RxDX/E+M/mYfgzlTF7mC0aWJ/7pXEcWe/A6xwOGI8Ih/IGDuLVuH0ZoMERm49WiHYR2q7LYMB5kAKMopYAO7IMRIkwHIkwlzKGkcV7d4hBpYhJK3mY2DBiFN4/3Irzn3qc75/IaIDaRZqkn57OIJ/x+1NCX35S/V8g6VvEF4N0sLmEjK/iYAX5yWskXvMMxRCggnWxO4VM6sjeb/dbnISpmrHb+kgZ0ZjC5/vza+/EbOrIrpWxiHr8kj49ZzM68yDTCpBGhlDO4gMyYmB2OebxJWeIGmM2zdU/eGQ9DcRFEp0HoOEi7rtqi7eIeLaTzS9LL+4yL7FgS6Sr2LbATXmvzw5xzVd9XE5E6acc+jGAmq/kfrRlIZ3+WL4AMcsikHUXkU5aGc+hf/v507iZCEeA9O5/BfaQxiudYxgV4jdfygEIonzLTCDGSd8rn3m7r9wf/kTtYzb+ACNNYi+GNaAYwg285gf1Y53fd2pNbKaRVzDjjYXLihl5NirWFzFfrfhyRHUwiNe9d8e6uXQCcb1Z1P0rnXPsq3xAJoCgFGC3qpd9wKYVM5A+sZS79OJHBjKr2uNkMJJuB22w3cvkZ1zOFZ9hAAXtzCcU8xI+spQNXYqSVdw/zyqdxN7cwnQgb8L68WXg3oYuAtvTjcP5GG39O61hbmU9Zo692Mc+vQ4TpwSCO41nWMIUM2tKOPSmlkFxmsoC36cAgjuepOnxaIlKTRJL3RdsvIrJjiFLAck5jKx8QpjPdGUeW30c5Wf/jt8ziaRxR5jOWNLLYvbxh1fZFmEYBh5HFVo4gjUzGsoBrWMoCojjWM4EhvMQqJlLMBjJde/Zx3TH7DwZ8arAUuBJv7usedKEPu9Kumh8QO3E66xiHkcZwSomwL/NYzAD25TIeIkQ6nTmkvHwaLTiFl+v0GYlI7SSSvH8EJjnnSrdbUiTgNvAEWxkPQIQ1rGIUffxnwslawkcxt5XTWMqnCSXvEh4B/5Y4RCjmHlYxn2J/y1agM99zHkvZyCJy3AeEojdySxqc46AA2Ij37KsbAKvYzDts5gP68y2ZMbffAbryC9LIYQOf04ZhHMMZGrlMpIlIJHl/DHRFo6tJMxBhLV7zrggQJVIP/+y7cQD5LMYRwVFKN/ZP8AhZMa9DGK3KE3eZIorIIJuO7IUruZKog8MiMCsDfnSwi0ErvC/xWqCs2VsB07ZJ3gCdOJVOnJpgnHVUuAgKF0LrfSFNPVBFqpJIP2/95JZmow3nYzHtqdtxTZ2PeSxPMIRL6cHBHMF9DI7rv709GdyC0ZNQLqTPziBz5elksQuxX812HO+9KJkGm78gvAlsI2RvhD0c5DjIBHoC2Rj4naqyeIlidqOUG4kyD0etpjKof2vfgK8HwLfHeP8t/DE1cYg0cYkMjxoFOjvn1jRsSA1Lw6NKbRXzI1v5H+n0pRUjUh0OAG7pa9jnI3GEMKIUHfAn5vT9hCJW0ZPL6cklXsH1u0NkbqV917aDnLB/u81gI/3Jox+d2UKYiVSMSOYJcQ1p/uArjebrgVBYNvBMGLpdDX3+0rgxSLPRLIZH9T1nZkU1FXDOnVaHeESajAz6kMGlqQ6jEpvrJVPzn51nLnqPoX0/3bZgZOU2mzIKIM0fkcG1haw282kdvYqIPVg29mglUR7EcQVG3+3GFWUzRiaW4Fjs2whlQmx7+dC2g8WISOLDo24C8reziEhDKNkI6+KmxHSFVZdN3w/nKgZi2WyQvQjYCuYglAdpuRBediWhTUv9glX9Oai5faojyhouYgnZLKEtW/hP4tcVq89DEPKf7bfoA12vqtvxRHZQida8f+ucU4M1kVTYvBhwFZ24i4H1+RCNQChcuWyrR7GtA4mEoLAEWs0BKxtaLQ0ogdCu3utwXglWCtEWUVx6e0hfD4BxPjCgxpC28jabeQ7wZjhfw/m0JB8jXON+1Wp7JOy3AoqXQ4t+qnmLVCORmnfdZxkQkeRl94f0LK+Z+BJgJbDwe/j6scrlIptg2cmwBcKboNXSisRd/iVuA+5doMS7SR3eBOl5kL5mPWmlb5LGl6TxPIYRYQrFPECEz7cJKUrlkZEdW3GU1O0603Kg5W5K3CI1UGtzkaBIy4Jjp0MkZlRxC8OqGZXLLT0dIt9XrMd/y0NAB7C+wLKYbeZ9yUN2ECH2wzBKeZsCDqSY0RRwKCVUHqq0JScTpkf5eja/IkSLul2niGxXIsn7CGB9QwUiIrWQMwgGX46XaUPgItDnqMplVk6pvN4Oon6V20JAR78G3gqYA0QO8/8SGGSPgXAXr3B0HSWlN4Mre3IOJTxZ6dBhOtCdmXTkeTrzLh14vB4vVkSqU+vk7Zz7VKOriaSYczDkQmi3K5REvRlG5ozzE6wvbbA3DmqZH+GjTFjQB+gFLsO/jbYYWAjM3g+6rIIuayH7em+f6FZYMwwr+jbm3GHMH5stVpj2ZHMBLTlBI7CJNJJEW5uLSKo4Bx9dBC/tBT/N9UZKjQCzXoWVMbfO9x4HuTvDO3jLFNiUDX/vDxOz4KcofBICvvHLh9Ig3BlCMXMKlXwNkYVkboJQMd5U2pEsMrmvkS5WRGqi5C0SFGtnwLznq37PYr7K6e3g7B+h/VGwAXCw7yy4eBEMWw8uDJkL/LKtcuDQ3257vLBXw7YotFwHrVZCy/WbCUUT7aAiIg1ByVskKJw/kEoa0DJm+z4XQpfBlcuawbnvQuYA2AQ9DQblQ5qDnptheBtgnz5w3XTI6brtudL6QuvrKg4XBiwTrHX9XpOIJKVWydvM2tR2aeiARZqtTvtAQR/vgXVbvCnCFu4Opz/jJet4aRkwegYMPAuyvMFZwC/qgC1L4bOLYeulsPU8KI0bACZ7DLS5CdLDEG4NbV4Caxl/FhFJgdreA/NvvtVKkqMziEiNLASbT4CxT0AkCmsNhrSpOnGXyciCX70CE7uDPeB9iw1Yg1eTX/cFlHzhlS35N2R/D6Ge/vkMWv8RWt0FhGs+j4g0qtreNj8CONJfLsabUfA+4Of+ch+Q678nIg3lkuvAdfO+gS1awg21aEBmBgffD7tNhuyL4Htgjf+7fecIXqu3CFAAkWne9qVPwvh28N8OsOhRKMxtkMvZhvsGok+Am9Y45xMJqFrPKla+g9mHwFPOuVfitp8DXOqcO7z+wqt/mlVMAq+wABbPh269oE3bxPdfNgEWvwNtd4bu10Io5m9A9mwobAGfDKD8ZpsDCoA97oVdb6yHC6hG9H2I/gyIAgahNyGkeY6k4QR5VrFkGqwNB6r6WTwN2L9u4YjIdrXIgkGDk0vcAD2PgkMegkHDvdFbysZgKcXr3124gkpPycxfZt8MRWvrGn31on+LOa+DqAZ8EalOMsl7GZRNGlzJL6kYbDEhZnaFmS02s0Izm2pmtfoRYGYjzcyZ2dhkzivSrIV6gkvzknYp3utQD2i7H7TsD5iXSyN4lWEcROs4bnlNbCcq/iSF/XURqUoyyfsa4DdmNtvMnvKXWcBv/PcSYmZnAQ8AdwL7ADOB8WY1f3PNrDfwF6hitgSReMWFMPVtmD4eotHtl9+6DhZOgPykfo8GQ7g7tHnZS+Khnt7rcDcIZ8FBU2DQGMjYxRvFDWCXSyGrim5l9SX0B2BPf2UQhMY03LlEAi7hZ94AZtYTuAwY5G/6Dvibcy7hv3RmNhX4yjl3pb8ewqvBP+qc+1M1+4SBz4BngEOAts65U2t5Pj3zbm5KiuDGQ2CB3xXqoDPhulerbz29ei48fRAUboBQOpw7Dvod23jxNiXREljzuZfQOxzQOC3OXSGYJjeRhtfcnnnjnFvmnLvZOXeav9ySZOLOAIYCE2KOHfXXh9ew6++B1c65p2txjsy4fujZicYpATfn04rEDTDxdViztPryUx6Cok3e62gpfHx7w8bXGIo3Qu5E2Loqsf1C6dD5SOg4vPG6iilxi2xXUsnbzA4xsxfNbJKZdfe3nW9mByd4qI54/cLj+6HkAl2qOffBwCiqfu5elZuA/JjlpwRjlKDLiv+9ZpBZw2AjofSYolZ5PYg2LYJ/94f3D4Y3d4bl/011RCJSRwknbzM7HRiP13lkH6BscuEc4Ob6C63Kc2cDLwCXOOdq2+z1XrzYypYeNReXHc7AA+CEK7zXFoJRD0BOp+rLH3w9ZPvPdtNbwTEJTsaRtwreegDGP+k9a0+1bx+EonXe62gJfH1TauMRkTpLZpaBW4FfO+f+aWYjY7ZP9N9LxFq8tqyd47Z3Bqq6v9cX6A2Ms4pbeN5MxGalwEDn3MLYHZxzRXjzL+GXSzBECTwz+NVf4ew7IJzuTcZRk7Y7w2/nw/oFkNMLWiTQNmJzHlyzD2xY5c0CNvF1uPN/qR2drNK5TSOliewAkrltPhCvsVi8fLwRl2vNOVcMfA2MKNvmN1gbAUyuYpd5eM1Rh8QsbwMf+6934KbBUmdtOm4/ca/7ESb/HRZPhM57JJa4AWZ9BHkrK+bXnvmhVxNPpd1+By38zhvhDBiqVtwiQZdMzXsV0A9YHLf9YGBREsd7AHjezKYBXwJXA62AZwHM7J/AcufcTc65QmBO7M5mtgHAOVdpu0jCVs2Fh/eDkq3e+olj4IjrEztGh9inMgbpmdv/wdDQsnvDafMhfy607gMtOqY2HhGps2Rq3v8AHjazYXhDOHQzs3Px+lw/kejBnHOvAaOBu4AZeDXo45xzZY3YegEN2LlUxDf9BYgUVax/9mDN5fPWww/zIBKp2DZwGJz/R69BXE5HuP71mhvHNZb0VtBxPyVukR1EMmObG17DtJuomFW4CPiLc+62+g2v/qmft1Trk/vh3eu8W94Wgi57wLUzqy779r/gkrOhtASGDIWxH0O2eiGKBEmz6uftPPcA7YE9gAOATkFI3CI1OuhyGOAPxtK6E5xZwzAC113uJW6AmdPh5WcaPr6gKVkBi06G73aFVXdXtAMQkTpL+Jm3mT0DXOWc2wTMjdneCm9UNE0LKsGUngWXvA8lBZDWouZW2SXFMSsGxcXVFm22lpwHmz8DIrDqNsjoA+3PTXVUIjuEZJ55/wLIqmJ7FnBB3cIRaQLSs7bfneqWeype9+gJI3/RsDEFUcFMvJ6gAGHY8Llq3yL1pNY1b/9ZcdnkgNlmFjv6RBg4AVhdv+GJNFGjLoeDDoeVy2HfA4L7vNs52Pg2FH4L2UdDy/3q79htfgZ5L+D9yYjAh3+HtovhtHFef3sRSVqtG6yZWZRKk/xuwwG3+8/Dmyw1WBOJsfrPsOJ6ym/C9fsIWh9WP8eOFnnH//p2yI16QzIBnPQK7Dqyxl1FGkOQG6wl8sz7CLyf0B8BpwPrY94rBpY451bUY2wi0tDWPeW/iAJpkPdS/SXvUCZ0uh7m3l75Z39pExgyViTgap28nXOfAphZH2CpS2YuURFpWtJ3hqKFeM+mo5Des36PH86AA26ByX/w1tsPggGn1e85RJqhZPp5XwRsds69Ebf9DKClc+75eoyv3um2uUiMoh9h8elQ+B20OR52fhFCDTCozMppULAGeh4G6U1g0BoRms9t8zI3Ab+qYvtq4EmgSSdvEYmR2QcGTm/483Tdt+HPIdKMJNNVrBfwYxXbl/jviYiISANKJnmvBgZXsX0vYF3dwhEREZHtSea2+SvAI2a2iYqpQQ8DHgZera/AREREpGrJJO/bgN7Ah0Cpvy0E/BNvwhIRERFpQAknb+dcMXCWmd2Gd6u8AJjtnFtS38GJiIjItpKpeQPgnPsB+KEeYxFpuiIRCIdTHYWICFDL5G1mDwC3Oee2+K+r5Zz7Xb1EJtIUzJwHp1wJS1fCyUfAq/dDi8zEjlFQCE++AXn5cMEpsEs9D4RSF4VF8NYE7/UpRyV+bSKSErWtee8NpMe8ro5GXZMdy0W3wLJV3gQeb38Mj78Cv7uw9vs7BydfAR9OgZDBwy/At+Og204NFnKtlZbCiPNg0jfe+kH7wCcvQ1rSN+REpJHU6lvqnDuiqtciO7wVqyEa9V6HQ7Bqbc3l4+VvggmTvdcRBxv89QtOqd84kzHju4rEDTBxunenYegeqYtJRGolmX7eIju+khI4ezTkxgxdkBaG805K7DitW0Lb7Mrzg/fpXj8x1lXbKqYxrWqbiDQ5tX3m/e/aHtA5p1kHJPheHAevvue9NoOc1jD1NRjQO7HjpKXBW4/BqFshbyNcPwoOaSJDhfbrDWOuh5vv99b/eC303TmlIYlI7dT24VZ+zGsDfu5vm+ZvGwq0BWqd5EVSxjnYuNmrFVfXgnxNHoRC3i1z56CoOPHEXebQfWH+B0mH26CuvxR++wvvtRqriQRGrW6bO+cuKluAXOB1oI9z7jS/pr0L3uhqCT4QFGlkW7bCkRdB22HQ5RCYOrPqcmceB9kxs19deW7jxJcKLTKVuEUCJpkpQdcABzvnvo/bPhCY5JzrUI/x1TtNCdrMjXkKbn7Iq1GHQrB7X5j1VtVll62Edz6Fnl3gxMMqP7cWkcBrblOCpgGDgO/jtg9CDeCkqcvbWJGEo1FYX8P3tWdXuGxk48QlIpKAZJLts8DTZvY7MzvYX64FnvLfE2m6LjwVsmJuEY++KHWxiIgkKZma92hgFXAt0NXfthL4M3B/PcUl0jAG7QKfvwDjv/AGJTl4aKojEhFJWMLPvCvt7D0/JkjPCvTMuxlzDkb/GR583ns94gB472+QkZHqyEQkBYL8zDupZ9RmlmZmRwFn4w+JambdzKx1fQYnUq+uuRceeM5L3OANWfqv/zX8eaNRmP0DLFrW8OcSkWYh4eRtZjsDs4G3gMeATv5bNwB/qb/QROpRcTE8+lIV20tq3q+gEF59B954zztGokpL4eTLYfCp0PdYuPOxxI8hIhInmZr3w3iDs7TDm8u7zH+AEfURlEi9C4e37cvcowucdnT1+5SUwGHnwNlXw5m/heMurhjnvLY+nALvflaxfsdjsH5DYscQEYmTTPI+BLjbORdfDVkMNJFBm0XihMPw7D2Q6T/fPnRfmPcOZLeqfp8vZ8FXsyrWP54C385P7LxVJXvNvScidZRMa/MQUNWYkj2ATXULR6QBnXk8nHQEbCmAju22X75dFQ0aE52446jhcOQw+Gg7l1E/AAATLElEQVSqt379KOjQNrFjiIjESabm/V/g6ph15zdUuxN4r16iEmkoWS1ql7gBdusPd13tzcMdDsODt0DPbomdLz0d/vsUTHkFZr8FY65NPGYRkTjJDI/aE/gAb4KS/njPv/vjjWt+qHNudX0HWZ/UVUwSVlDojcqm8b9FdihB7iqW8G1z59wyM9sLOAvYC2gNPA285JwrqHFnkSDKapHqCEREKkkoeZtZOjAP+Jlz7iWgir43IpK0TVvgry95U5ZefBr0753qiESkCUooeTvnSsxM1RDZcZSWwt1/gwmTYdhguOfq1N0edw6OuwSmzAQLwROvwnfvQtdO299XRJqVZBqsPQbcYGbJtFQXaVrufw7uegImfgMPveBNF5oqa9bDpBkQdRCJQP5m+PSr1MUjIk1WMgl4P7zBWI4xs9nAltg3nXOn1UdgIo3iy9kVr6NRmPxN6mJpm+0tGzd7CRygb8/UxSMiTVYyNe8NwL+A8cAKID9uEQmOw/atGOvcDI4YlrpYMjJg3OPezGfdO8Njt8F+e6YuHhFpsuo0q1gQqauYVBKNwiMvwsdTvUR5wyivb7aI7PCC3FWs1snbzELAdcDJQAbwIXBn0LqHKXk3Q/MWwcMvQFoYrrsYeiU40IqI7JCCnLwTeeZ9C3AHMAFvQpKrgJ2Ai+s/LJF6sn4DHHiu9xwZ4K2P4If3NeCKiARaIs+8LwAud84d65w7FTgJONevkYs0TdPnQl6+13o7EoFlq2D+klRHJSJSJ4kk3l7EjF3unJuANz+S7kFK0zWgN6SneY3RQiFolQW9uqY6KhGROkkkeacBhXHbSgC17pGmq1c3eOuvMHQ3GLYnjP8H5CQ4M5iISBOTSIO1KPA+UBSz+STgI2L6ejf1ft5qsCYiItB8Gqw9X8W2F+srEBEREamdWidv59xFDRmIiIiI1I5aiouIiASMkreIiEjAKHmLiIgETJNI3mZ2hZktNrNCM5tqZvvXUPYSM/vczPL8ZUJN5UVERHY0KU/eZnYW8ABwJ7APMBMYb2Y7VbPL4cArwBHAcGAZ8F8z697w0YqIiKReymcVM7OpwFfOuSv99RBeQn7UOfenWuwfBvKAK51z/6xFefXzFhGRQPfzTmnN28wygKF4k50A4JyL+uvDa3mYlnijvK2v5hyZZtambAE0vJaIiARaqm+bdwTCQG7c9lygSy2PMQZYQcwPgDg3Afkxy0+JhykiItJ0pDp514mZ3QiMBH7unIsfd73MvUBOzNKjkcITERFpEIkMj9oQ1gIRoHPc9s7Aqpp2NLPRwI3AUc65WdWVc84VETMeu5klHayIiEhTkNKat3OuGPgaGFG2zW+wNgKYXN1+ZnY9cBtwnHNuWkPHKSIi0pSkuuYNXjex581sGvAlcDXQCngWwMz+CSx3zt3kr98A3AWcAyw2s7Jn45udc5sbO3gREZHGlvLk7Zx7zcw64SXkLsAMvBp1WSO2XkA0ZpfLgAzgzbhD3Qnc0bDRioiIpF7K+3k3NvXzFhERUD9vERERaURK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIB0ySSt5ldYWaLzazQzKaa2f7bKX+Gmc3zy882sxMaK1YREZFUS3nyNrOzgAeAO4F9gJnAeDPbqZryBwKvAE8DewNjgbFmtkfjRCwiIpJa5pxLbQBmU4GvnHNX+ushYBnwqHPuT1WUfw1o5Zz7Wcy2KcAM59yva3G+NkB+fn4+bdq0qa/LEBGRgNm4cSM5OTkAOc65jamOJxEprXmbWQYwFJhQts05F/XXh1ez2/DY8r7x1ZU3s0wza1O2ANl1DlxERCSFUn3bvCMQBnLjtucCXarZp0uC5W8C8mOWn5KKVEREpIlIdfJuDPcCOTFLj9SGIyIiUjdpKT7/WiACdI7b3hlYVc0+qxIp75wrAorK1s0sqUBFRESaipTWvJ1zxcDXwIiybX6DtRHA5Gp2mxxb3nd0DeVFRER2KKmueYPXTex5M5sGfAlcDbQCngUws38Cy51zN/nlHwY+NbNrgXeBkcC+wKWNHbiIiEgqpDx5O+deM7NOwF14jc5mAMc558oapfUCojHlJ5nZOcDdwB+B+cCpzrk5jRu5iIhIaqS8n3djUz9vEREB9fMWERGRRqTkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5C0iIhIwSt4iIiIBo+QtIiISMGmpDiBVNm7cmOoQREQkhYKcB8w5l+oYGpWZdQd+SnUcIiLSZPRwzi1PdRCJaI7J24BuwKYUhpGN9wOiR4rjSJXmfv2gz6C5Xz/oM2gq158NrHABS4bN7ra5/z9QSn9heb8fANjknAvufZskNffrB30Gzf36QZ9BE7r+QH72arAmIiISMEreIiIiAaPknRpFwJ3+f5uj5n79oM+guV8/6DNo7tdfJ82uwZqIiEjQqeYtIiISMEreIiIiAaPkLSIiEjBK3iIiIgGj5N1AzOwKM1tsZoVmNtXM9t9O+TPMbJ5ffraZndBYsTaERK7fzC4xs8/NLM9fJmzv8wqCRP8NxOw30sycmY1t6BgbUhLfgbZm9piZrTSzIjP7oTl9D/zyV5vZ92ZWYGbLzOxBM2vRWPHWJzM71MzGmdkK/9/zqbXY53Azm+7/77/AzC5shFADScm7AZjZWcADeN0g9gFmAuPNbKdqyh8IvAI8DewNjAXGmtkejRNx/Ur0+oHD8a7/CGA4sAz4rz8OfSAl8RmU7dcb+AvweQOH2KCS+A5kAP8DegP/BwwELiHFoyHWRRKfwTnAn/zyuwKjgLOAPzZKwPWvFd41X1GbwmbWB3gX+BgYAjwEPGVmxzZYhEHmnNNSzwswFfhrzHoI74/QjdWUfw14J27bFOBvqb6Wxrj+KvYP4w1ZeEGqr6UxPwP/uifi/dF+Dhib6utorOsHfg0sBNJTHXsKP4O/Ah/Gbbsf+CLV11IPn4UDTt1OmTHAnLhtrwIfpDr+prio5l3P/BrEUGBC2TbnXNRfH17NbsNjy/vG11C+yUry+uO1BNKB9fUeYCOow2fwe2C1c+7pho2wYSV5/ScDk4HHzCzXzOaY2c1mFm7wgBtAkp/BJGBo2a11M9sFOAF4r2GjbTJ2mL+DjaHZTUzSCDri1aBy47bnAoOq2adLNeW71G9ojSKZ6483BljBtl/koEj4MzCzg/Fq3EMaNrRGkcy/gV2AI4GX8BJWP+BxvB9xdzZMmA0q4c/AOfeymXUEvvBnP0zDu/sW1Nvmiaru72AbM8tyzhWkIKYmSzVvaVLM7EZgJPBz51xhquNpDGaWDbwAXOKcW5vqeFIkBKwGLnXOfe2cew24B+92erNgZocDNwOX4z0jPw040cxuS2Vc0jSp5l3/1gIRoHPc9s7Aqmr2WZVg+aYsmesHwMxGAzcCRznnZjVMeI0i0c+gL15DrXEx0ySGAMysFBjonFvYIJE2jGT+DawESpxzkZht3wFdzCzDOVdc/2E2qGQ+gz8ALzjnnvLXZ5tZK+BJM7vHv+2+I6vu7+BG1bq3pZp3PfP/yHwNjCjbZmYhf31yNbtNji3vO7qG8k1WktePmV0P3AYc55yb1tBxNqQkPoN5wJ54t8zLlrepaHW7rIFDrldJ/huYCPTzy5UZAKwMYOJO9jNoCcQn6LIfM8aOb4f5O9goUt1ibkdc8Lp3FAK/wOvy8XcgD+jsv/9P4N6Y8gcCJcC1eM/D7gCKgT1SfS2NdP034M0sdDrec6+ypXWqr6WxPoMq9n+OYLc2T/TfQE+8HgaP4iXtE/Ged96S6mtpxM/gDv8zGAn0wUtcC4DXUn0tSV5/ayp+jDrgGv91L//9e4F/xpTvA2wB7vP/Dl4OlALHpvpamuKS8gB21AW4EljiJ6WpwLCY9z4BnosrfwbwvV9+DnBCqq+hsa4fWOx/ueOXO1J9HY35byBu30An72SuH69V8RQ/4S3Ee/4bTvV1NNZngPcY83Y/YRcAS4HHgLapvo4kr/3war7Xz/nvPwd8UsU+3/if10LgwlRfR1NdNCWoiIhIwOiZt4iISMAoeYuIiASMkreIiEjAKHmLiIgEjJK3iIhIwCh5i4iIBIySt4iISMAoeYvIdpnZIDObYmaFZjYj1fGINHdK3iKAmbntLHeYWW//dcTMusft39XMSv33e6fmKhrUnXhDVw5k2/Gny5lZTzN7xsxWmFmxmS0xs4fNrEOjRVo5njv0Y0N2REreIp6uMcvVeGNMx277S0zZ5cAFcfv/wt+eMmaW3oCH7wt84Zxb4pxbV835dwGmAf2Bs/Hm5P41/mQcZta+AeMTaVaUvEUA59yqsgXI9zZVbHPObY4p/jxwUdwhLvK3lzOzdmb2kpmtMbMCM5tvZhfFvN/DzF4xs/VmtsXMppnZsJj3LzOzhX4N9nszOz/u+M4v87aZbQFu8befYmbT/Vvci8zsdjOrdvpfMwuZ2e/N7CczKzKzGWZ2XOx5gKHA78vuQlRzqMfwJtQ5xjn3qXNuqXPufeAooDve/NyxsZ8aF8cGM7vQf112l2OkmU3yr2WOmR0WU/5CM9sQd4xT/Xjxj3U7sFfMHZQLq/scRIJEyVskcW8D7czsYAD/v+2AcXHl/gDsBhyPN6vUZXjzPGNmrYFP8ZLaycBeeLMplc3j/XPgYeB+YA+8GameNbMj4s5xB/AfvClFnzGzQ/Bmq3rYP/evgAvxE3s1rsKb0W40MBgYD7xtZv3997sC3/qxxN+FwI+3PXAs8LiLm3vZ/0H0EnCWxUxYXkt/9s+7N97UkOMSuAX/mr/vt1TcQXktwfOLNEnV/hoXkWqVAC8CFwNf+P990d8eqxfwjauYn3xxzHvnAJ2A/Zxz6/1tC2LeH403+9Lj/voDZnaAv/3jmHIvO+eeLVsxs2eAPznnyu4CLDKz2/B+GNxZzfWMBsY4517112/wfyRcDVzhnFtlZqXAZj8RV6U/3pzT31Xz/nd4P3A6AaurKVOVvzrn/uVf22XAccAo/3pq5JwrMLPNQGkNcYsEkmreIsl5BjjDzLrgTef6TBVlngBG+reh7zOzA2PeG4KX2NdXsR94NfWJcdsm+ttjTYtb3wvv9vbmsgX4B9DVzFrGn8TM2gDdanmu2ki0Zr09k8teOOdK8a43mbhEdihK3iJJcM7NBuYBrwDfOefmVFHmfWBn4EG8BPmhmZXdci6IL5+kLXHrrfGe8w6JWfbEqxkX1tM5q7IAb67m6hLrrkAesMZfd2yb6BNtcBeth2OIBJKSt0jyngEOp+paNwDOuTXOueedc+fh3Ya+1H9rFjCkhhbY3wEHxW07CJi7nZimAwOdcwuqWKJVxLcRWJHkuWKPsw74H3C5mWXFvuffnTgXeM055/zNa/CeQZeV6Q9sc2cAOCCmTBpew7myW/NrgGwzaxVTfkjc/sVAuLbXIRIUeuYtkrx/AG8AG6p608zuAr7GazCVCfyMisTzCnAzMNbMbgJW4jXKWuGcm4zXUOt1M/sGmACcBJyG13K7JncB75jZUuBNvNrpXsAezrlbq9nnz8CdZrYQmIHXcn4IXsJNxJXAJGC8md0K/Ajs7h9/OZUbzX0EXGlmk/GS6xi2bTMAcIWZzcf73K7Be25e9mNpKrAV+KOZPQIMw2ucF2sx0MfMhgA/AZucc0UJXpdIk6Oat0iSnHOlzrm1/rPYqhQD9+LVsj8DIsBIf99i4Bi8xlvvAbOBG/0yOOfG4rUCH42X/H8FXOSc+2Q7MY3H+5FwDPAVMAUv6S2pYbdHgAfwWmbPxmsUdrJzbn5N56ri3POBfYFFwOvAQuBJvAZ2w+Oe718LLAM+B17Ga8G+tYrD3ugvM4GD/bjW+udbD5wHnODHfTZe6/tY/wI+8GNY45cRCTyruIslItI0+KPU/Qjs7ZzTCGkicVTzFhERCRglbxERkYDRbXMREZGAUc1bREQkYJS8RUREAkbJW0REJGCUvEVERAJGyVtERCRglLxFREQCRslbREQkYJS8RUREAkbJW0REJGD+H5cdyHl9yNQuAAAAAElFTkSuQmCC\n" + "image/png": "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\n" }, "metadata": { "needs_background": "light" } - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "0.9142998039353573\n" - ] } ] }, @@ -432,28 +462,28 @@ "height": 497 }, "id": "_Ix9KPR3kuLu", - "outputId": "f2582a38-46d0-4db4-bda7-f3fdc985144e" + "outputId": "2ed55002-b417-4526-88fd-ea844544d7a5" }, - "execution_count": 9, + "execution_count": null, "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "0.8287990333595944\n" + ] + }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], - "image/png": "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\n" + "image/png": "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\n" }, "metadata": { "needs_background": "light" } - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "0.8287968094269524\n" - ] } ] }, @@ -492,7 +522,7 @@ "metadata": { "id": "Ed3SBjR0Defv" }, - "execution_count": 10, + "execution_count": null, "outputs": [] }, { @@ -500,23 +530,33 @@ "source": [ "# setup model\n", "clear_mem()\n", - "af = af2rank(\"5CEG_AD_trim.pdb\", chain=\"A,B\")" + "af = af2rank(\"5CEG_AD_trim.pdb\", chain=\"A,B\", model_name=SETTINGS[\"model_name\"])\n", + "SCORES,LABELS = [],[]" ], "metadata": { "id": "lySWA526TtUQ" }, - "execution_count": 11, + "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ - "SCORES = []\n", - "for mut,x in seqs.items():\n", - " SCORES.append(af.predict(seq=x[\"seq\"], **SETTINGS, extras={\"fitness\":x[\"sco\"], \"mut\":mut}))" + "for label,x in seqs.items():\n", + " if label not in LABELS:\n", + "\n", + " if save_output_pdbs:\n", + " output_pdb = os.path.join(\"tmp\",f\"{label}.pdb\")\n", + " else:\n", + " output_pdb = None\n", + "\n", + " score = af.predict(seq=x[\"seq\"], **SETTINGS, output_pdb=output_pdb,\n", + " extras={\"fitness\":x[\"sco\"], \"id\":label})\n", + " SCORES.append(score)\n", + " LABELS.append(label)" ], "metadata": { - "id": "cMfdjfIbr_PV" + "id": "fYUmIaJBL4j7" }, "execution_count": null, "outputs": [] @@ -524,82 +564,21 @@ { "cell_type": "code", "source": [ - "plot_me(SCORES, x=\"fitness\", y=\"composite\", scale_axis=False)" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 474 - }, - "id": "HaYwiXeop_is", - "outputId": "202c8345-3c82-4938-849d-e8425823b2c5" - }, - "execution_count": 18, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": [ - "
" - ], - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "0.37408836098630754\n" - ] - } - ] - }, - { - "cell_type": "code", - "source": [ - "plot_me(SCORES, x=\"fitness\", y=\"dgram_cce\", scale_axis=False)" + "SCORES[1]" ], "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 475 - }, - "id": "Mybxni5mqcLD", - "outputId": "90ae7780-cd3e-4f91-ac9e-81f3a44b899b" + "id": "3qKYfNDWvpLd" }, - "execution_count": 21, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": [ - "
" - ], - "image/png": "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\n" - }, - "metadata": { - "needs_background": "light" - } - }, - { - "output_type": "stream", - "name": "stdout", - "text": [ - "-0.37727701197990876\n" - ] - } - ] + "execution_count": null, + "outputs": [] }, { "cell_type": "code", "source": [ - "" + "plot_me(SCORES, x=\"fitness\", y=\"composite\", scale_axis=False)" ], "metadata": { - "id": "n65JnuQaMYlA" + "id": "mpBcceKdSlOG" }, "execution_count": null, "outputs": [] diff --git a/af/examples/afdesign_hotspot_test.ipynb b/af/examples/afdesign_hotspot_test.ipynb index ef4d6b49..9ec33d4a 100644 --- a/af/examples/afdesign_hotspot_test.ipynb +++ b/af/examples/afdesign_hotspot_test.ipynb @@ -33,7 +33,7 @@ "if [ ! -d params ]; then\n", " pip -q install git+https://github.com/sokrypton/ColabDesign.git\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" diff --git a/af/examples/binder_hallucination.ipynb b/af/examples/binder_hallucination.ipynb index 9889a7a0..6faa2c05 100644 --- a/af/examples/binder_hallucination.ipynb +++ b/af/examples/binder_hallucination.ipynb @@ -38,7 +38,7 @@ "if [ ! -d params ]; then\n", " pip -q install git+https://github.com/sokrypton/ColabDesign.git\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" @@ -123,27 +123,26 @@ "#@markdown - The contact definition is based on Cb-Cb diststance `cutoff`. To avoid \n", "#@markdown biasing towards helical contact, only contacts with sequence seperation > \n", "#@markdown `seqsep` are considered.\n", - "seqsep = 0 #@param [\"0\",\"5\",\"9\"] {type:\"raw\"}\n", - "cutoff = \"max\" #@param [\"8\", \"14\", \"max\"]\n", - "num = \"max\" #@param [\"1\", \"2\", \"4\", \"8\", \"max\"]\n", - "binary = True #@param {type:\"boolean\"}\n", + "seqsep = 9 #@param [\"0\",\"5\",\"9\"] {type:\"raw\"}\n", + "cutoff = \"14\" #@param [\"8\", \"14\", \"max\"]\n", + "num = \"2\" #@param [\"1\", \"2\", \"4\", \"8\"]\n", + "binary = False #@param {type:\"boolean\"}\n", "if cutoff == \"max\": cutoff = 21.6875\n", "if num == \"max\": num = binder_len\n", "\n", "opt = {\"con\":{\"seqsep\":int(seqsep),\"cutoff\":float(cutoff),\"num\":int(num),\"binary\":binary}}\n", "#@markdown ---\n", "#@markdown ####interface Weights\n", - "i_pae = 1.0 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", - "i_con = 0.5 #@param [\"0.01\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", + "tb_con = 0.0 #@param [\"0.0\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", + "bt_con = 1.0 #@param [\"0.0\", \"0.1\", \"0.5\", \"1.0\"] {type:\"raw\"}\n", "\n", - "weights.update({\"i_pae\":float(i_pae),\"i_con\":float(i_con)})\n", + "weights.update({\"bt_pae\":float(bt_pae),\"tb_con\":float(tb_con)})\n", "\n", "#@markdown ####interface Contact Definition\n", "cutoff = \"max\" #@param [\"8\", \"14\", \"max\"]\n", - "num = \"max\" #@param [\"1\", \"2\", \"4\", \"8\", \"max\"]\n", - "binary = True #@param {type:\"boolean\"}\n", + "num = \"1\" #@param [\"1\", \"2\", \"4\", \"8\"]\n", + "binary = False #@param {type:\"boolean\"}\n", "if cutoff == \"max\": cutoff = 21.6875\n", - "if num == \"max\": num = binder_len\n", "\n", "opt.update({\"i_con\":{\"cutoff\":float(cutoff),\"num\":int(num),\"binary\":binary}})\n", "\n", diff --git a/af/examples/disulfide_design.ipynb b/af/examples/disulfide_design.ipynb index 8ec2a835..5a0231cc 100644 --- a/af/examples/disulfide_design.ipynb +++ b/af/examples/disulfide_design.ipynb @@ -38,7 +38,7 @@ " ln -s /usr/local/lib/python3.7/dist-packages/colabdesign colabdesign\n", " # download params\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" @@ -86,7 +86,7 @@ "import random\n", "from jax.lax import dynamic_slice\n", "import jax.numpy as jnp\n", - "from colabdesign.af.loss import get_con_loss\n", + "from colabdesign.af.loss import _get_con_loss\n", "\n", "def generate_disulfide_pattern(L, disulfide_num, min_sep=5):\n", " disulfide_pattern = []\n", @@ -121,7 +121,7 @@ " for pair in disulfide_pattern:\n", " i,j = pair\n", " pair_dgram = dynamic_slice(dgram, (i,j,0), (1,1,len(dgram_bins))) + dynamic_slice(dgram, (j,i,0), (1,1,len(dgram_bins)))\n", - " disulfide_loss += get_con_loss(pair_dgram, dgram_bins, cutoff=7.0, binary=False, num=1)\n", + " disulfide_loss += _get_con_loss(pair_dgram, dgram_bins, cutoff=7.0, binary=False, num=1)\n", " return disulfide_loss.mean()\n", "\n", " # add disulfide loss here:\n", diff --git a/af/examples/hallucination.ipynb b/af/examples/hallucination.ipynb index c199e114..48b65f97 100644 --- a/af/examples/hallucination.ipynb +++ b/af/examples/hallucination.ipynb @@ -38,7 +38,7 @@ "if [ ! -d params ]; then\n", " pip -q install git+https://github.com/sokrypton/ColabDesign.git\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" diff --git a/af/examples/hallucination_custom_loss.ipynb b/af/examples/hallucination_custom_loss.ipynb index 208ece8a..42f057bd 100644 --- a/af/examples/hallucination_custom_loss.ipynb +++ b/af/examples/hallucination_custom_loss.ipynb @@ -38,7 +38,7 @@ " ln -s /usr/local/lib/python3.7/dist-packages/colabdesign colabdesign\n", " # download params\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" diff --git a/af/examples/partial_hallucination_rewire.ipynb b/af/examples/partial_hallucination_rewire.ipynb index 284d7796..ad72619b 100644 --- a/af/examples/partial_hallucination_rewire.ipynb +++ b/af/examples/partial_hallucination_rewire.ipynb @@ -33,7 +33,7 @@ "if [ ! -d params ]; then\n", " pip -q install git+https://github.com/sokrypton/ColabDesign.git\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" diff --git a/af/examples/use_esm_1b_bias.ipynb b/af/examples/use_esm_1b_bias.ipynb index 8d32b896..4da84648 100644 --- a/af/examples/use_esm_1b_bias.ipynb +++ b/af/examples/use_esm_1b_bias.ipynb @@ -147,7 +147,7 @@ "if [ ! -d params ]; then\n", " pip -q install git+https://github.com/sokrypton/ColabDesign.git\n", " mkdir params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params\n", " for W in openfold_model_ptm_1 openfold_model_ptm_2 openfold_model_no_templ_ptm_1\n", " do wget -qnc https://files.ipd.uw.edu/krypton/openfold/${W}.npz -P params; done\n", "fi" diff --git a/colabdesign/af/README.md b/colabdesign/af/README.md index d3068458..3c905288 100644 --- a/colabdesign/af/README.md +++ b/colabdesign/af/README.md @@ -3,4 +3,5 @@ - loss.py - configure the loss - prep.py - prep features - design.py - gradient update loop -- utils.py - various tools for saving/plotting \ No newline at end of file +- utils.py - various tools for saving/plotting +- crop.py - functions specific to cropping \ No newline at end of file diff --git a/colabdesign/af/alphafold/common/confidence_jax.py b/colabdesign/af/alphafold/common/confidence_jax.py index 209c6f96..6e041ac1 100644 --- a/colabdesign/af/alphafold/common/confidence_jax.py +++ b/colabdesign/af/alphafold/common/confidence_jax.py @@ -20,8 +20,6 @@ import jax from jax import jit - -@jit def compute_plddt_jax(logits): """Port of confidence.compute_plddt to jax @@ -40,7 +38,6 @@ def compute_plddt_jax(logits): predicted_lddt_ca = jax.numpy.sum(probs * bin_centers[None, :], axis=-1) return predicted_lddt_ca * 100 -@jit def _calculate_bin_centers(breaks): """Gets the bin centers from the bin edges. @@ -59,13 +56,12 @@ def _calculate_bin_centers(breaks): axis=0) return bin_centers -@jit def predicted_tm_score_jax( logits, breaks, residue_weights=None, asym_id=None, - interface: bool = False): + interface=False): """Computes predicted TM alignment or predicted interface TM alignment score. Args: @@ -114,12 +110,10 @@ def predicted_tm_score_jax( pair_residue_weights = pair_mask * ( residue_weights[None, :] * residue_weights[:, None]) - normed_residue_mask = pair_residue_weights / (1e-8 + jax.numpy.sum( - pair_residue_weights, axis=-1, keepdims=True)) + normed_residue_mask = pair_residue_weights / (1e-8 + pair_residue_weights.sum(-1,keepdims=True)) per_alignment = jax.numpy.sum(predicted_tm_term * normed_residue_mask, axis=-1) return jax.numpy.asarray(per_alignment[(per_alignment * residue_weights).argmax()]) - def get_confidence_metrics( prediction_result, multimer_mode: bool): @@ -152,8 +146,6 @@ def get_confidence_metrics( return confidence_metrics - -@jit def _calculate_expected_aligned_error( alignment_confidence_breaks, aligned_distance_error_probs): @@ -175,8 +167,6 @@ def _calculate_expected_aligned_error( return (jax.numpy.sum(aligned_distance_error_probs * bin_centers, axis=-1), jax.numpy.asarray(bin_centers[-1])) - -@jit def compute_predicted_aligned_error( logits, breaks): diff --git a/colabdesign/af/alphafold/data/pipeline_multimer.py b/colabdesign/af/alphafold/data/pipeline_multimer.py new file mode 100644 index 00000000..012408cd --- /dev/null +++ b/colabdesign/af/alphafold/data/pipeline_multimer.py @@ -0,0 +1,284 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Functions for building the features for the AlphaFold multimer model.""" + +import collections +import contextlib +import copy +import dataclasses +import json +import os +import tempfile +from typing import Mapping, MutableMapping, Sequence + +from absl import logging +from alphafold.common import protein +from alphafold.common import residue_constants +from alphafold.data import feature_processing +from alphafold.data import msa_pairing +from alphafold.data import parsers +from alphafold.data import pipeline +from alphafold.data.tools import jackhmmer +import numpy as np + +# Internal import (7716). + + +@dataclasses.dataclass(frozen=True) +class _FastaChain: + sequence: str + description: str + + +def _make_chain_id_map(*, + sequences: Sequence[str], + descriptions: Sequence[str], + ) -> Mapping[str, _FastaChain]: + """Makes a mapping from PDB-format chain ID to sequence and description.""" + if len(sequences) != len(descriptions): + raise ValueError('sequences and descriptions must have equal length. ' + f'Got {len(sequences)} != {len(descriptions)}.') + if len(sequences) > protein.PDB_MAX_CHAINS: + raise ValueError('Cannot process more chains than the PDB format supports. ' + f'Got {len(sequences)} chains.') + chain_id_map = {} + for chain_id, sequence, description in zip( + protein.PDB_CHAIN_IDS, sequences, descriptions): + chain_id_map[chain_id] = _FastaChain( + sequence=sequence, description=description) + return chain_id_map + + +@contextlib.contextmanager +def temp_fasta_file(fasta_str: str): + with tempfile.NamedTemporaryFile('w', suffix='.fasta') as fasta_file: + fasta_file.write(fasta_str) + fasta_file.seek(0) + yield fasta_file.name + + +def convert_monomer_features( + monomer_features: pipeline.FeatureDict, + chain_id: str) -> pipeline.FeatureDict: + """Reshapes and modifies monomer features for multimer models.""" + converted = {} + converted['auth_chain_id'] = np.asarray(chain_id, dtype=np.object_) + unnecessary_leading_dim_feats = { + 'sequence', 'domain_name', 'num_alignments', 'seq_length'} + for feature_name, feature in monomer_features.items(): + if feature_name in unnecessary_leading_dim_feats: + # asarray ensures it's a np.ndarray. + feature = np.asarray(feature[0], dtype=feature.dtype) + elif feature_name == 'aatype': + # The multimer model performs the one-hot operation itself. + feature = np.argmax(feature, axis=-1).astype(np.int32) + elif feature_name == 'template_aatype': + feature = np.argmax(feature, axis=-1).astype(np.int32) + new_order_list = residue_constants.MAP_HHBLITS_AATYPE_TO_OUR_AATYPE + feature = np.take(new_order_list, feature.astype(np.int32), axis=0) + elif feature_name == 'template_all_atom_mask': + feature_name = 'template_all_atom_mask' + converted[feature_name] = feature + return converted + + +def int_id_to_str_id(num: int) -> str: + """Encodes a number as a string, using reverse spreadsheet style naming. + + Args: + num: A positive integer. + + Returns: + A string that encodes the positive integer using reverse spreadsheet style, + naming e.g. 1 = A, 2 = B, ..., 27 = AA, 28 = BA, 29 = CA, ... This is the + usual way to encode chain IDs in mmCIF files. + """ + if num <= 0: + raise ValueError(f'Only positive integers allowed, got {num}.') + + num = num - 1 # 1-based indexing. + output = [] + while num >= 0: + output.append(chr(num % 26 + ord('A'))) + num = num // 26 - 1 + return ''.join(output) + + +def add_assembly_features( + all_chain_features: MutableMapping[str, pipeline.FeatureDict], + ) -> MutableMapping[str, pipeline.FeatureDict]: + """Add features to distinguish between chains. + + Args: + all_chain_features: A dictionary which maps chain_id to a dictionary of + features for each chain. + + Returns: + all_chain_features: A dictionary which maps strings of the form + `_` to the corresponding chain features. E.g. two + chains from a homodimer would have keys A_1 and A_2. Two chains from a + heterodimer would have keys A_1 and B_1. + """ + # Group the chains by sequence + seq_to_entity_id = {} + grouped_chains = collections.defaultdict(list) + for chain_id, chain_features in all_chain_features.items(): + seq = str(chain_features['sequence']) + if seq not in seq_to_entity_id: + seq_to_entity_id[seq] = len(seq_to_entity_id) + 1 + grouped_chains[seq_to_entity_id[seq]].append(chain_features) + + new_all_chain_features = {} + chain_id = 1 + for entity_id, group_chain_features in grouped_chains.items(): + for sym_id, chain_features in enumerate(group_chain_features, start=1): + new_all_chain_features[ + f'{int_id_to_str_id(entity_id)}_{sym_id}'] = chain_features + seq_length = chain_features['seq_length'] + chain_features['asym_id'] = chain_id * np.ones(seq_length) + chain_features['sym_id'] = sym_id * np.ones(seq_length) + chain_features['entity_id'] = entity_id * np.ones(seq_length) + chain_id += 1 + + return new_all_chain_features + + +def pad_msa(np_example, min_num_seq): + np_example = dict(np_example) + num_seq = np_example['msa'].shape[0] + if num_seq < min_num_seq: + for feat in ('msa', 'deletion_matrix', 'bert_mask', 'msa_mask'): + np_example[feat] = np.pad( + np_example[feat], ((0, min_num_seq - num_seq), (0, 0))) + np_example['cluster_bias_mask'] = np.pad( + np_example['cluster_bias_mask'], ((0, min_num_seq - num_seq),)) + return np_example + + +class DataPipeline: + """Runs the alignment tools and assembles the input features.""" + + def __init__(self, + monomer_data_pipeline: pipeline.DataPipeline, + jackhmmer_binary_path: str, + uniprot_database_path: str, + max_uniprot_hits: int = 50000, + use_precomputed_msas: bool = False): + """Initializes the data pipeline. + + Args: + monomer_data_pipeline: An instance of pipeline.DataPipeline - that runs + the data pipeline for the monomer AlphaFold system. + jackhmmer_binary_path: Location of the jackhmmer binary. + uniprot_database_path: Location of the unclustered uniprot sequences, that + will be searched with jackhmmer and used for MSA pairing. + max_uniprot_hits: The maximum number of hits to return from uniprot. + use_precomputed_msas: Whether to use pre-existing MSAs; see run_alphafold. + """ + self._monomer_data_pipeline = monomer_data_pipeline + self._uniprot_msa_runner = jackhmmer.Jackhmmer( + binary_path=jackhmmer_binary_path, + database_path=uniprot_database_path) + self._max_uniprot_hits = max_uniprot_hits + self.use_precomputed_msas = use_precomputed_msas + + def _process_single_chain( + self, + chain_id: str, + sequence: str, + description: str, + msa_output_dir: str, + is_homomer_or_monomer: bool) -> pipeline.FeatureDict: + """Runs the monomer pipeline on a single chain.""" + chain_fasta_str = f'>chain_{chain_id}\n{sequence}\n' + chain_msa_output_dir = os.path.join(msa_output_dir, chain_id) + if not os.path.exists(chain_msa_output_dir): + os.makedirs(chain_msa_output_dir) + with temp_fasta_file(chain_fasta_str) as chain_fasta_path: + logging.info('Running monomer pipeline on chain %s: %s', + chain_id, description) + chain_features = self._monomer_data_pipeline.process( + input_fasta_path=chain_fasta_path, + msa_output_dir=chain_msa_output_dir) + + # We only construct the pairing features if there are 2 or more unique + # sequences. + if not is_homomer_or_monomer: + all_seq_msa_features = self._all_seq_msa_features(chain_fasta_path, + chain_msa_output_dir) + chain_features.update(all_seq_msa_features) + return chain_features + + def _all_seq_msa_features(self, input_fasta_path, msa_output_dir): + """Get MSA features for unclustered uniprot, for pairing.""" + out_path = os.path.join(msa_output_dir, 'uniprot_hits.sto') + result = pipeline.run_msa_tool( + self._uniprot_msa_runner, input_fasta_path, out_path, 'sto', + self.use_precomputed_msas) + msa = parsers.parse_stockholm(result['sto']) + msa = msa.truncate(max_seqs=self._max_uniprot_hits) + all_seq_features = pipeline.make_msa_features([msa]) + valid_feats = msa_pairing.MSA_FEATURES + ( + 'msa_species_identifiers', + ) + feats = {f'{k}_all_seq': v for k, v in all_seq_features.items() + if k in valid_feats} + return feats + + def process(self, + input_fasta_path: str, + msa_output_dir: str) -> pipeline.FeatureDict: + """Runs alignment tools on the input sequences and creates features.""" + with open(input_fasta_path) as f: + input_fasta_str = f.read() + input_seqs, input_descs = parsers.parse_fasta(input_fasta_str) + + chain_id_map = _make_chain_id_map(sequences=input_seqs, + descriptions=input_descs) + chain_id_map_path = os.path.join(msa_output_dir, 'chain_id_map.json') + with open(chain_id_map_path, 'w') as f: + chain_id_map_dict = {chain_id: dataclasses.asdict(fasta_chain) + for chain_id, fasta_chain in chain_id_map.items()} + json.dump(chain_id_map_dict, f, indent=4, sort_keys=True) + + all_chain_features = {} + sequence_features = {} + is_homomer_or_monomer = len(set(input_seqs)) == 1 + for chain_id, fasta_chain in chain_id_map.items(): + if fasta_chain.sequence in sequence_features: + all_chain_features[chain_id] = copy.deepcopy( + sequence_features[fasta_chain.sequence]) + continue + chain_features = self._process_single_chain( + chain_id=chain_id, + sequence=fasta_chain.sequence, + description=fasta_chain.description, + msa_output_dir=msa_output_dir, + is_homomer_or_monomer=is_homomer_or_monomer) + + chain_features = convert_monomer_features(chain_features, + chain_id=chain_id) + all_chain_features[chain_id] = chain_features + sequence_features[fasta_chain.sequence] = chain_features + + all_chain_features = add_assembly_features(all_chain_features) + + np_example = feature_processing.pair_and_merge( + all_chain_features=all_chain_features) + + # Pad MSA to avoid zero-sized extra_msa. + np_example = pad_msa(np_example, 512) + + return np_example diff --git a/colabdesign/af/alphafold/model/all_atom.py b/colabdesign/af/alphafold/model/all_atom.py index 1e6dbeef..c3bc5780 100644 --- a/colabdesign/af/alphafold/model/all_atom.py +++ b/colabdesign/af/alphafold/model/all_atom.py @@ -1086,9 +1086,9 @@ def frame_aligned_point_error( normed_error *= jnp.expand_dims(frames_mask, axis=-1) normed_error *= jnp.expand_dims(positions_mask, axis=-2) - normalization_factor = ( - jnp.sum(frames_mask, axis=-1) * - jnp.sum(positions_mask, axis=-1)) + mask = (jnp.expand_dims(frames_mask, axis=-1) * + jnp.expand_dims(positions_mask, axis=-2)) + normalization_factor = jnp.sum(mask, axis=(-1, -2)) return (jnp.sum(normed_error, axis=(-2, -1)) / (epsilon + normalization_factor)) diff --git a/colabdesign/af/alphafold/model/all_atom_multimer.py b/colabdesign/af/alphafold/model/all_atom_multimer.py new file mode 100644 index 00000000..382f5948 --- /dev/null +++ b/colabdesign/af/alphafold/model/all_atom_multimer.py @@ -0,0 +1,983 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Ops for all atom representations.""" + +from typing import Dict, Text + +from colabdesign.af.alphafold.common import residue_constants +from colabdesign.af.alphafold.model import geometry +from colabdesign.af.alphafold.model import utils + +import jax +import jax.numpy as jnp +import numpy as np + +def squared_difference(x, y): + return jnp.square(x - y) + +def _make_chi_atom_indices(): + """Returns atom indices needed to compute chi angles for all residue types. + + Returns: + A tensor of shape [residue_types=21, chis=4, atoms=4]. The residue types are + in the order specified in residue_constants.restypes + unknown residue type + at the end. For chi angles which are not defined on the residue, the + positions indices are by default set to 0. + """ + chi_atom_indices = [] + for residue_name in residue_constants.restypes: + residue_name = residue_constants.restype_1to3[residue_name] + residue_chi_angles = residue_constants.chi_angles_atoms[residue_name] + atom_indices = [] + for chi_angle in residue_chi_angles: + atom_indices.append( + [residue_constants.atom_order[atom] for atom in chi_angle]) + for _ in range(4 - len(atom_indices)): + atom_indices.append([0, 0, 0, 0]) # For chi angles not defined on the AA. + chi_atom_indices.append(atom_indices) + + chi_atom_indices.append([[0, 0, 0, 0]] * 4) # For UNKNOWN residue. + + return np.array(chi_atom_indices) + + +def _make_renaming_matrices(): + """Matrices to map atoms to symmetry partners in ambiguous case.""" + # As the atom naming is ambiguous for 7 of the 20 amino acids, provide + # alternative groundtruth coordinates where the naming is swapped + restype_3 = [ + residue_constants.restype_1to3[res] for res in residue_constants.restypes + ] + restype_3 += ['UNK'] + # Matrices for renaming ambiguous atoms. + all_matrices = {res: np.eye(14, dtype=np.float32) for res in restype_3} + for resname, swap in residue_constants.residue_atom_renaming_swaps.items(): + correspondences = np.arange(14) + for source_atom_swap, target_atom_swap in swap.items(): + source_index = residue_constants.restype_name_to_atom14_names[ + resname].index(source_atom_swap) + target_index = residue_constants.restype_name_to_atom14_names[ + resname].index(target_atom_swap) + correspondences[source_index] = target_index + correspondences[target_index] = source_index + renaming_matrix = np.zeros((14, 14), dtype=np.float32) + for index, correspondence in enumerate(correspondences): + renaming_matrix[index, correspondence] = 1. + all_matrices[resname] = renaming_matrix.astype(np.float32) + renaming_matrices = np.stack([all_matrices[restype] for restype in restype_3]) + return renaming_matrices + + +def _make_restype_atom37_mask(): + """Mask of which atoms are present for which residue type in atom37.""" + # create the corresponding mask + restype_atom37_mask = np.zeros([21, 37], dtype=np.float32) + for restype, restype_letter in enumerate(residue_constants.restypes): + restype_name = residue_constants.restype_1to3[restype_letter] + atom_names = residue_constants.residue_atoms[restype_name] + for atom_name in atom_names: + atom_type = residue_constants.atom_order[atom_name] + restype_atom37_mask[restype, atom_type] = 1 + return restype_atom37_mask + + +def _make_restype_atom14_mask(): + """Mask of which atoms are present for which residue type in atom14.""" + restype_atom14_mask = [] + + for rt in residue_constants.restypes: + atom_names = residue_constants.restype_name_to_atom14_names[ + residue_constants.restype_1to3[rt]] + restype_atom14_mask.append([(1. if name else 0.) for name in atom_names]) + + restype_atom14_mask.append([0.] * 14) + restype_atom14_mask = np.array(restype_atom14_mask, dtype=np.float32) + return restype_atom14_mask + + +def _make_restype_atom37_to_atom14(): + """Map from atom37 to atom14 per residue type.""" + restype_atom37_to_atom14 = [] # mapping (restype, atom37) --> atom14 + for rt in residue_constants.restypes: + atom_names = residue_constants.restype_name_to_atom14_names[ + residue_constants.restype_1to3[rt]] + atom_name_to_idx14 = {name: i for i, name in enumerate(atom_names)} + restype_atom37_to_atom14.append([ + (atom_name_to_idx14[name] if name in atom_name_to_idx14 else 0) + for name in residue_constants.atom_types + ]) + + restype_atom37_to_atom14.append([0] * 37) + restype_atom37_to_atom14 = np.array(restype_atom37_to_atom14, dtype=np.int32) + return restype_atom37_to_atom14 + + +def _make_restype_atom14_to_atom37(): + """Map from atom14 to atom37 per residue type.""" + restype_atom14_to_atom37 = [] # mapping (restype, atom14) --> atom37 + for rt in residue_constants.restypes: + atom_names = residue_constants.restype_name_to_atom14_names[ + residue_constants.restype_1to3[rt]] + restype_atom14_to_atom37.append([ + (residue_constants.atom_order[name] if name else 0) + for name in atom_names + ]) + # Add dummy mapping for restype 'UNK' + restype_atom14_to_atom37.append([0] * 14) + restype_atom14_to_atom37 = np.array(restype_atom14_to_atom37, dtype=np.int32) + return restype_atom14_to_atom37 + + +def _make_restype_atom14_is_ambiguous(): + """Mask which atoms are ambiguous in atom14.""" + # create an ambiguous atoms mask. shape: (21, 14) + restype_atom14_is_ambiguous = np.zeros((21, 14), dtype=np.float32) + for resname, swap in residue_constants.residue_atom_renaming_swaps.items(): + for atom_name1, atom_name2 in swap.items(): + restype = residue_constants.restype_order[ + residue_constants.restype_3to1[resname]] + atom_idx1 = residue_constants.restype_name_to_atom14_names[resname].index( + atom_name1) + atom_idx2 = residue_constants.restype_name_to_atom14_names[resname].index( + atom_name2) + restype_atom14_is_ambiguous[restype, atom_idx1] = 1 + restype_atom14_is_ambiguous[restype, atom_idx2] = 1 + + return restype_atom14_is_ambiguous + + +def _make_restype_rigidgroup_base_atom37_idx(): + """Create Map from rigidgroups to atom37 indices.""" + # Create an array with the atom names. + # shape (num_restypes, num_rigidgroups, 3_atoms): (21, 8, 3) + base_atom_names = np.full([21, 8, 3], '', dtype=object) + + # 0: backbone frame + base_atom_names[:, 0, :] = ['C', 'CA', 'N'] + + # 3: 'psi-group' + base_atom_names[:, 3, :] = ['CA', 'C', 'O'] + + # 4,5,6,7: 'chi1,2,3,4-group' + for restype, restype_letter in enumerate(residue_constants.restypes): + resname = residue_constants.restype_1to3[restype_letter] + for chi_idx in range(4): + if residue_constants.chi_angles_mask[restype][chi_idx]: + atom_names = residue_constants.chi_angles_atoms[resname][chi_idx] + base_atom_names[restype, chi_idx + 4, :] = atom_names[1:] + + # Translate atom names into atom37 indices. + lookuptable = residue_constants.atom_order.copy() + lookuptable[''] = 0 + restype_rigidgroup_base_atom37_idx = np.vectorize(lambda x: lookuptable[x])( + base_atom_names) + return restype_rigidgroup_base_atom37_idx + + +CHI_ATOM_INDICES = _make_chi_atom_indices() +RENAMING_MATRICES = _make_renaming_matrices() +RESTYPE_ATOM14_TO_ATOM37 = _make_restype_atom14_to_atom37() +RESTYPE_ATOM37_TO_ATOM14 = _make_restype_atom37_to_atom14() +RESTYPE_ATOM37_MASK = _make_restype_atom37_mask() +RESTYPE_ATOM14_MASK = _make_restype_atom14_mask() +RESTYPE_ATOM14_IS_AMBIGUOUS = _make_restype_atom14_is_ambiguous() +RESTYPE_RIGIDGROUP_BASE_ATOM37_IDX = _make_restype_rigidgroup_base_atom37_idx() + +# Create mask for existing rigid groups. +RESTYPE_RIGIDGROUP_MASK = np.zeros([21, 8], dtype=np.float32) +RESTYPE_RIGIDGROUP_MASK[:, 0] = 1 +RESTYPE_RIGIDGROUP_MASK[:, 3] = 1 +RESTYPE_RIGIDGROUP_MASK[:20, 4:] = residue_constants.chi_angles_mask + + +def get_atom37_mask(aatype): + if not jnp.issubdtype(aatype.dtype, jnp.integer): aatype = aatype.argmax(-1) + return utils.batched_gather(jnp.asarray(RESTYPE_ATOM37_MASK), aatype) + +def get_atom14_mask(aatype): + if not jnp.issubdtype(aatype.dtype, jnp.integer): aatype = aatype.argmax(-1) + return utils.batched_gather(jnp.asarray(RESTYPE_ATOM14_MASK), aatype) + +def get_atom14_is_ambiguous(aatype): + if not jnp.issubdtype(aatype.dtype, jnp.integer): aatype = aatype.argmax(-1) + return utils.batched_gather(jnp.asarray(RESTYPE_ATOM14_IS_AMBIGUOUS), aatype) + +def get_atom14_to_atom37_map(aatype): + if not jnp.issubdtype(aatype.dtype, jnp.integer): aatype = aatype.argmax(-1) + return utils.batched_gather(jnp.asarray(RESTYPE_ATOM14_TO_ATOM37), aatype) + +def get_atom37_to_atom14_map(aatype): + if not jnp.issubdtype(aatype.dtype, jnp.integer): aatype = aatype.argmax(-1) + return utils.batched_gather(jnp.asarray(RESTYPE_ATOM37_TO_ATOM14), aatype) + +def atom14_to_atom37(atom14_data: jnp.ndarray, # (N, 14, ...) + aatype: jnp.ndarray + ) -> jnp.ndarray: # (N, 37, ...) + """Convert atom14 to atom37 representation.""" + if not jnp.issubdtype(aatype.dtype, jnp.integer): + aatype = aatype.argmax(-1) + + assert len(atom14_data.shape) in [2, 3] + idx_atom37_to_atom14 = get_atom37_to_atom14_map(aatype) + atom37_data = utils.batched_gather( + atom14_data, idx_atom37_to_atom14, batch_dims=1) + atom37_mask = get_atom37_mask(aatype) + if len(atom14_data.shape) == 2: + atom37_data *= atom37_mask + elif len(atom14_data.shape) == 3: + atom37_data *= atom37_mask[:, :, None].astype(atom37_data.dtype) + return atom37_data + + +def atom37_to_atom14(aatype, all_atom_pos, all_atom_mask): + """Convert Atom37 positions to Atom14 positions.""" + residx_atom14_to_atom37 = utils.batched_gather( + jnp.asarray(RESTYPE_ATOM14_TO_ATOM37), aatype) + atom14_mask = utils.batched_gather( + all_atom_mask, residx_atom14_to_atom37, batch_dims=1).astype(jnp.float32) + # create a mask for known groundtruth positions + atom14_mask *= utils.batched_gather(jnp.asarray(RESTYPE_ATOM14_MASK), aatype) + # gather the groundtruth positions + atom14_positions = jax.tree_map( + lambda x: utils.batched_gather(x, residx_atom14_to_atom37, batch_dims=1), + all_atom_pos) + atom14_positions = atom14_mask * atom14_positions + return atom14_positions, atom14_mask + + +def get_alt_atom14(aatype, positions: geometry.Vec3Array, mask): + """Get alternative atom14 positions.""" + # pick the transformation matrices for the given residue sequence + # shape (num_res, 14, 14) + renaming_transform = utils.batched_gather( + jnp.asarray(RENAMING_MATRICES), aatype) + + alternative_positions = jax.tree_map( + lambda x: jnp.sum(x, axis=1), positions[:, :, None] * renaming_transform) + + # Create the mask for the alternative ground truth (differs from the + # ground truth mask, if only one of the atoms in an ambiguous pair has a + # ground truth position) + alternative_mask = jnp.sum(mask[..., None] * renaming_transform, axis=1) + + return alternative_positions, alternative_mask + + +def atom37_to_frames( + aatype: jnp.ndarray, # (...) + all_atom_positions: geometry.Vec3Array, # (..., 37) + all_atom_mask: jnp.ndarray, # (..., 37) +) -> Dict[Text, jnp.ndarray]: + if not jnp.issubdtype(aatype.dtype, jnp.integer): aatype = aatype.argmax(-1) + + """Computes the frames for the up to 8 rigid groups for each residue.""" + # 0: 'backbone group', + # 1: 'pre-omega-group', (empty) + # 2: 'phi-group', (currently empty, because it defines only hydrogens) + # 3: 'psi-group', + # 4,5,6,7: 'chi1,2,3,4-group' + aatype_in_shape = aatype.shape + + # If there is a batch axis, just flatten it away, and reshape everything + # back at the end of the function. + aatype = jnp.reshape(aatype, [-1]) + all_atom_positions = jax.tree_map(lambda x: jnp.reshape(x, [-1, 37]), + all_atom_positions) + all_atom_mask = jnp.reshape(all_atom_mask, [-1, 37]) + + # Compute the gather indices for all residues in the chain. + # shape (N, 8, 3) + residx_rigidgroup_base_atom37_idx = utils.batched_gather( + RESTYPE_RIGIDGROUP_BASE_ATOM37_IDX, aatype) + + # Gather the base atom positions for each rigid group. + base_atom_pos = jax.tree_map( + lambda x: utils.batched_gather( # pylint: disable=g-long-lambda + x, residx_rigidgroup_base_atom37_idx, batch_dims=1), + all_atom_positions) + + # Compute the Rigids. + point_on_neg_x_axis = base_atom_pos[:, :, 0] + origin = base_atom_pos[:, :, 1] + point_on_xy_plane = base_atom_pos[:, :, 2] + gt_rotation = geometry.Rot3Array.from_two_vectors( + origin - point_on_neg_x_axis, point_on_xy_plane - origin) + + gt_frames = geometry.Rigid3Array(gt_rotation, origin) + + # Compute a mask whether the group exists. + # (N, 8) + group_exists = utils.batched_gather(RESTYPE_RIGIDGROUP_MASK, aatype) + + # Compute a mask whether ground truth exists for the group + gt_atoms_exist = utils.batched_gather( # shape (N, 8, 3) + all_atom_mask.astype(jnp.float32), + residx_rigidgroup_base_atom37_idx, + batch_dims=1) + gt_exists = jnp.min(gt_atoms_exist, axis=-1) * group_exists # (N, 8) + + # Adapt backbone frame to old convention (mirror x-axis and z-axis). + rots = np.tile(np.eye(3, dtype=np.float32), [8, 1, 1]) + rots[0, 0, 0] = -1 + rots[0, 2, 2] = -1 + gt_frames = gt_frames.compose_rotation( + geometry.Rot3Array.from_array(rots)) + + # The frames for ambiguous rigid groups are just rotated by 180 degree around + # the x-axis. The ambiguous group is always the last chi-group. + restype_rigidgroup_is_ambiguous = np.zeros([21, 8], dtype=np.float32) + restype_rigidgroup_rots = np.tile(np.eye(3, dtype=np.float32), [21, 8, 1, 1]) + + for resname, _ in residue_constants.residue_atom_renaming_swaps.items(): + restype = residue_constants.restype_order[ + residue_constants.restype_3to1[resname]] + chi_idx = int(sum(residue_constants.chi_angles_mask[restype]) - 1) + restype_rigidgroup_is_ambiguous[restype, chi_idx + 4] = 1 + restype_rigidgroup_rots[restype, chi_idx + 4, 1, 1] = -1 + restype_rigidgroup_rots[restype, chi_idx + 4, 2, 2] = -1 + + # Gather the ambiguity information for each residue. + residx_rigidgroup_is_ambiguous = utils.batched_gather( + restype_rigidgroup_is_ambiguous, aatype) + ambiguity_rot = utils.batched_gather(restype_rigidgroup_rots, aatype) + ambiguity_rot = geometry.Rot3Array.from_array(ambiguity_rot) + + # Create the alternative ground truth frames. + alt_gt_frames = gt_frames.compose_rotation(ambiguity_rot) + + fix_shape = lambda x: jnp.reshape(x, aatype_in_shape + (8,)) + + # reshape back to original residue layout + gt_frames = jax.tree_map(fix_shape, gt_frames) + gt_exists = fix_shape(gt_exists) + group_exists = fix_shape(group_exists) + residx_rigidgroup_is_ambiguous = fix_shape(residx_rigidgroup_is_ambiguous) + alt_gt_frames = jax.tree_map(fix_shape, alt_gt_frames) + + return { + 'rigidgroups_gt_frames': gt_frames, # Rigid (..., 8) + 'rigidgroups_gt_exists': gt_exists, # (..., 8) + 'rigidgroups_group_exists': group_exists, # (..., 8) + 'rigidgroups_group_is_ambiguous': + residx_rigidgroup_is_ambiguous, # (..., 8) + 'rigidgroups_alt_gt_frames': alt_gt_frames, # Rigid (..., 8) + } + + +def torsion_angles_to_frames( + aatype: jnp.ndarray, # (N) + backb_to_global: geometry.Rigid3Array, # (N) + torsion_angles_sin_cos: jnp.ndarray # (N, 7, 2) +) -> geometry.Rigid3Array: # (N, 8) + """Compute rigid group frames from torsion angles.""" + if not jnp.issubdtype(aatype.dtype, jnp.integer): + aatype = aatype.argmax(-1) + + assert len(aatype.shape) == 1, ( + f'Expected array of rank 1, got array with shape: {aatype.shape}.') + assert len(backb_to_global.rotation.shape) == 1, ( + f'Expected array of rank 1, got array with shape: ' + f'{backb_to_global.rotation.shape}') + assert len(torsion_angles_sin_cos.shape) == 3, ( + f'Expected array of rank 3, got array with shape: ' + f'{torsion_angles_sin_cos.shape}') + assert torsion_angles_sin_cos.shape[1] == 7, ( + f'wrong shape {torsion_angles_sin_cos.shape}') + assert torsion_angles_sin_cos.shape[2] == 2, ( + f'wrong shape {torsion_angles_sin_cos.shape}') + + # Gather the default frames for all rigid groups. + # geometry.Rigid3Array with shape (N, 8) + m = utils.batched_gather(residue_constants.restype_rigid_group_default_frame, + aatype) + default_frames = geometry.Rigid3Array.from_array4x4(m) + + # Create the rotation matrices according to the given angles (each frame is + # defined such that its rotation is around the x-axis). + sin_angles = torsion_angles_sin_cos[..., 0] + cos_angles = torsion_angles_sin_cos[..., 1] + + # insert zero rotation for backbone group. + num_residues, = aatype.shape + sin_angles = jnp.concatenate([jnp.zeros([num_residues, 1]), sin_angles], + axis=-1) + cos_angles = jnp.concatenate([jnp.ones([num_residues, 1]), cos_angles], + axis=-1) + zeros = jnp.zeros_like(sin_angles) + ones = jnp.ones_like(sin_angles) + + # all_rots are geometry.Rot3Array with shape (N, 8) + all_rots = geometry.Rot3Array(ones, zeros, zeros, + zeros, cos_angles, -sin_angles, + zeros, sin_angles, cos_angles) + + # Apply rotations to the frames. + all_frames = default_frames.compose_rotation(all_rots) + + # chi2, chi3, and chi4 frames do not transform to the backbone frame but to + # the previous frame. So chain them up accordingly. + + chi1_frame_to_backb = all_frames[:, 4] + chi2_frame_to_backb = chi1_frame_to_backb @ all_frames[:, 5] + chi3_frame_to_backb = chi2_frame_to_backb @ all_frames[:, 6] + chi4_frame_to_backb = chi3_frame_to_backb @ all_frames[:, 7] + + all_frames_to_backb = jax.tree_map( + lambda *x: jnp.concatenate(x, axis=-1), all_frames[:, 0:5], + chi2_frame_to_backb[:, None], chi3_frame_to_backb[:, None], + chi4_frame_to_backb[:, None]) + + # Create the global frames. + # shape (N, 8) + all_frames_to_global = backb_to_global[:, None] @ all_frames_to_backb + + return all_frames_to_global + + +def frames_and_literature_positions_to_atom14_pos( + aatype: jnp.ndarray, # (N) + all_frames_to_global: geometry.Rigid3Array # (N, 8) +) -> geometry.Vec3Array: # (N, 14) + """Put atom literature positions (atom14 encoding) in each rigid group.""" + + if not jnp.issubdtype(aatype.dtype, jnp.integer): + aatype = aatype.argmax(-1) + + # Pick the appropriate transform for every atom. + residx_to_group_idx = utils.batched_gather( + residue_constants.restype_atom14_to_rigid_group, aatype) + group_mask = jax.nn.one_hot( + residx_to_group_idx, num_classes=8) # shape (N, 14, 8) + + # geometry.Rigid3Array with shape (N, 14) + map_atoms_to_global = jax.tree_map( + lambda x: jnp.sum(x[:, None, :] * group_mask, axis=-1), + all_frames_to_global) + + # Gather the literature atom positions for each residue. + # geometry.Vec3Array with shape (N, 14) + lit_positions = geometry.Vec3Array.from_array( + utils.batched_gather( + residue_constants.restype_atom14_rigid_group_positions, aatype)) + + # Transform each atom from its local frame to the global frame. + # geometry.Vec3Array with shape (N, 14) + pred_positions = map_atoms_to_global.apply_to_point(lit_positions) + + # Mask out non-existing atoms. + mask = utils.batched_gather(residue_constants.restype_atom14_mask, aatype) + pred_positions = pred_positions * mask + + return pred_positions + + +def extreme_ca_ca_distance_violations( + positions: geometry.Vec3Array, # (N, 37(14)) + mask: jnp.ndarray, # (N, 37(14)) + residue_index: jnp.ndarray, # (N) + max_angstrom_tolerance=1.5 + ) -> jnp.ndarray: + """Counts residues whose Ca is a large distance from its neighbor.""" + this_ca_pos = positions[:-1, 1] # (N - 1,) + this_ca_mask = mask[:-1, 1] # (N - 1) + next_ca_pos = positions[1:, 1] # (N - 1,) + next_ca_mask = mask[1:, 1] # (N - 1) + has_no_gap_mask = ((residue_index[1:] - residue_index[:-1]) == 1.0).astype( + jnp.float32) + ca_ca_distance = geometry.euclidean_distance(this_ca_pos, next_ca_pos, 1e-6) + violations = (ca_ca_distance - + residue_constants.ca_ca) > max_angstrom_tolerance + mask = this_ca_mask * next_ca_mask * has_no_gap_mask + return utils.mask_mean(mask=mask, value=violations) + + +def between_residue_bond_loss( + pred_atom_positions: geometry.Vec3Array, # (N, 37(14)) + pred_atom_mask: jnp.ndarray, # (N, 37(14)) + residue_index: jnp.ndarray, # (N) + aatype: jnp.ndarray, # (N) + tolerance_factor_soft=12.0, + tolerance_factor_hard=12.0) -> Dict[Text, jnp.ndarray]: + """Flat-bottom loss to penalize structural violations between residues.""" + if not jnp.issubdtype(aatype.dtype, jnp.integer): + aatype = aatype.argmax(-1) + + assert len(pred_atom_positions.shape) == 2 + assert len(pred_atom_mask.shape) == 2 + assert len(residue_index.shape) == 1 + assert len(aatype.shape) == 1 + + # Get the positions of the relevant backbone atoms. + this_ca_pos = pred_atom_positions[:-1, 1] # (N - 1) + this_ca_mask = pred_atom_mask[:-1, 1] # (N - 1) + this_c_pos = pred_atom_positions[:-1, 2] # (N - 1) + this_c_mask = pred_atom_mask[:-1, 2] # (N - 1) + next_n_pos = pred_atom_positions[1:, 0] # (N - 1) + next_n_mask = pred_atom_mask[1:, 0] # (N - 1) + next_ca_pos = pred_atom_positions[1:, 1] # (N - 1) + next_ca_mask = pred_atom_mask[1:, 1] # (N - 1) + has_no_gap_mask = ((residue_index[1:] - residue_index[:-1]) == 1.0).astype( + jnp.float32) + + # Compute loss for the C--N bond. + c_n_bond_length = geometry.euclidean_distance(this_c_pos, next_n_pos, 1e-6) + + # The C-N bond to proline has slightly different length because of the ring. + next_is_proline = ( + aatype[1:] == residue_constants.restype_order['P']).astype(jnp.float32) + gt_length = ( + (1. - next_is_proline) * residue_constants.between_res_bond_length_c_n[0] + + next_is_proline * residue_constants.between_res_bond_length_c_n[1]) + gt_stddev = ( + (1. - next_is_proline) * + residue_constants.between_res_bond_length_stddev_c_n[0] + + next_is_proline * residue_constants.between_res_bond_length_stddev_c_n[1]) + c_n_bond_length_error = jnp.sqrt(1e-6 + + jnp.square(c_n_bond_length - gt_length)) + c_n_loss_per_residue = jax.nn.relu( + c_n_bond_length_error - tolerance_factor_soft * gt_stddev) + mask = this_c_mask * next_n_mask * has_no_gap_mask + c_n_loss = jnp.sum(mask * c_n_loss_per_residue) / (jnp.sum(mask) + 1e-6) + c_n_violation_mask = mask * ( + c_n_bond_length_error > (tolerance_factor_hard * gt_stddev)) + + # Compute loss for the angles. + c_ca_unit_vec = (this_ca_pos - this_c_pos).normalized(1e-6) + c_n_unit_vec = (next_n_pos - this_c_pos) / c_n_bond_length + n_ca_unit_vec = (next_ca_pos - next_n_pos).normalized(1e-6) + + ca_c_n_cos_angle = c_ca_unit_vec.dot(c_n_unit_vec) + gt_angle = residue_constants.between_res_cos_angles_ca_c_n[0] + gt_stddev = residue_constants.between_res_bond_length_stddev_c_n[0] + ca_c_n_cos_angle_error = jnp.sqrt( + 1e-6 + jnp.square(ca_c_n_cos_angle - gt_angle)) + ca_c_n_loss_per_residue = jax.nn.relu( + ca_c_n_cos_angle_error - tolerance_factor_soft * gt_stddev) + mask = this_ca_mask * this_c_mask * next_n_mask * has_no_gap_mask + ca_c_n_loss = jnp.sum(mask * ca_c_n_loss_per_residue) / (jnp.sum(mask) + 1e-6) + ca_c_n_violation_mask = mask * (ca_c_n_cos_angle_error > + (tolerance_factor_hard * gt_stddev)) + + c_n_ca_cos_angle = (-c_n_unit_vec).dot(n_ca_unit_vec) + gt_angle = residue_constants.between_res_cos_angles_c_n_ca[0] + gt_stddev = residue_constants.between_res_cos_angles_c_n_ca[1] + c_n_ca_cos_angle_error = jnp.sqrt( + 1e-6 + jnp.square(c_n_ca_cos_angle - gt_angle)) + c_n_ca_loss_per_residue = jax.nn.relu( + c_n_ca_cos_angle_error - tolerance_factor_soft * gt_stddev) + mask = this_c_mask * next_n_mask * next_ca_mask * has_no_gap_mask + c_n_ca_loss = jnp.sum(mask * c_n_ca_loss_per_residue) / (jnp.sum(mask) + 1e-6) + c_n_ca_violation_mask = mask * ( + c_n_ca_cos_angle_error > (tolerance_factor_hard * gt_stddev)) + + # Compute a per residue loss (equally distribute the loss to both + # neighbouring residues). + per_residue_loss_sum = (c_n_loss_per_residue + + ca_c_n_loss_per_residue + + c_n_ca_loss_per_residue) + per_residue_loss_sum = 0.5 * (jnp.pad(per_residue_loss_sum, [[0, 1]]) + + jnp.pad(per_residue_loss_sum, [[1, 0]])) + + # Compute hard violations. + violation_mask = jnp.max( + jnp.stack([c_n_violation_mask, + ca_c_n_violation_mask, + c_n_ca_violation_mask]), axis=0) + violation_mask = jnp.maximum( + jnp.pad(violation_mask, [[0, 1]]), + jnp.pad(violation_mask, [[1, 0]])) + + return {'c_n_loss_mean': c_n_loss, # shape () + 'ca_c_n_loss_mean': ca_c_n_loss, # shape () + 'c_n_ca_loss_mean': c_n_ca_loss, # shape () + 'per_residue_loss_sum': per_residue_loss_sum, # shape (N) + 'per_residue_violation_mask': violation_mask # shape (N) + } + + +def between_residue_clash_loss( + pred_positions: geometry.Vec3Array, # (N, 14) + atom_exists: jnp.ndarray, # (N, 14) + atom_radius: jnp.ndarray, # (N, 14) + residue_index: jnp.ndarray, # (N) + asym_id: jnp.ndarray, # (N) + overlap_tolerance_soft=1.5, + overlap_tolerance_hard=1.5) -> Dict[Text, jnp.ndarray]: + """Loss to penalize steric clashes between residues.""" + assert len(pred_positions.shape) == 2 + assert len(atom_exists.shape) == 2 + assert len(atom_radius.shape) == 2 + assert len(residue_index.shape) == 1 + + # Create the distance matrix. + # (N, N, 14, 14) + dists = geometry.euclidean_distance(pred_positions[:, None, :, None], + pred_positions[None, :, None, :], 1e-10) + + # Create the mask for valid distances. + # shape (N, N, 14, 14) + dists_mask = (atom_exists[:, None, :, None] * atom_exists[None, :, None, :]) + + # Mask out all the duplicate entries in the lower triangular matrix. + # Also mask out the diagonal (atom-pairs from the same residue) -- these atoms + # are handled separately. + dists_mask *= ( + residue_index[:, None, None, None] < residue_index[None, :, None, None]) + + # Backbone C--N bond between subsequent residues is no clash. + c_one_hot = jax.nn.one_hot(2, num_classes=14) + n_one_hot = jax.nn.one_hot(0, num_classes=14) + neighbour_mask = ((residue_index[:, None] + 1) == residue_index[None, :]) + neighbour_mask &= (asym_id[:, None] == asym_id[None, :]) + neighbour_mask = neighbour_mask[..., None, None] + c_n_bonds = neighbour_mask * c_one_hot[None, None, :, + None] * n_one_hot[None, None, None, :] + dists_mask *= (1. - c_n_bonds) + + # Disulfide bridge between two cysteines is no clash. + cys_sg_idx = residue_constants.restype_name_to_atom14_names['CYS'].index('SG') + cys_sg_one_hot = jax.nn.one_hot(cys_sg_idx, num_classes=14) + disulfide_bonds = (cys_sg_one_hot[None, None, :, None] * + cys_sg_one_hot[None, None, None, :]) + dists_mask *= (1. - disulfide_bonds) + + # Compute the lower bound for the allowed distances. + # shape (N, N, 14, 14) + dists_lower_bound = dists_mask * ( + atom_radius[:, None, :, None] + atom_radius[None, :, None, :]) + + # Compute the error. + # shape (N, N, 14, 14) + dists_to_low_error = dists_mask * jax.nn.relu( + dists_lower_bound - overlap_tolerance_soft - dists) + + # Compute the mean loss. + # shape () + mean_loss = (jnp.sum(dists_to_low_error) + / (1e-6 + jnp.sum(dists_mask))) + + # Compute the per atom loss sum. + # shape (N, 14) + per_atom_loss_sum = (jnp.sum(dists_to_low_error, axis=[0, 2]) + + jnp.sum(dists_to_low_error, axis=[1, 3])) + + # Compute the hard clash mask. + # shape (N, N, 14, 14) + clash_mask = dists_mask * ( + dists < (dists_lower_bound - overlap_tolerance_hard)) + + # Compute the per atom clash. + # shape (N, 14) + per_atom_clash_mask = jnp.maximum( + jnp.max(clash_mask, axis=[0, 2]), + jnp.max(clash_mask, axis=[1, 3])) + + return {'mean_loss': mean_loss, # shape () + 'per_atom_loss_sum': per_atom_loss_sum, # shape (N, 14) + 'per_atom_clash_mask': per_atom_clash_mask # shape (N, 14) + } + + +def within_residue_violations( + pred_positions: geometry.Vec3Array, # (N, 14) + atom_exists: jnp.ndarray, # (N, 14) + dists_lower_bound: jnp.ndarray, # (N, 14, 14) + dists_upper_bound: jnp.ndarray, # (N, 14, 14) + tighten_bounds_for_loss=0.0, +) -> Dict[Text, jnp.ndarray]: + """Find within-residue violations.""" + assert len(pred_positions.shape) == 2 + assert len(atom_exists.shape) == 2 + assert len(dists_lower_bound.shape) == 3 + assert len(dists_upper_bound.shape) == 3 + + # Compute the mask for each residue. + # shape (N, 14, 14) + dists_masks = (1. - jnp.eye(14, 14)[None]) + dists_masks *= (atom_exists[:, :, None] * atom_exists[:, None, :]) + + # Distance matrix + # shape (N, 14, 14) + dists = geometry.euclidean_distance(pred_positions[:, :, None], + pred_positions[:, None, :], 1e-10) + + # Compute the loss. + # shape (N, 14, 14) + dists_to_low_error = jax.nn.relu( + dists_lower_bound + tighten_bounds_for_loss - dists) + dists_to_high_error = jax.nn.relu( + dists + tighten_bounds_for_loss - dists_upper_bound) + loss = dists_masks * (dists_to_low_error + dists_to_high_error) + + # Compute the per atom loss sum. + # shape (N, 14) + per_atom_loss_sum = (jnp.sum(loss, axis=1) + + jnp.sum(loss, axis=2)) + + # Compute the violations mask. + # shape (N, 14, 14) + violations = dists_masks * ((dists < dists_lower_bound) | + (dists > dists_upper_bound)) + + # Compute the per atom violations. + # shape (N, 14) + per_atom_violations = jnp.maximum( + jnp.max(violations, axis=1), jnp.max(violations, axis=2)) + + return {'per_atom_loss_sum': per_atom_loss_sum, # shape (N, 14) + 'per_atom_violations': per_atom_violations # shape (N, 14) + } + + +def find_optimal_renaming( + gt_positions: geometry.Vec3Array, # (N, 14) + alt_gt_positions: geometry.Vec3Array, # (N, 14) + atom_is_ambiguous: jnp.ndarray, # (N, 14) + gt_exists: jnp.ndarray, # (N, 14) + pred_positions: geometry.Vec3Array, # (N, 14) +) -> jnp.ndarray: # (N): + """Find optimal renaming for ground truth that maximizes LDDT.""" + assert len(gt_positions.shape) == 2 + assert len(alt_gt_positions.shape) == 2 + assert len(atom_is_ambiguous.shape) == 2 + assert len(gt_exists.shape) == 2 + assert len(pred_positions.shape) == 2 + + # Create the pred distance matrix. + # shape (N, N, 14, 14) + pred_dists = geometry.euclidean_distance(pred_positions[:, None, :, None], + pred_positions[None, :, None, :], + 1e-10) + + # Compute distances for ground truth with original and alternative names. + # shape (N, N, 14, 14) + gt_dists = geometry.euclidean_distance(gt_positions[:, None, :, None], + gt_positions[None, :, None, :], 1e-10) + + alt_gt_dists = geometry.euclidean_distance(alt_gt_positions[:, None, :, None], + alt_gt_positions[None, :, None, :], + 1e-10) + + # Compute LDDT's. + # shape (N, N, 14, 14) + lddt = jnp.sqrt(1e-10 + squared_difference(pred_dists, gt_dists)) + alt_lddt = jnp.sqrt(1e-10 + squared_difference(pred_dists, alt_gt_dists)) + + # Create a mask for ambiguous atoms in rows vs. non-ambiguous atoms + # in cols. + # shape (N ,N, 14, 14) + mask = ( + gt_exists[:, None, :, None] * # rows + atom_is_ambiguous[:, None, :, None] * # rows + gt_exists[None, :, None, :] * # cols + (1. - atom_is_ambiguous[None, :, None, :])) # cols + + # Aggregate distances for each residue to the non-amibuguous atoms. + # shape (N) + per_res_lddt = jnp.sum(mask * lddt, axis=[1, 2, 3]) + alt_per_res_lddt = jnp.sum(mask * alt_lddt, axis=[1, 2, 3]) + + # Decide for each residue, whether alternative naming is better. + # shape (N) + alt_naming_is_better = (alt_per_res_lddt < per_res_lddt).astype(jnp.float32) + + return alt_naming_is_better # shape (N) + + +def frame_aligned_point_error( + pred_frames: geometry.Rigid3Array, # shape (num_frames) + target_frames: geometry.Rigid3Array, # shape (num_frames) + frames_mask: jnp.ndarray, # shape (num_frames) + pred_positions: geometry.Vec3Array, # shape (num_positions) + target_positions: geometry.Vec3Array, # shape (num_positions) + positions_mask: jnp.ndarray, # shape (num_positions) + pair_mask: jnp.ndarray, # shape (num_frames, num_posiitons) + l1_clamp_distance: float, + length_scale=20., + epsilon=1e-4) -> jnp.ndarray: # shape () + """Measure point error under different alignements. + + Computes error between two structures with B points + under A alignments derived form the given pairs of frames. + Args: + pred_frames: num_frames reference frames for 'pred_positions'. + target_frames: num_frames reference frames for 'target_positions'. + frames_mask: Mask for frame pairs to use. + pred_positions: num_positions predicted positions of the structure. + target_positions: num_positions target positions of the structure. + positions_mask: Mask on which positions to score. + pair_mask: A (num_frames, num_positions) mask to use in the loss, useful + for separating intra from inter chain losses. + l1_clamp_distance: Distance cutoff on error beyond which gradients will + be zero. + length_scale: length scale to divide loss by. + epsilon: small value used to regularize denominator for masked average. + Returns: + Masked Frame aligned point error. + """ + # For now we do not allow any batch dimensions. + assert len(pred_frames.rotation.shape) == 1 + assert len(target_frames.rotation.shape) == 1 + assert frames_mask.ndim == 1 + assert pred_positions.x.ndim == 1 + assert target_positions.x.ndim == 1 + assert positions_mask.ndim == 1 + + # Compute array of predicted positions in the predicted frames. + # geometry.Vec3Array (num_frames, num_positions) + local_pred_pos = pred_frames[:, None].inverse().apply_to_point( + pred_positions[None, :]) + + # Compute array of target positions in the target frames. + # geometry.Vec3Array (num_frames, num_positions) + local_target_pos = target_frames[:, None].inverse().apply_to_point( + target_positions[None, :]) + + # Compute errors between the structures. + # jnp.ndarray (num_frames, num_positions) + error_dist = geometry.euclidean_distance(local_pred_pos, local_target_pos, + epsilon) + + clipped_error_dist = jnp.clip(error_dist, 0, l1_clamp_distance) + + normed_error = clipped_error_dist / length_scale + normed_error *= jnp.expand_dims(frames_mask, axis=-1) + normed_error *= jnp.expand_dims(positions_mask, axis=-2) + if pair_mask is not None: + normed_error *= pair_mask + + mask = (jnp.expand_dims(frames_mask, axis=-1) * + jnp.expand_dims(positions_mask, axis=-2)) + if pair_mask is not None: + mask *= pair_mask + normalization_factor = jnp.sum(mask, axis=(-1, -2)) + return (jnp.sum(normed_error, axis=(-2, -1)) / + (epsilon + normalization_factor)) + + +def get_chi_atom_indices(): + """Returns atom indices needed to compute chi angles for all residue types. + + Returns: + A tensor of shape [residue_types=21, chis=4, atoms=4]. The residue types are + in the order specified in residue_constants.restypes + unknown residue type + at the end. For chi angles which are not defined on the residue, the + positions indices are by default set to 0. + """ + chi_atom_indices = [] + for residue_name in residue_constants.restypes: + residue_name = residue_constants.restype_1to3[residue_name] + residue_chi_angles = residue_constants.chi_angles_atoms[residue_name] + atom_indices = [] + for chi_angle in residue_chi_angles: + atom_indices.append( + [residue_constants.atom_order[atom] for atom in chi_angle]) + for _ in range(4 - len(atom_indices)): + atom_indices.append([0, 0, 0, 0]) # For chi angles not defined on the AA. + chi_atom_indices.append(atom_indices) + + chi_atom_indices.append([[0, 0, 0, 0]] * 4) # For UNKNOWN residue. + + return jnp.asarray(chi_atom_indices) + + +def compute_chi_angles(positions: geometry.Vec3Array, + mask: geometry.Vec3Array, + aatype: geometry.Vec3Array): + """Computes the chi angles given all atom positions and the amino acid type. + + Args: + positions: A Vec3Array of shape + [num_res, residue_constants.atom_type_num], with positions of + atoms needed to calculate chi angles. Supports up to 1 batch dimension. + mask: An optional tensor of shape + [num_res, residue_constants.atom_type_num] that masks which atom + positions are set for each residue. If given, then the chi mask will be + set to 1 for a chi angle only if the amino acid has that chi angle and all + the chi atoms needed to calculate that chi angle are set. If not given + (set to None), the chi mask will be set to 1 for a chi angle if the amino + acid has that chi angle and whether the actual atoms needed to calculate + it were set will be ignored. + aatype: A tensor of shape [num_res] with amino acid type integer + code (0 to 21). Supports up to 1 batch dimension. + + Returns: + A tuple of tensors (chi_angles, mask), where both have shape + [num_res, 4]. The mask masks out unused chi angles for amino acid + types that have less than 4 chi angles. If atom_positions_mask is set, the + chi mask will also mask out uncomputable chi angles. + """ + + if not jnp.issubdtype(aatype.dtype, jnp.integer): + aatype = aatype.argmax(-1) + # Don't assert on the num_res and batch dimensions as they might be unknown. + + assert positions.shape[-1] == residue_constants.atom_type_num + assert mask.shape[-1] == residue_constants.atom_type_num + + # Compute the table of chi angle indices. Shape: [restypes, chis=4, atoms=4]. + chi_atom_indices = get_chi_atom_indices() + # Select atoms to compute chis. Shape: [num_res, chis=4, atoms=4]. + atom_indices = utils.batched_gather( + params=chi_atom_indices, indices=aatype, axis=0) + # Gather atom positions. Shape: [num_res, chis=4, atoms=4, xyz=3]. + chi_angle_atoms = jax.tree_map( + lambda x: utils.batched_gather( # pylint: disable=g-long-lambda + params=x, indices=atom_indices, axis=-1, batch_dims=1), positions) + a, b, c, d = [chi_angle_atoms[..., i] for i in range(4)] + + chi_angles = geometry.dihedral_angle(a, b, c, d) + + # Copy the chi angle mask, add the UNKNOWN residue. Shape: [restypes, 4]. + chi_angles_mask = list(residue_constants.chi_angles_mask) + chi_angles_mask.append([0.0, 0.0, 0.0, 0.0]) + chi_angles_mask = jnp.asarray(chi_angles_mask) + # Compute the chi angle mask. Shape [num_res, chis=4]. + chi_mask = utils.batched_gather(params=chi_angles_mask, indices=aatype, + axis=0) + + # The chi_mask is set to 1 only when all necessary chi angle atoms were set. + # Gather the chi angle atoms mask. Shape: [num_res, chis=4, atoms=4]. + chi_angle_atoms_mask = utils.batched_gather( + params=mask, indices=atom_indices, axis=-1, batch_dims=1) + # Check if all 4 chi angle atoms were set. Shape: [num_res, chis=4]. + chi_angle_atoms_mask = jnp.prod(chi_angle_atoms_mask, axis=[-1]) + chi_mask = chi_mask * chi_angle_atoms_mask.astype(jnp.float32) + + return chi_angles, chi_mask + +def make_transform_from_reference( + a_xyz: geometry.Vec3Array, + b_xyz: geometry.Vec3Array, + c_xyz: geometry.Vec3Array) -> geometry.Rigid3Array: + """Returns rotation and translation matrices to convert from reference. + + Note that this method does not take care of symmetries. If you provide the + coordinates in the non-standard way, the A atom will end up in the negative + y-axis rather than in the positive y-axis. You need to take care of such + cases in your code. + + Args: + a_xyz: A Vec3Array. + b_xyz: A Vec3Array. + c_xyz: A Vec3Array. + + Returns: + A Rigid3Array which, when applied to coordinates in a canonicalized + reference frame, will give coordinates approximately equal + the original coordinates (in the global frame). + """ + rotation = geometry.Rot3Array.from_two_vectors(c_xyz - b_xyz, + a_xyz - b_xyz) + return geometry.Rigid3Array(rotation, b_xyz) diff --git a/colabdesign/af/alphafold/model/common_modules.py b/colabdesign/af/alphafold/model/common_modules.py index f239c870..8c405eee 100644 --- a/colabdesign/af/alphafold/model/common_modules.py +++ b/colabdesign/af/alphafold/model/common_modules.py @@ -13,72 +13,113 @@ # limitations under the License. """A collection of common Haiku modules for use in protein folding.""" +import numbers +from typing import Union, Sequence + import haiku as hk import jax.numpy as jnp +import numpy as np -class Linear(hk.Module): - """Protein folding specific Linear Module. +# Constant from scipy.stats.truncnorm.std(a=-2, b=2, loc=0., scale=1.) +TRUNCATED_NORMAL_STDDEV_FACTOR = np.asarray(.87962566103423978, + dtype=np.float32) + + +def get_initializer_scale(initializer_name, input_shape): + """Get Initializer for weights and scale to multiply activations by.""" + + if initializer_name == 'zeros': + w_init = hk.initializers.Constant(0.0) + else: + # fan-in scaling + scale = 1. + for channel_dim in input_shape: + scale /= channel_dim + if initializer_name == 'relu': + scale *= 2 + + noise_scale = scale + + stddev = np.sqrt(noise_scale) + # Adjust stddev for truncation. + stddev = stddev / TRUNCATED_NORMAL_STDDEV_FACTOR + w_init = hk.initializers.TruncatedNormal(mean=0.0, stddev=stddev) + + return w_init + +class Linear(hk.Module): + """Protein folding specific Linear module. This differs from the standard Haiku Linear in a few ways: - * It supports inputs of arbitrary rank + * It supports inputs and outputs of arbitrary rank * Initializers are specified by strings """ def __init__(self, - num_output: int, + num_output: Union[int, Sequence[int]], initializer: str = 'linear', + num_input_dims: int = 1, use_bias: bool = True, bias_init: float = 0., + precision = None, name: str = 'linear'): """Constructs Linear Module. - Args: - num_output: number of output channels. + num_output: Number of output channels. Can be tuple when outputting + multiple dimensions. initializer: What initializer to use, should be one of {'linear', 'relu', 'zeros'} + num_input_dims: Number of dimensions from the end to project. use_bias: Whether to include trainable bias bias_init: Value used to initialize bias. - name: name of module, used for name scopes. + precision: What precision to use for matrix multiplication, defaults + to None. + name: Name of module, used for name scopes. """ - super().__init__(name=name) - self.num_output = num_output + if isinstance(num_output, numbers.Integral): + self.output_shape = (num_output,) + else: + self.output_shape = tuple(num_output) self.initializer = initializer self.use_bias = use_bias self.bias_init = bias_init + self.num_input_dims = num_input_dims + self.num_output_dims = len(self.output_shape) + self.precision = precision - def __call__(self, inputs: jnp.ndarray) -> jnp.ndarray: + def __call__(self, inputs): """Connects Module. - Args: - inputs: Tensor of shape [..., num_channel] - + inputs: Tensor with at least num_input_dims dimensions. Returns: - output of shape [..., num_output] + output of shape [...] + num_output. """ - n_channels = int(inputs.shape[-1]) - weight_shape = [n_channels, self.num_output] - if self.initializer == 'linear': - weight_init = hk.initializers.VarianceScaling(mode='fan_in', scale=1.) - elif self.initializer == 'relu': - weight_init = hk.initializers.VarianceScaling(mode='fan_in', scale=2.) - elif self.initializer == 'zeros': - weight_init = hk.initializers.Constant(0.0) + num_input_dims = self.num_input_dims + if self.num_input_dims > 0: + in_shape = inputs.shape[-self.num_input_dims:] + else: + in_shape = () + + weight_init = get_initializer_scale(self.initializer, in_shape) + + in_letters = 'abcde'[:self.num_input_dims] + out_letters = 'hijkl'[:self.num_output_dims] + + weight_shape = in_shape + self.output_shape weights = hk.get_parameter('weights', weight_shape, inputs.dtype, weight_init) - # this is equivalent to einsum('...c,cd->...d', inputs, weights) - # but turns out to be slightly faster - inputs = jnp.swapaxes(inputs, -1, -2) - output = jnp.einsum('...cb,cd->...db', inputs, weights) - output = jnp.swapaxes(output, -1, -2) + equation = f'...{in_letters}, {in_letters}{out_letters}->...{out_letters}' + + output = jnp.einsum(equation, inputs, weights, precision=self.precision) if self.use_bias: - bias = hk.get_parameter('bias', [self.num_output], inputs.dtype, + bias = hk.get_parameter('bias', self.output_shape, inputs.dtype, hk.initializers.Constant(self.bias_init)) output += bias - return output + return output \ No newline at end of file diff --git a/colabdesign/af/alphafold/model/config.py b/colabdesign/af/alphafold/model/config.py index 03612cda..0076f803 100644 --- a/colabdesign/af/alphafold/model/config.py +++ b/colabdesign/af/alphafold/model/config.py @@ -23,40 +23,34 @@ NUM_EXTRA_SEQ = shape_placeholders.NUM_EXTRA_SEQ NUM_TEMPLATES = shape_placeholders.NUM_TEMPLATES - def model_config(name: str) -> ml_collections.ConfigDict: """Get the ConfigDict of a CASP14 model.""" + if 'multimer' in name: + return CONFIG_MULTIMER + if name not in CONFIG_DIFFS: raise ValueError(f'Invalid model name {name}.') cfg = copy.deepcopy(CONFIG) cfg.update_from_flattened_dict(CONFIG_DIFFS[name]) return cfg - CONFIG_DIFFS = { 'model_1': { # Jumper et al. (2021) Suppl. Table 5, Model 1.1.1 - 'data.common.max_extra_msa': 5120, - 'data.common.reduce_msa_clusters_by_max_templates': True, - 'data.common.use_templates': True, 'model.embeddings_and_evoformer.template.embed_torsion_angles': True, 'model.embeddings_and_evoformer.template.enabled': True }, 'model_2': { # Jumper et al. (2021) Suppl. Table 5, Model 1.1.2 - 'data.common.reduce_msa_clusters_by_max_templates': True, - 'data.common.use_templates': True, 'model.embeddings_and_evoformer.template.embed_torsion_angles': True, 'model.embeddings_and_evoformer.template.enabled': True }, 'model_3': { # Jumper et al. (2021) Suppl. Table 5, Model 1.2.1 - 'data.common.max_extra_msa': 5120, }, 'model_4': { # Jumper et al. (2021) Suppl. Table 5, Model 1.2.2 - 'data.common.max_extra_msa': 5120, }, 'model_5': { # Jumper et al. (2021) Suppl. Table 5, Model 1.2.3 @@ -66,26 +60,19 @@ def model_config(name: str) -> ml_collections.ConfigDict: # with an additional predicted_aligned_error head that can produce # predicted TM-score (pTM) and predicted aligned errors. 'model_1_ptm': { - 'data.common.max_extra_msa': 5120, - 'data.common.reduce_msa_clusters_by_max_templates': True, - 'data.common.use_templates': True, 'model.embeddings_and_evoformer.template.embed_torsion_angles': True, 'model.embeddings_and_evoformer.template.enabled': True, 'model.heads.predicted_aligned_error.weight': 0.1 }, 'model_2_ptm': { - 'data.common.reduce_msa_clusters_by_max_templates': True, - 'data.common.use_templates': True, 'model.embeddings_and_evoformer.template.embed_torsion_angles': True, 'model.embeddings_and_evoformer.template.enabled': True, 'model.heads.predicted_aligned_error.weight': 0.1 }, 'model_3_ptm': { - 'data.common.max_extra_msa': 5120, 'model.heads.predicted_aligned_error.weight': 0.1 }, 'model_4_ptm': { - 'data.common.max_extra_msa': 5120, 'model.heads.predicted_aligned_error.weight': 0.1 }, 'model_5_ptm': { @@ -95,29 +82,6 @@ def model_config(name: str) -> ml_collections.ConfigDict: CONFIG = ml_collections.ConfigDict({ 'data': { - 'common': { - 'masked_msa': { - 'profile_prob': 0.1, - 'same_prob': 0.1, - 'uniform_prob': 0.1 - }, - 'max_extra_msa': 1024, - 'msa_cluster_features': True, - 'num_recycle': 3, - 'reduce_msa_clusters_by_max_templates': False, - 'resample_msa_in_recycling': True, - 'template_features': [ - 'template_all_atom_positions', 'template_sum_probs', - 'template_aatype', 'template_all_atom_masks', - 'template_domain_names' - ], - 'unsupervised_features': [ - 'aatype', 'residue_index', 'sequence', 'msa', 'domain_name', - 'num_alignments', 'seq_length', 'between_segment_residues', - 'deletion_matrix' - ], - 'use_templates': False, - }, 'eval': { 'feat': { 'aatype': [NUM_RES], @@ -161,7 +125,7 @@ def model_config(name: str) -> ml_collections.ConfigDict: 'seq_mask': [NUM_RES], 'target_feat': [NUM_RES, None], 'template_aatype': [NUM_TEMPLATES, NUM_RES], - 'template_all_atom_masks': [NUM_TEMPLATES, NUM_RES, None], + 'template_all_atom_mask': [NUM_TEMPLATES, NUM_RES, None], 'template_all_atom_positions': [ NUM_TEMPLATES, NUM_RES, None, None], 'template_backbone_affine_mask': [NUM_TEMPLATES, NUM_RES], @@ -171,14 +135,11 @@ def model_config(name: str) -> ml_collections.ConfigDict: 'template_pseudo_beta': [NUM_TEMPLATES, NUM_RES, None], 'template_pseudo_beta_mask': [NUM_TEMPLATES, NUM_RES], 'template_sum_probs': [NUM_TEMPLATES, None], - 'true_msa': [NUM_MSA_SEQ, NUM_RES] + 'true_msa': [NUM_MSA_SEQ, NUM_RES], + 'asym_id': [NUM_RES], + 'sym_id': [NUM_RES], + 'entity_id': [NUM_RES] }, - 'fixed_size': True, - 'subsample_templates': False, # We want top templates. - 'masked_msa_replace_fraction': 0.15, - 'max_msa_clusters': 512, - 'max_templates': 4, - 'num_ensemble': 1, }, }, 'model': { @@ -206,6 +167,7 @@ def model_config(name: str) -> ml_collections.ConfigDict: 'shared_dropout': True }, 'outer_product_mean': { + 'first': False, 'chunk_size': 128, 'dropout_rate': 0.0, 'num_outer_channel': 32, @@ -323,14 +285,13 @@ def model_config(name: str) -> ml_collections.ConfigDict: 'shared_dropout': True } }, - 'max_templates': 4, 'subbatch_size': 128, 'use_template_unit_vector': False, } }, 'global_config': { - 'mixed_precision': False, 'deterministic': False, + 'multimer_mode': False, 'subbatch_size': 4, 'use_remat': False, 'zero_init': True @@ -406,9 +367,227 @@ def model_config(name: str) -> ml_collections.ConfigDict: }, }, 'num_recycle': 3, - 'backprop_recycle': False, - 'resample_msa_in_recycling': True, - 'add_prev': False, 'use_struct': True, }, }) + +CONFIG_MULTIMER = ml_collections.ConfigDict({ + 'model': { + 'embeddings_and_evoformer': { + 'evoformer_num_block': 48, + 'evoformer': { + 'msa_column_attention': { + 'dropout_rate': 0.0, + 'gating': True, + 'num_head': 8, + 'orientation': 'per_column', + 'shared_dropout': True + }, + 'msa_row_attention_with_pair_bias': { + 'dropout_rate': 0.15, + 'gating': True, + 'num_head': 8, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'msa_transition': { + 'dropout_rate': 0.0, + 'num_intermediate_factor': 4, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'outer_product_mean': { + 'chunk_size': 128, + 'dropout_rate': 0.0, + 'first': True, + 'num_outer_channel': 32, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'pair_transition': { + 'dropout_rate': 0.0, + 'num_intermediate_factor': 4, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'triangle_attention_ending_node': { + 'dropout_rate': 0.25, + 'gating': True, + 'num_head': 4, + 'orientation': 'per_column', + 'shared_dropout': True + }, + 'triangle_attention_starting_node': { + 'dropout_rate': 0.25, + 'gating': True, + 'num_head': 4, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'triangle_multiplication_incoming': { + 'dropout_rate': 0.25, + 'equation': 'kjc,kic->ijc', + 'num_intermediate_channel': 128, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'triangle_multiplication_outgoing': { + 'dropout_rate': 0.25, + 'equation': 'ikc,jkc->ijc', + 'num_intermediate_channel': 128, + 'orientation': 'per_row', + 'shared_dropout': True + } + }, + 'extra_msa_channel': 64, + 'extra_msa_stack_num_block': 4, + 'num_extra_msa': 1, + 'masked_msa': { + 'profile_prob': 0.1, + 'replace_fraction': 0.15, + 'same_prob': 0.1, + 'uniform_prob': 0.1 + }, + 'use_chain_relative': True, + 'max_relative_chain': 2, + 'max_relative_idx': 32, + 'seq_channel': 384, + 'msa_channel': 256, + 'pair_channel': 128, + 'prev_pos': { + 'max_bin': 20.75, + 'min_bin': 3.25, + 'num_bins': 15 + }, + 'recycle_features': True, + 'recycle_pos': True, + 'template': { + 'attention': { + 'gating': False, + 'num_head': 4 + }, + 'dgram_features': { + 'max_bin': 50.75, + 'min_bin': 3.25, + 'num_bins': 39 + }, + 'enabled': True, + 'num_channels': 64, + 'subbatch_size': 128, + 'template_pair_stack': { + 'num_block': 2, + 'pair_transition': { + 'dropout_rate': 0.0, + 'num_intermediate_factor': 2, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'triangle_attention_ending_node': { + 'dropout_rate': 0.25, + 'gating': True, + 'num_head': 4, + 'orientation': 'per_column', + 'shared_dropout': True + }, + 'triangle_attention_starting_node': { + 'dropout_rate': 0.25, + 'gating': True, + 'num_head': 4, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'triangle_multiplication_incoming': { + 'dropout_rate': 0.25, + 'equation': 'kjc,kic->ijc', + 'num_intermediate_channel': 64, + 'orientation': 'per_row', + 'shared_dropout': True + }, + 'triangle_multiplication_outgoing': { + 'dropout_rate': 0.25, + 'equation': 'ikc,jkc->ijc', + 'num_intermediate_channel': 64, + 'orientation': 'per_row', + 'shared_dropout': True + } + } + }, + }, + 'global_config': { + 'deterministic': False, + 'multimer_mode': True, + 'subbatch_size': 4, + 'use_remat': False, + 'zero_init': True + }, + 'heads': { + 'distogram': { + 'first_break': 2.3125, + 'last_break': 21.6875, + 'num_bins': 64, + 'weight': 0.3 + }, + 'experimentally_resolved': { + 'filter_by_resolution': True, + 'max_resolution': 3.0, + 'min_resolution': 0.1, + 'weight': 0.01 + }, + 'masked_msa': { + 'weight': 2.0 + }, + 'predicted_aligned_error': { + 'filter_by_resolution': True, + 'max_error_bin': 31.0, + 'max_resolution': 3.0, + 'min_resolution': 0.1, + 'num_bins': 64, + 'num_channels': 128, + 'weight': 0.1 + }, + 'predicted_lddt': { + 'filter_by_resolution': True, + 'max_resolution': 3.0, + 'min_resolution': 0.1, + 'num_bins': 50, + 'num_channels': 128, + 'weight': 0.01 + }, + 'structure_module': { + 'angle_norm_weight': 0.01, + 'chi_weight': 0.5, + 'clash_overlap_tolerance': 1.5, + 'dropout': 0.1, + 'interface_fape': { + 'atom_clamp_distance': 1000.0, + 'loss_unit_distance': 20.0 + }, + 'intra_chain_fape': { + 'atom_clamp_distance': 10.0, + 'loss_unit_distance': 10.0 + }, + 'num_channel': 384, + 'num_head': 12, + 'num_layer': 8, + 'num_layer_in_transition': 3, + 'num_point_qk': 4, + 'num_point_v': 8, + 'num_scalar_qk': 16, + 'num_scalar_v': 16, + 'position_scale': 20.0, + 'sidechain': { + 'atom_clamp_distance': 10.0, + 'loss_unit_distance': 10.0, + 'num_channel': 128, + 'num_residual_block': 2, + 'weight_frac': 0.5 + }, + 'structural_violation_loss_weight': 1.0, + 'violation_tolerance_factor': 12.0, + 'weight': 1.0 + } + }, + 'num_recycle': 3, + 'use_struct': True, + } +}) \ No newline at end of file diff --git a/colabdesign/af/alphafold/model/folding.py b/colabdesign/af/alphafold/model/folding.py index 92c4d040..85a87b64 100644 --- a/colabdesign/af/alphafold/model/folding.py +++ b/colabdesign/af/alphafold/model/folding.py @@ -463,7 +463,7 @@ def fold_iter(act, key): class dummy(hk.Module): - def __init__(self, config, global_config, compute_loss=True): + def __init__(self, config, global_config): super().__init__(name="dummy") def __call__(self, representations, batch, is_training, safe_key=None): if safe_key is None: @@ -476,12 +476,11 @@ class StructureModule(hk.Module): Jumper et al. (2021) Suppl. Alg. 20 "StructureModule" """ - def __init__(self, config, global_config, compute_loss=True, + def __init__(self, config, global_config, name='structure_module'): super().__init__(name=name) self.config = config self.global_config = global_config - self.compute_loss = compute_loss def __call__(self, representations, batch, is_training, safe_key=None): @@ -515,46 +514,6 @@ def __call__(self, representations, batch, is_training, return ret - def loss(self, value, batch): - ret = {'loss': 0.} - - ret['metrics'] = {} - # If requested, compute in-graph metrics. - if self.config.compute_in_graph_metrics: - atom14_pred_positions = value['final_atom14_positions'] - # Compute renaming and violations. - value.update(compute_renamed_ground_truth(batch, atom14_pred_positions)) - value['violations'] = find_structural_violations( - batch, atom14_pred_positions, self.config) - - # Several violation metrics: - violation_metrics = compute_violation_metrics( - batch=batch, - atom14_pred_positions=atom14_pred_positions, - violations=value['violations']) - ret['metrics'].update(violation_metrics) - - backbone_loss(ret, batch, value, self.config) - - if 'renamed_atom14_gt_positions' not in value: - value.update(compute_renamed_ground_truth( - batch, value['final_atom14_positions'])) - sc_loss = sidechain_loss(batch, value, self.config) - - ret['loss'] = ((1 - self.config.sidechain.weight_frac) * ret['loss'] + - self.config.sidechain.weight_frac * sc_loss['loss']) - ret['sidechain_fape'] = sc_loss['fape'] - - supervised_chi_loss(ret, batch, value, self.config) - - if self.config.structural_violation_loss_weight: - if 'violations' not in value: - value['violations'] = find_structural_violations( - batch, value['final_atom14_positions'], self.config) - structural_violation_loss(ret, batch, value, self.config) - - return ret - def compute_renamed_ground_truth( batch: Dict[str, jnp.ndarray], @@ -613,7 +572,7 @@ def compute_renamed_ground_truth( } -def backbone_loss(ret, batch, value, config): +def backbone_loss(batch, value, config): """Backbone FAPE Loss. Jumper et al. (2021) Suppl. Alg. 20 "StructureModule" line 17 @@ -672,9 +631,8 @@ def backbone_loss(ret, batch, value, config): backbone_mask) fape_loss = (fape_loss * use_clamped_fape + fape_loss_unclamped * (1 - use_clamped_fape)) - - ret['fape'] = fape_loss[-1] - ret['loss'] += jnp.mean(fape_loss) + + return jnp.mean(fape_loss), fape_loss[-1] def sidechain_loss(batch, value, config): diff --git a/colabdesign/af/alphafold/model/folding_multimer.py b/colabdesign/af/alphafold/model/folding_multimer.py new file mode 100644 index 00000000..33a87b9b --- /dev/null +++ b/colabdesign/af/alphafold/model/folding_multimer.py @@ -0,0 +1,1031 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Modules and utilities for the structure module in the multimer system.""" + +import functools +import numbers +from typing import Any, Dict, Iterable, Mapping, Optional, Tuple, Union + +from colabdesign.af.alphafold.common import residue_constants +from colabdesign.af.alphafold.model import all_atom_multimer +from colabdesign.af.alphafold.model import common_modules +from colabdesign.af.alphafold.model import geometry +from colabdesign.af.alphafold.model import modules +from colabdesign.af.alphafold.model import prng +from colabdesign.af.alphafold.model import utils +from colabdesign.af.alphafold.model.geometry import utils as geometry_utils +import haiku as hk +import jax +import jax.numpy as jnp +import ml_collections +import numpy as np + + +EPSILON = 1e-8 +Float = Union[float, jnp.ndarray] + + +def squared_difference(x: jnp.ndarray, y: jnp.ndarray) -> jnp.ndarray: + """Computes Squared difference between two arrays.""" + return jnp.square(x - y) + + +def make_backbone_affine( + positions: geometry.Vec3Array, + mask: jnp.ndarray, + aatype: jnp.ndarray, + ) -> Tuple[geometry.Rigid3Array, jnp.ndarray]: + """Make backbone Rigid3Array and mask.""" + del aatype + a = residue_constants.atom_order['N'] + b = residue_constants.atom_order['CA'] + c = residue_constants.atom_order['C'] + + rigid_mask = (mask[:, a] * mask[:, b] * mask[:, c]).astype( + jnp.float32) + + rigid = all_atom_multimer.make_transform_from_reference( + a_xyz=positions[:, a], b_xyz=positions[:, b], c_xyz=positions[:, c]) + + return rigid, rigid_mask + + +class QuatRigid(hk.Module): + """Module for projecting Rigids via a quaternion.""" + + def __init__(self, + global_config: ml_collections.ConfigDict, + rigid_shape: Union[int, Iterable[int]] = tuple(), + full_quat: bool = False, + init: str = 'zeros', + name: str = 'quat_rigid'): + """Module projecting a Rigid Object. + + For this Module the Rotation is parametrized as a quaternion, + If 'full_quat' is True a 4 vector is produced for the rotation which is + normalized and treated as a quaternion. + When 'full_quat' is False a 3 vector is produced and the 1st component of + the quaternion is set to 1. + + Args: + global_config: Global Config, used to set certain properties of underlying + Linear module, see common_modules.Linear for details. + rigid_shape: Shape of Rigids relative to shape of activations, e.g. when + activations have shape (n,) and this is (m,) output will be (n, m) + full_quat: Whether to parametrize rotation using full quaternion. + init: initializer to use, see common_modules.Linear for details + name: Name to use for module. + """ + self.init = init + self.global_config = global_config + if isinstance(rigid_shape, int): + self.rigid_shape = (rigid_shape,) + else: + self.rigid_shape = tuple(rigid_shape) + self.full_quat = full_quat + super(QuatRigid, self).__init__(name=name) + + def __call__(self, activations: jnp.ndarray) -> geometry.Rigid3Array: + """Executes Module. + + This returns a set of rigid with the same shape as activations, projecting + the channel dimension, rigid_shape controls the trailing dimensions. + For example when activations is shape (12, 5) and rigid_shape is (3, 2) + then the shape of the output rigids will be (12, 3, 2). + This also supports passing in an empty tuple for rigid shape, in that case + the example would produce a rigid of shape (12,). + + Args: + activations: Activations to use for projection, shape [..., num_channel] + Returns: + Rigid transformations with shape [...] + rigid_shape + """ + if self.full_quat: + rigid_dim = 7 + else: + rigid_dim = 6 + linear_dims = self.rigid_shape + (rigid_dim,) + rigid_flat = common_modules.Linear( + linear_dims, + initializer=self.init, + precision=jax.lax.Precision.HIGHEST, + name='rigid')( + activations) + rigid_flat = geometry_utils.unstack(rigid_flat) + if self.full_quat: + qw, qx, qy, qz = rigid_flat[:4] + translation = rigid_flat[4:] + else: + qx, qy, qz = rigid_flat[:3] + qw = jnp.ones_like(qx) + translation = rigid_flat[3:] + rotation = geometry.Rot3Array.from_quaternion( + qw, qx, qy, qz, normalize=True) + translation = geometry.Vec3Array(*translation) + return geometry.Rigid3Array(rotation, translation) + + +class PointProjection(hk.Module): + """Given input reprensentation and frame produces points in global frame.""" + + def __init__(self, + num_points: Union[Iterable[int], int], + global_config: ml_collections.ConfigDict, + return_local_points: bool = False, + name: str = 'point_projection'): + """Constructs Linear Module. + + Args: + num_points: number of points to project. Can be tuple when outputting + multiple dimensions + global_config: Global Config, passed through to underlying Linear + return_local_points: Whether to return points in local frame as well. + name: name of module, used for name scopes. + """ + if isinstance(num_points, numbers.Integral): + self.num_points = (num_points,) + else: + self.num_points = tuple(num_points) + + self.return_local_points = return_local_points + + self.global_config = global_config + + super().__init__(name=name) + + def __call__( + self, activations: jnp.ndarray, rigids: geometry.Rigid3Array + ) -> Union[geometry.Vec3Array, Tuple[geometry.Vec3Array, geometry.Vec3Array]]: + output_shape = self.num_points + output_shape = output_shape[:-1] + (3 * output_shape[-1],) + points_local = common_modules.Linear( + output_shape, + precision=jax.lax.Precision.HIGHEST, + name='point_projection')( + activations) + points_local = jnp.split(points_local, 3, axis=-1) + points_local = geometry.Vec3Array(*points_local) + rigids = rigids[(...,) + (None,) * len(output_shape)] + points_global = rigids.apply_to_point(points_local) + if self.return_local_points: + return points_global, points_local + else: + return points_global + + +class InvariantPointAttention(hk.Module): + """Invariant point attention module. + + The high-level idea is that this attention module works over a set of points + and associated orientations in 3D space (e.g. protein residues). + + Each residue outputs a set of queries and keys as points in their local + reference frame. The attention is then defined as the euclidean distance + between the queries and keys in the global frame. + """ + + def __init__(self, + config: ml_collections.ConfigDict, + global_config: ml_collections.ConfigDict, + dist_epsilon: float = 1e-8, + name: str = 'invariant_point_attention'): + """Initialize. + + Args: + config: iterative Fold Head Config + global_config: Global Config of Model. + dist_epsilon: Small value to avoid NaN in distance calculation. + name: Sonnet name. + """ + super().__init__(name=name) + + self._dist_epsilon = dist_epsilon + self._zero_initialize_last = global_config.zero_init + + self.config = config + + self.global_config = global_config + + def __call__( + self, + inputs_1d: jnp.ndarray, + inputs_2d: jnp.ndarray, + mask: jnp.ndarray, + rigid: geometry.Rigid3Array, + ) -> jnp.ndarray: + """Compute geometric aware attention. + + Given a set of query residues (defined by affines and associated scalar + features), this function computes geometric aware attention between the + query residues and target residues. + + The residues produce points in their local reference frame, which + are converted into the global frame to get attention via euclidean distance. + + Equivalently the target residues produce points in their local frame to be + used as attention values, which are converted into the query residues local + frames. + + Args: + inputs_1d: (N, C) 1D input embedding that is the basis for the + scalar queries. + inputs_2d: (N, M, C') 2D input embedding, used for biases values in the + attention between query_inputs_1d and target_inputs_1d. + mask: (N, 1) mask to indicate query_inputs_1d that participate in + the attention. + rigid: Rigid object describing the position and orientation of + every element in query_inputs_1d. + + Returns: + Transformation of the input embedding. + """ + + num_head = self.config.num_head + + attn_logits = 0. + + num_point_qk = self.config.num_point_qk + # Each point pair (q, k) contributes Var [0.5 ||q||^2 - ] = 9 / 2 + point_variance = max(num_point_qk, 1) * 9. / 2 + point_weights = np.sqrt(1.0 / point_variance) + + # This is equivalent to jax.nn.softplus, but avoids a bug in the test... + softplus = lambda x: jnp.logaddexp(x, jnp.zeros_like(x)) + raw_point_weights = hk.get_parameter( + 'trainable_point_weights', + shape=[num_head], + # softplus^{-1} (1) + init=hk.initializers.Constant(np.log(np.exp(1.) - 1.))) + + # Trainable per-head weights for points. + trainable_point_weights = softplus(raw_point_weights) + point_weights *= trainable_point_weights + q_point = PointProjection([num_head, num_point_qk], + self.global_config, + name='q_point_projection')(inputs_1d, + rigid) + + k_point = PointProjection([num_head, num_point_qk], + self.global_config, + name='k_point_projection')(inputs_1d, + rigid) + + dist2 = geometry.square_euclidean_distance( + q_point[:, None, :, :], k_point[None, :, :, :], epsilon=0.) + attn_qk_point = -0.5 * jnp.sum(point_weights[:, None] * dist2, axis=-1) + attn_logits += attn_qk_point + + num_scalar_qk = self.config.num_scalar_qk + # We assume that all queries and keys come iid from N(0, 1) distribution + # and compute the variances of the attention logits. + # Each scalar pair (q, k) contributes Var q*k = 1 + scalar_variance = max(num_scalar_qk, 1) * 1. + scalar_weights = np.sqrt(1.0 / scalar_variance) + q_scalar = common_modules.Linear([num_head, num_scalar_qk], + use_bias=False, + name='q_scalar_projection')( + inputs_1d) + + k_scalar = common_modules.Linear([num_head, num_scalar_qk], + use_bias=False, + name='k_scalar_projection')( + inputs_1d) + q_scalar *= scalar_weights + attn_logits += jnp.einsum('qhc,khc->qkh', q_scalar, k_scalar) + + attention_2d = common_modules.Linear( + num_head, name='attention_2d')(inputs_2d) + attn_logits += attention_2d + + mask_2d = mask * jnp.swapaxes(mask, -1, -2) + attn_logits -= 1e5 * (1. - mask_2d[..., None]) + + attn_logits *= np.sqrt(1. / 3) # Normalize by number of logit terms (3) + attn = jax.nn.softmax(attn_logits, axis=-2) + + num_scalar_v = self.config.num_scalar_v + + v_scalar = common_modules.Linear([num_head, num_scalar_v], + use_bias=False, + name='v_scalar_projection')( + inputs_1d) + + # [num_query_residues, num_head, num_scalar_v] + result_scalar = jnp.einsum('qkh, khc->qhc', attn, v_scalar) + + num_point_v = self.config.num_point_v + v_point = PointProjection([num_head, num_point_v], + self.global_config, + name='v_point_projection')(inputs_1d, + rigid) + + result_point_global = jax.tree_map( + lambda x: jnp.sum(attn[..., None] * x, axis=-3), v_point[None]) + + # Features used in the linear output projection. Should have the size + # [num_query_residues, ?] + output_features = [] + num_query_residues, _ = inputs_1d.shape + + flat_shape = [num_query_residues, -1] + + result_scalar = jnp.reshape(result_scalar, flat_shape) + output_features.append(result_scalar) + + result_point_global = jax.tree_map(lambda r: jnp.reshape(r, flat_shape), + result_point_global) + result_point_local = rigid[..., None].apply_inverse_to_point( + result_point_global) + output_features.extend( + [result_point_local.x, result_point_local.y, result_point_local.z]) + + point_norms = result_point_local.norm(self._dist_epsilon) + output_features.append(point_norms) + + # Dimensions: h = heads, i and j = residues, + # c = inputs_2d channels + # Contraction happens over the second residue dimension, similarly to how + # the usual attention is performed. + result_attention_over_2d = jnp.einsum('ijh, ijc->ihc', attn, inputs_2d) + output_features.append(jnp.reshape(result_attention_over_2d, flat_shape)) + + final_init = 'zeros' if self._zero_initialize_last else 'linear' + + final_act = jnp.concatenate(output_features, axis=-1) + + return common_modules.Linear( + self.config.num_channel, + initializer=final_init, + name='output_projection')(final_act) + + +class FoldIteration(hk.Module): + """A single iteration of iterative folding. + + First, each residue attends to all residues using InvariantPointAttention. + Then, we apply transition layers to update the hidden representations. + Finally, we use the hidden representations to produce an update to the + affine of each residue. + """ + + def __init__(self, + config: ml_collections.ConfigDict, + global_config: ml_collections.ConfigDict, + name: str = 'fold_iteration'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__( + self, + activations: Mapping[str, Any], + aatype: jnp.ndarray, + sequence_mask: jnp.ndarray, + update_rigid: bool, + is_training: bool, + initial_act: jnp.ndarray, + safe_key: Optional[prng.SafeKey] = None, + static_feat_2d: Optional[jnp.ndarray] = None, + dropout_scale=1.0, + ) -> Tuple[Dict[str, Any], Dict[str, Any]]: + + c = self.config + + if safe_key is None: + safe_key = prng.SafeKey(hk.next_rng_key()) + + def safe_dropout_fn(tensor, safe_key): + return modules.apply_dropout( + tensor=tensor, + safe_key=safe_key, + rate=0.0 if self.global_config.deterministic else (c.dropout * dropout_scale), + is_training=is_training) + + rigid = activations['rigid'] + + act = activations['act'] + attention_module = InvariantPointAttention( + self.config, self.global_config) + # Attention + act += attention_module( + inputs_1d=act, + inputs_2d=static_feat_2d, + mask=sequence_mask, + rigid=rigid) + + safe_key, *sub_keys = safe_key.split(3) + sub_keys = iter(sub_keys) + act = safe_dropout_fn(act, next(sub_keys)) + act = hk.LayerNorm( + axis=-1, + create_scale=True, + create_offset=True, + name='attention_layer_norm')( + act) + final_init = 'zeros' if self.global_config.zero_init else 'linear' + + # Transition + input_act = act + for i in range(c.num_layer_in_transition): + init = 'relu' if i < c.num_layer_in_transition - 1 else final_init + act = common_modules.Linear( + c.num_channel, + initializer=init, + name='transition')( + act) + if i < c.num_layer_in_transition - 1: + act = jax.nn.relu(act) + act += input_act + act = safe_dropout_fn(act, next(sub_keys)) + act = hk.LayerNorm( + axis=-1, + create_scale=True, + create_offset=True, + name='transition_layer_norm')(act) + if update_rigid: + # Rigid update + rigid_update = QuatRigid( + self.global_config, init=final_init)( + act) + rigid = rigid @ rigid_update + + sc = MultiRigidSidechain(c.sidechain, self.global_config)( + rigid.scale_translation(c.position_scale), [act, initial_act], aatype) + + outputs = {'rigid': rigid, 'sc': sc} + + rotation = rigid.rotation #jax.tree_map(jax.lax.stop_gradient, rigid.rotation) + rigid = geometry.Rigid3Array(rotation, rigid.translation) + + new_activations = { + 'act': act, + 'rigid': rigid + } + return new_activations, outputs + + +def generate_monomer_rigids(representations: Mapping[str, jnp.ndarray], + batch: Mapping[str, jnp.ndarray], + config: ml_collections.ConfigDict, + global_config: ml_collections.ConfigDict, + is_training: bool, + safe_key: prng.SafeKey + ) -> Dict[str, Any]: + """Generate predicted Rigid's for a single chain. + + This is the main part of the iterative fold head - it iteratively applies + folding to produce a set of predicted residue positions. + + Args: + representations: Embeddings dictionary. + batch: Batch dictionary. + config: config for the iterative fold head. + global_config: global config. + is_training: is training. + safe_key: A prng.SafeKey object that wraps a PRNG key. + + Returns: + A dictionary containing residue Rigid's and sidechain positions. + """ + c = config + sequence_mask = batch['seq_mask'][:, None] + act = hk.LayerNorm( + axis=-1, create_scale=True, create_offset=True, name='single_layer_norm')( + representations['single']) + + initial_act = act + act = common_modules.Linear( + c.num_channel, name='initial_projection')(act) + + # Sequence Mask has extra 1 at the end. + rigid = geometry.Rigid3Array.identity(sequence_mask.shape[:-1]) + + fold_iteration = FoldIteration( + c, global_config, name='fold_iteration') + + assert len(batch['seq_mask'].shape) == 1 + + activations = { + 'act': + act, + 'rigid': + rigid + } + + act_2d = hk.LayerNorm( + axis=-1, + create_scale=True, + create_offset=True, + name='pair_layer_norm')( + representations['pair']) + + outputs = [] + def fold_iter(act, key): + act, out = fold_iteration( + act, + initial_act=initial_act, + static_feat_2d=act_2d, + aatype=batch['aatype'], + safe_key=prng.SafeKey(key), + sequence_mask=sequence_mask, + update_rigid=True, + is_training=is_training, + dropout_scale=batch["dropout_scale"]) + return act, out + + keys = jax.random.split(safe_key.get(), c.num_layer) + activations, output = hk.scan(fold_iter, activations, keys) + output['act'] = activations['act'] + + return output + + +class StructureModule(hk.Module): + """StructureModule as a network head. + + Jumper et al. (2021) Suppl. Alg. 20 "StructureModule" + """ + + def __init__(self, + config: ml_collections.ConfigDict, + global_config: ml_collections.ConfigDict, + name: str = 'structure_module'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__(self, + representations: Mapping[str, jnp.ndarray], + batch: Mapping[str, Any], + is_training: bool, + safe_key: Optional[prng.SafeKey] = None, + ) -> Dict[str, Any]: + c = self.config + ret = {} + + if safe_key is None: + safe_key = prng.SafeKey(hk.next_rng_key()) + + output = generate_monomer_rigids( + representations=representations, + batch=batch, + config=self.config, + global_config=self.global_config, + is_training=is_training, + safe_key=safe_key) + + ret['traj'] = output['rigid'].scale_translation(c.position_scale).to_array() + ret['sidechains'] = output['sc'] + ret['sidechains']['atom_pos'] = ret['sidechains']['atom_pos'].to_array() + ret['sidechains']['frames'] = ret['sidechains']['frames'].to_array() + if 'local_atom_pos' in ret['sidechains']: + ret['sidechains']['local_atom_pos'] = ret['sidechains'][ + 'local_atom_pos'].to_array() + ret['sidechains']['local_frames'] = ret['sidechains'][ + 'local_frames'].to_array() + + aatype = batch['aatype'] + seq_mask = batch['seq_mask'] + + atom14_pred_mask = all_atom_multimer.get_atom14_mask( + aatype) * seq_mask[:, None] + atom14_pred_positions = output['sc']['atom_pos'][-1] + ret['final_atom14_positions'] = atom14_pred_positions # (N, 14, 3) + ret['final_atom14_mask'] = atom14_pred_mask # (N, 14) + + atom37_mask = all_atom_multimer.get_atom37_mask(aatype) * seq_mask[:, None] + atom37_pred_positions = all_atom_multimer.atom14_to_atom37( + atom14_pred_positions, aatype) + atom37_pred_positions *= atom37_mask[:, :, None] + ret['final_atom_positions'] = atom37_pred_positions # (N, 37, 3) + ret['final_atom_mask'] = atom37_mask # (N, 37) + ret['final_rigids'] = ret['traj'][-1] + + ret['act'] = output['act'] + + return ret + + +def compute_atom14_gt( + aatype: jnp.ndarray, + all_atom_positions: geometry.Vec3Array, + all_atom_mask: jnp.ndarray, + pred_pos: geometry.Vec3Array +) -> Tuple[geometry.Vec3Array, jnp.ndarray, jnp.ndarray]: + """Find atom14 positions, this includes finding the correct renaming.""" + gt_positions, gt_mask = all_atom_multimer.atom37_to_atom14( + aatype, all_atom_positions, + all_atom_mask) + alt_gt_positions, alt_gt_mask = all_atom_multimer.get_alt_atom14( + aatype, gt_positions, gt_mask) + atom_is_ambiguous = all_atom_multimer.get_atom14_is_ambiguous(aatype) + + alt_naming_is_better = all_atom_multimer.find_optimal_renaming( + gt_positions=gt_positions, + alt_gt_positions=alt_gt_positions, + atom_is_ambiguous=atom_is_ambiguous, + gt_exists=gt_mask, + pred_positions=pred_pos) + + use_alt = alt_naming_is_better[:, None] + + gt_mask = (1. - use_alt) * gt_mask + use_alt * alt_gt_mask + gt_positions = (1. - use_alt) * gt_positions + use_alt * alt_gt_positions + + return gt_positions, alt_gt_mask, alt_naming_is_better + + +def backbone_loss(gt_rigid: geometry.Rigid3Array, + gt_frames_mask: jnp.ndarray, + gt_positions_mask: jnp.ndarray, + target_rigid: geometry.Rigid3Array, + config: ml_collections.ConfigDict, + pair_mask: jnp.ndarray + ) -> Tuple[Float, jnp.ndarray]: + """Backbone FAPE Loss.""" + loss_fn = functools.partial( + all_atom_multimer.frame_aligned_point_error, + l1_clamp_distance=config.atom_clamp_distance, + length_scale=config.loss_unit_distance) + + loss_fn = jax.vmap(loss_fn, (0, None, None, 0, None, None, None)) + fape = loss_fn(target_rigid, gt_rigid, gt_frames_mask, + target_rigid.translation, gt_rigid.translation, + gt_positions_mask, pair_mask) + + return jnp.mean(fape), fape[-1] + + +def compute_frames( + aatype: jnp.ndarray, + all_atom_positions: geometry.Vec3Array, + all_atom_mask: jnp.ndarray, + use_alt: jnp.ndarray + ) -> Tuple[geometry.Rigid3Array, jnp.ndarray]: + """Compute Frames from all atom positions. + + Args: + aatype: array of aatypes, int of [N] + all_atom_positions: Vector of all atom positions, shape [N, 37] + all_atom_mask: mask, shape [N] + use_alt: whether to use alternative orientation for ambiguous aatypes + shape [N] + Returns: + Rigid corresponding to Frames w shape [N, 8], + mask which Rigids are present w shape [N, 8] + """ + frames_batch = all_atom_multimer.atom37_to_frames(aatype, all_atom_positions, + all_atom_mask) + gt_frames = frames_batch['rigidgroups_gt_frames'] + alt_gt_frames = frames_batch['rigidgroups_alt_gt_frames'] + use_alt = use_alt[:, None] + + renamed_gt_frames = jax.tree_map( + lambda x, y: (1. - use_alt) * x + use_alt * y, gt_frames, alt_gt_frames) + + return renamed_gt_frames, frames_batch['rigidgroups_gt_exists'] + + +def sidechain_loss(gt_frames: geometry.Rigid3Array, + gt_frames_mask: jnp.ndarray, + gt_positions: geometry.Vec3Array, + gt_mask: jnp.ndarray, + pred_frames: geometry.Rigid3Array, + pred_positions: geometry.Vec3Array, + config: ml_collections.ConfigDict + ) -> Dict[str, jnp.ndarray]: + """Sidechain Loss using cleaned up rigids.""" + + flat_gt_frames = jax.tree_map(jnp.ravel, gt_frames) + flat_frames_mask = jnp.ravel(gt_frames_mask) + + flat_gt_positions = jax.tree_map(jnp.ravel, gt_positions) + flat_positions_mask = jnp.ravel(gt_mask) + + # Compute frame_aligned_point_error score for the final layer. + def _slice_last_layer_and_flatten(x): + return jnp.ravel(x[-1]) + + flat_pred_frames = jax.tree_map(_slice_last_layer_and_flatten, pred_frames) + flat_pred_positions = jax.tree_map(_slice_last_layer_and_flatten, + pred_positions) + fape = all_atom_multimer.frame_aligned_point_error( + pred_frames=flat_pred_frames, + target_frames=flat_gt_frames, + frames_mask=flat_frames_mask, + pred_positions=flat_pred_positions, + target_positions=flat_gt_positions, + positions_mask=flat_positions_mask, + pair_mask=None, + length_scale=config.sidechain.loss_unit_distance, + l1_clamp_distance=config.sidechain.atom_clamp_distance) + + return { + 'fape': fape, + 'loss': fape} + + +def structural_violation_loss(mask: jnp.ndarray, + violations: Mapping[str, Float], + config: ml_collections.ConfigDict + ) -> Float: + """Computes Loss for structural Violations.""" + # Put all violation losses together to one large loss. + num_atoms = jnp.sum(mask).astype(jnp.float32) + 1e-6 + between_residues = violations['between_residues'] + within_residues = violations['within_residues'] + return (config.structural_violation_loss_weight * + (between_residues['bonds_c_n_loss_mean'] + + between_residues['angles_ca_c_n_loss_mean'] + + between_residues['angles_c_n_ca_loss_mean'] + + jnp.sum(between_residues['clashes_per_atom_loss_sum'] + + within_residues['per_atom_loss_sum']) / num_atoms + )) + + +def find_structural_violations( + aatype: jnp.ndarray, + residue_index: jnp.ndarray, + mask: jnp.ndarray, + pred_positions: geometry.Vec3Array, # (N, 14) + config: ml_collections.ConfigDict, + asym_id: jnp.ndarray, + ) -> Dict[str, Any]: + """Computes several checks for structural Violations.""" + + # Compute between residue backbone violations of bonds and angles. + connection_violations = all_atom_multimer.between_residue_bond_loss( + pred_atom_positions=pred_positions, + pred_atom_mask=mask.astype(jnp.float32), + residue_index=residue_index.astype(jnp.float32), + aatype=aatype, + tolerance_factor_soft=config.violation_tolerance_factor, + tolerance_factor_hard=config.violation_tolerance_factor) + + # Compute the van der Waals radius for every atom + # (the first letter of the atom name is the element type). + # shape (N, 14) + atomtype_radius = jnp.array([ + residue_constants.van_der_waals_radius[name[0]] + for name in residue_constants.atom_types + ]) + residx_atom14_to_atom37 = all_atom_multimer.get_atom14_to_atom37_map(aatype) + atom_radius = mask * utils.batched_gather(atomtype_radius, + residx_atom14_to_atom37) + + # Compute the between residue clash loss. + between_residue_clashes = all_atom_multimer.between_residue_clash_loss( + pred_positions=pred_positions, + atom_exists=mask, + atom_radius=atom_radius, + residue_index=residue_index, + overlap_tolerance_soft=config.clash_overlap_tolerance, + overlap_tolerance_hard=config.clash_overlap_tolerance, + asym_id=asym_id) + + # Compute all within-residue violations (clashes, + # bond length and angle violations). + restype_atom14_bounds = residue_constants.make_atom14_dists_bounds( + overlap_tolerance=config.clash_overlap_tolerance, + bond_length_tolerance_factor=config.violation_tolerance_factor) + dists_lower_bound = utils.batched_gather(restype_atom14_bounds['lower_bound'], + aatype) + dists_upper_bound = utils.batched_gather(restype_atom14_bounds['upper_bound'], + aatype) + within_residue_violations = all_atom_multimer.within_residue_violations( + pred_positions=pred_positions, + atom_exists=mask, + dists_lower_bound=dists_lower_bound, + dists_upper_bound=dists_upper_bound, + tighten_bounds_for_loss=0.0) + + # Combine them to a single per-residue violation mask (used later for LDDT). + per_residue_violations_mask = jnp.max(jnp.stack([ + connection_violations['per_residue_violation_mask'], + jnp.max(between_residue_clashes['per_atom_clash_mask'], axis=-1), + jnp.max(within_residue_violations['per_atom_violations'], + axis=-1)]), axis=0) + + return { + 'between_residues': { + 'bonds_c_n_loss_mean': + connection_violations['c_n_loss_mean'], # () + 'angles_ca_c_n_loss_mean': + connection_violations['ca_c_n_loss_mean'], # () + 'angles_c_n_ca_loss_mean': + connection_violations['c_n_ca_loss_mean'], # () + 'connections_per_residue_loss_sum': + connection_violations['per_residue_loss_sum'], # (N) + 'connections_per_residue_violation_mask': + connection_violations['per_residue_violation_mask'], # (N) + 'clashes_mean_loss': + between_residue_clashes['mean_loss'], # () + 'clashes_per_atom_loss_sum': + between_residue_clashes['per_atom_loss_sum'], # (N, 14) + 'clashes_per_atom_clash_mask': + between_residue_clashes['per_atom_clash_mask'], # (N, 14) + }, + 'within_residues': { + 'per_atom_loss_sum': + within_residue_violations['per_atom_loss_sum'], # (N, 14) + 'per_atom_violations': + within_residue_violations['per_atom_violations'], # (N, 14), + }, + 'total_per_residue_violations_mask': + per_residue_violations_mask, # (N) + } + + +def compute_violation_metrics( + residue_index: jnp.ndarray, + mask: jnp.ndarray, + seq_mask: jnp.ndarray, + pred_positions: geometry.Vec3Array, # (N, 14) + violations: Mapping[str, jnp.ndarray], +) -> Dict[str, jnp.ndarray]: + """Compute several metrics to assess the structural violations.""" + ret = {} + between_residues = violations['between_residues'] + within_residues = violations['within_residues'] + extreme_ca_ca_violations = all_atom_multimer.extreme_ca_ca_distance_violations( + positions=pred_positions, + mask=mask.astype(jnp.float32), + residue_index=residue_index.astype(jnp.float32)) + ret['violations_extreme_ca_ca_distance'] = extreme_ca_ca_violations + ret['violations_between_residue_bond'] = utils.mask_mean( + mask=seq_mask, + value=between_residues['connections_per_residue_violation_mask']) + ret['violations_between_residue_clash'] = utils.mask_mean( + mask=seq_mask, + value=jnp.max(between_residues['clashes_per_atom_clash_mask'], axis=-1)) + ret['violations_within_residue'] = utils.mask_mean( + mask=seq_mask, + value=jnp.max(within_residues['per_atom_violations'], axis=-1)) + ret['violations_per_residue'] = utils.mask_mean( + mask=seq_mask, value=violations['total_per_residue_violations_mask']) + return ret + + +def supervised_chi_loss( + sequence_mask: jnp.ndarray, + target_chi_mask: jnp.ndarray, + aatype: jnp.ndarray, + target_chi_angles: jnp.ndarray, + pred_angles: jnp.ndarray, + unnormed_angles: jnp.ndarray, + config: ml_collections.ConfigDict) -> Tuple[Float, Float, Float]: + """Computes loss for direct chi angle supervision.""" + eps = 1e-6 + chi_mask = target_chi_mask.astype(jnp.float32) + + pred_angles = pred_angles[:, :, 3:] + + residue_type_one_hot = jax.nn.one_hot( + aatype, residue_constants.restype_num + 1, dtype=jnp.float32)[None] + chi_pi_periodic = jnp.einsum('ijk, kl->ijl', residue_type_one_hot, + jnp.asarray(residue_constants.chi_pi_periodic)) + + true_chi = target_chi_angles[None] + sin_true_chi = jnp.sin(true_chi) + cos_true_chi = jnp.cos(true_chi) + sin_cos_true_chi = jnp.stack([sin_true_chi, cos_true_chi], axis=-1) + + # This is -1 if chi is pi periodic and +1 if it's 2 pi periodic + shifted_mask = (1 - 2 * chi_pi_periodic)[..., None] + sin_cos_true_chi_shifted = shifted_mask * sin_cos_true_chi + + sq_chi_error = jnp.sum( + squared_difference(sin_cos_true_chi, pred_angles), -1) + sq_chi_error_shifted = jnp.sum( + squared_difference(sin_cos_true_chi_shifted, pred_angles), -1) + sq_chi_error = jnp.minimum(sq_chi_error, sq_chi_error_shifted) + + sq_chi_loss = utils.mask_mean(mask=chi_mask[None], value=sq_chi_error) + angle_norm = jnp.sqrt(jnp.sum(jnp.square(unnormed_angles), axis=-1) + eps) + norm_error = jnp.abs(angle_norm - 1.) + angle_norm_loss = utils.mask_mean(mask=sequence_mask[None, :, None], + value=norm_error) + loss = (config.chi_weight * sq_chi_loss + + config.angle_norm_weight * angle_norm_loss) + return loss, sq_chi_loss, angle_norm_loss + + +def l2_normalize(x: jnp.ndarray, + axis: int = -1, + epsilon: float = 1e-12 + ) -> jnp.ndarray: + return x / jnp.sqrt( + jnp.maximum(jnp.sum(x**2, axis=axis, keepdims=True), epsilon)) + + +def get_renamed_chi_angles(aatype: jnp.ndarray, + chi_angles: jnp.ndarray, + alt_is_better: jnp.ndarray + ) -> jnp.ndarray: + """Return renamed chi angles.""" + chi_angle_is_ambiguous = utils.batched_gather( + jnp.array(residue_constants.chi_pi_periodic, dtype=jnp.float32), aatype) + alt_chi_angles = chi_angles + np.pi * chi_angle_is_ambiguous + # Map back to [-pi, pi]. + alt_chi_angles = alt_chi_angles - 2 * np.pi * (alt_chi_angles > np.pi).astype( + jnp.float32) + alt_is_better = alt_is_better[:, None] + return (1. - alt_is_better) * chi_angles + alt_is_better * alt_chi_angles + + +class MultiRigidSidechain(hk.Module): + """Class to make side chain atoms.""" + + def __init__(self, + config: ml_collections.ConfigDict, + global_config: ml_collections.ConfigDict, + name: str = 'rigid_sidechain'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__(self, + rigid: geometry.Rigid3Array, + representations_list: Iterable[jnp.ndarray], + aatype: jnp.ndarray + ) -> Dict[str, Any]: + """Predict sidechains using multi-rigid representations. + + Args: + rigid: The Rigid's for each residue (translations in angstoms) + representations_list: A list of activations to predict sidechains from. + aatype: amino acid types. + + Returns: + dict containing atom positions and frames (in angstrom) + """ + act = [ + common_modules.Linear( # pylint: disable=g-complex-comprehension + self.config.num_channel, + name='input_projection')(jax.nn.relu(x)) + for x in representations_list] + # Sum the activation list (equivalent to concat then Conv1D) + act = sum(act) + + final_init = 'zeros' if self.global_config.zero_init else 'linear' + + # Mapping with some residual blocks. + for _ in range(self.config.num_residual_block): + old_act = act + act = common_modules.Linear( + self.config.num_channel, + initializer='relu', + name='resblock1')( + jax.nn.relu(act)) + act = common_modules.Linear( + self.config.num_channel, + initializer=final_init, + name='resblock2')( + jax.nn.relu(act)) + act += old_act + + # Map activations to torsion angles. + # [batch_size, num_res, 14] + num_res = act.shape[0] + unnormalized_angles = common_modules.Linear( + 14, name='unnormalized_angles')( + jax.nn.relu(act)) + unnormalized_angles = jnp.reshape( + unnormalized_angles, [num_res, 7, 2]) + angles = l2_normalize(unnormalized_angles, axis=-1) + + outputs = { + 'angles_sin_cos': angles, # jnp.ndarray (N, 7, 2) + 'unnormalized_angles_sin_cos': + unnormalized_angles, # jnp.ndarray (N, 7, 2) + } + + # Map torsion angles to frames. + # geometry.Rigid3Array with shape (N, 8) + all_frames_to_global = all_atom_multimer.torsion_angles_to_frames( + aatype, + rigid, + angles) + + # Use frames and literature positions to create the final atom coordinates. + # geometry.Vec3Array with shape (N, 14) + pred_positions = all_atom_multimer.frames_and_literature_positions_to_atom14_pos( + aatype, all_frames_to_global) + + outputs.update({ + 'atom_pos': pred_positions, # geometry.Vec3Array (N, 14) + 'frames': all_frames_to_global, # geometry.Rigid3Array (N, 8) + }) + return outputs diff --git a/colabdesign/af/alphafold/model/geometry/__init__.py b/colabdesign/af/alphafold/model/geometry/__init__.py new file mode 100644 index 00000000..761b886e --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/__init__.py @@ -0,0 +1,31 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Geometry Module.""" + +from colabdesign.af.alphafold.model.geometry import rigid_matrix_vector +from colabdesign.af.alphafold.model.geometry import rotation_matrix +from colabdesign.af.alphafold.model.geometry import struct_of_array +from colabdesign.af.alphafold.model.geometry import vector + +Rot3Array = rotation_matrix.Rot3Array +Rigid3Array = rigid_matrix_vector.Rigid3Array + +StructOfArray = struct_of_array.StructOfArray + +Vec3Array = vector.Vec3Array +square_euclidean_distance = vector.square_euclidean_distance +euclidean_distance = vector.euclidean_distance +dihedral_angle = vector.dihedral_angle +dot = vector.dot +cross = vector.cross diff --git a/colabdesign/af/alphafold/model/geometry/rigid_matrix_vector.py b/colabdesign/af/alphafold/model/geometry/rigid_matrix_vector.py new file mode 100644 index 00000000..4c7bb105 --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/rigid_matrix_vector.py @@ -0,0 +1,106 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rigid3Array Transformations represented by a Matrix and a Vector.""" + +from __future__ import annotations +from typing import Union + +from colabdesign.af.alphafold.model.geometry import rotation_matrix +from colabdesign.af.alphafold.model.geometry import struct_of_array +from colabdesign.af.alphafold.model.geometry import vector +import jax +import jax.numpy as jnp + +Float = Union[float, jnp.ndarray] + +VERSION = '0.1' + + +@struct_of_array.StructOfArray(same_dtype=True) +class Rigid3Array: + """Rigid Transformation, i.e. element of special euclidean group.""" + + rotation: rotation_matrix.Rot3Array + translation: vector.Vec3Array + + def __matmul__(self, other: Rigid3Array) -> Rigid3Array: + new_rotation = self.rotation @ other.rotation + new_translation = self.apply_to_point(other.translation) + return Rigid3Array(new_rotation, new_translation) + + def inverse(self) -> Rigid3Array: + """Return Rigid3Array corresponding to inverse transform.""" + inv_rotation = self.rotation.inverse() + inv_translation = inv_rotation.apply_to_point(-self.translation) + return Rigid3Array(inv_rotation, inv_translation) + + def apply_to_point(self, point: vector.Vec3Array) -> vector.Vec3Array: + """Apply Rigid3Array transform to point.""" + return self.rotation.apply_to_point(point) + self.translation + + def apply_inverse_to_point(self, point: vector.Vec3Array) -> vector.Vec3Array: + """Apply inverse Rigid3Array transform to point.""" + new_point = point - self.translation + return self.rotation.apply_inverse_to_point(new_point) + + def compose_rotation(self, other_rotation): + rot = self.rotation @ other_rotation + trans = jax.tree_map(lambda x: jnp.broadcast_to(x, rot.shape), + self.translation) + return Rigid3Array(rot, trans) + + @classmethod + def identity(cls, shape, dtype=jnp.float32) -> Rigid3Array: + """Return identity Rigid3Array of given shape.""" + return cls( + rotation_matrix.Rot3Array.identity(shape, dtype=dtype), + vector.Vec3Array.zeros(shape, dtype=dtype)) # pytype: disable=wrong-arg-count # trace-all-classes + + def scale_translation(self, factor: Float) -> Rigid3Array: + """Scale translation in Rigid3Array by 'factor'.""" + return Rigid3Array(self.rotation, self.translation * factor) + + def to_array(self): + rot_array = self.rotation.to_array() + vec_array = self.translation.to_array() + return jnp.concatenate([rot_array, vec_array[..., None]], axis=-1) + + @classmethod + def from_array(cls, array): + rot = rotation_matrix.Rot3Array.from_array(array[..., :3]) + vec = vector.Vec3Array.from_array(array[..., -1]) + return cls(rot, vec) # pytype: disable=wrong-arg-count # trace-all-classes + + @classmethod + def from_array4x4(cls, array: jnp.ndarray) -> Rigid3Array: + """Construct Rigid3Array from homogeneous 4x4 array.""" + assert array.shape[-1] == 4 + assert array.shape[-2] == 4 + rotation = rotation_matrix.Rot3Array( + array[..., 0, 0], array[..., 0, 1], array[..., 0, 2], + array[..., 1, 0], array[..., 1, 1], array[..., 1, 2], + array[..., 2, 0], array[..., 2, 1], array[..., 2, 2] + ) + translation = vector.Vec3Array( + array[..., 0, 3], array[..., 1, 3], array[..., 2, 3]) + return cls(rotation, translation) # pytype: disable=wrong-arg-count # trace-all-classes + + def __getstate__(self): + return (VERSION, (self.rotation, self.translation)) + + def __setstate__(self, state): + version, (rot, trans) = state + del version + object.__setattr__(self, 'rotation', rot) + object.__setattr__(self, 'translation', trans) diff --git a/colabdesign/af/alphafold/model/geometry/rotation_matrix.py b/colabdesign/af/alphafold/model/geometry/rotation_matrix.py new file mode 100644 index 00000000..846ea5d2 --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/rotation_matrix.py @@ -0,0 +1,157 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Rot3Array Matrix Class.""" + +from __future__ import annotations +import dataclasses + +from colabdesign.af.alphafold.model.geometry import struct_of_array +from colabdesign.af.alphafold.model.geometry import utils +from colabdesign.af.alphafold.model.geometry import vector +import jax +import jax.numpy as jnp +import numpy as np + +COMPONENTS = ['xx', 'xy', 'xz', 'yx', 'yy', 'yz', 'zx', 'zy', 'zz'] + +VERSION = '0.1' + + +@struct_of_array.StructOfArray(same_dtype=True) +class Rot3Array: + """Rot3Array Matrix in 3 dimensional Space implemented as struct of arrays.""" + + xx: jnp.ndarray = dataclasses.field(metadata={'dtype': jnp.float32}) + xy: jnp.ndarray + xz: jnp.ndarray + yx: jnp.ndarray + yy: jnp.ndarray + yz: jnp.ndarray + zx: jnp.ndarray + zy: jnp.ndarray + zz: jnp.ndarray + + __array_ufunc__ = None + + def inverse(self) -> Rot3Array: + """Returns inverse of Rot3Array.""" + return Rot3Array(self.xx, self.yx, self.zx, + self.xy, self.yy, self.zy, + self.xz, self.yz, self.zz) + + def apply_to_point(self, point: vector.Vec3Array) -> vector.Vec3Array: + """Applies Rot3Array to point.""" + return vector.Vec3Array( + self.xx * point.x + self.xy * point.y + self.xz * point.z, + self.yx * point.x + self.yy * point.y + self.yz * point.z, + self.zx * point.x + self.zy * point.y + self.zz * point.z) + + def apply_inverse_to_point(self, point: vector.Vec3Array) -> vector.Vec3Array: + """Applies inverse Rot3Array to point.""" + return self.inverse().apply_to_point(point) + + def __matmul__(self, other: Rot3Array) -> Rot3Array: + """Composes two Rot3Arrays.""" + c0 = self.apply_to_point(vector.Vec3Array(other.xx, other.yx, other.zx)) + c1 = self.apply_to_point(vector.Vec3Array(other.xy, other.yy, other.zy)) + c2 = self.apply_to_point(vector.Vec3Array(other.xz, other.yz, other.zz)) + return Rot3Array(c0.x, c1.x, c2.x, c0.y, c1.y, c2.y, c0.z, c1.z, c2.z) + + @classmethod + def identity(cls, shape, dtype=jnp.float32) -> Rot3Array: + """Returns identity of given shape.""" + ones = jnp.ones(shape, dtype=dtype) + zeros = jnp.zeros(shape, dtype=dtype) + return cls(ones, zeros, zeros, zeros, ones, zeros, zeros, zeros, ones) # pytype: disable=wrong-arg-count # trace-all-classes + + @classmethod + def from_two_vectors(cls, e0: vector.Vec3Array, + e1: vector.Vec3Array) -> Rot3Array: + """Construct Rot3Array from two Vectors. + + Rot3Array is constructed such that in the corresponding frame 'e0' lies on + the positive x-Axis and 'e1' lies in the xy plane with positive sign of y. + + Args: + e0: Vector + e1: Vector + Returns: + Rot3Array + """ + # Normalize the unit vector for the x-axis, e0. + e0 = e0.normalized() + # make e1 perpendicular to e0. + c = e1.dot(e0) + e1 = (e1 - c * e0).normalized() + # Compute e2 as cross product of e0 and e1. + e2 = e0.cross(e1) + return cls(e0.x, e1.x, e2.x, e0.y, e1.y, e2.y, e0.z, e1.z, e2.z) # pytype: disable=wrong-arg-count # trace-all-classes + + @classmethod + def from_array(cls, array: jnp.ndarray) -> Rot3Array: + """Construct Rot3Array Matrix from array of shape. [..., 3, 3].""" + unstacked = utils.unstack(array, axis=-2) + unstacked = sum([utils.unstack(x, axis=-1) for x in unstacked], []) + return cls(*unstacked) + + def to_array(self) -> jnp.ndarray: + """Convert Rot3Array to array of shape [..., 3, 3].""" + return jnp.stack( + [jnp.stack([self.xx, self.xy, self.xz], axis=-1), + jnp.stack([self.yx, self.yy, self.yz], axis=-1), + jnp.stack([self.zx, self.zy, self.zz], axis=-1)], + axis=-2) + + @classmethod + def from_quaternion(cls, + w: jnp.ndarray, + x: jnp.ndarray, + y: jnp.ndarray, + z: jnp.ndarray, + normalize: bool = True, + epsilon: float = 1e-6) -> Rot3Array: + """Construct Rot3Array from components of quaternion.""" + if normalize: + inv_norm = jax.lax.rsqrt(jnp.maximum(epsilon, w**2 + x**2 + y**2 + z**2)) + w *= inv_norm + x *= inv_norm + y *= inv_norm + z *= inv_norm + xx = 1 - 2 * (jnp.square(y) + jnp.square(z)) + xy = 2 * (x * y - w * z) + xz = 2 * (x * z + w * y) + yx = 2 * (x * y + w * z) + yy = 1 - 2 * (jnp.square(x) + jnp.square(z)) + yz = 2 * (y * z - w * x) + zx = 2 * (x * z - w * y) + zy = 2 * (y * z + w * x) + zz = 1 - 2 * (jnp.square(x) + jnp.square(y)) + return cls(xx, xy, xz, yx, yy, yz, zx, zy, zz) # pytype: disable=wrong-arg-count # trace-all-classes + + @classmethod + def random_uniform(cls, key, shape, dtype=jnp.float32) -> Rot3Array: + """Samples uniform random Rot3Array according to Haar Measure.""" + quat_array = jax.random.normal(key, tuple(shape) + (4,), dtype=dtype) + quats = utils.unstack(quat_array) + return cls.from_quaternion(*quats) + + def __getstate__(self): + return (VERSION, + [np.asarray(getattr(self, field)) for field in COMPONENTS]) + + def __setstate__(self, state): + version, state = state + del version + for i, field in enumerate(COMPONENTS): + object.__setattr__(self, field, state[i]) diff --git a/colabdesign/af/alphafold/model/geometry/struct_of_array.py b/colabdesign/af/alphafold/model/geometry/struct_of_array.py new file mode 100644 index 00000000..97a89fd4 --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/struct_of_array.py @@ -0,0 +1,220 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Class decorator to represent (nested) struct of arrays.""" + +import dataclasses + +import jax + + +def get_item(instance, key): + sliced = {} + for field in get_array_fields(instance): + num_trailing_dims = field.metadata.get('num_trailing_dims', 0) + this_key = key + if isinstance(key, tuple) and Ellipsis in this_key: + this_key += (slice(None),) * num_trailing_dims + sliced[field.name] = getattr(instance, field.name)[this_key] + return dataclasses.replace(instance, **sliced) + + +@property +def get_shape(instance): + """Returns Shape for given instance of dataclass.""" + first_field = dataclasses.fields(instance)[0] + num_trailing_dims = first_field.metadata.get('num_trailing_dims', None) + value = getattr(instance, first_field.name) + if num_trailing_dims: + return value.shape[:-num_trailing_dims] + else: + return value.shape + + +def get_len(instance): + """Returns length for given instance of dataclass.""" + shape = instance.shape + if shape: + return shape[0] + else: + raise TypeError('len() of unsized object') # Match jax.numpy behavior. + + +@property +def get_dtype(instance): + """Returns Dtype for given instance of dataclass.""" + fields = dataclasses.fields(instance) + sets_dtype = [ + field.name for field in fields if field.metadata.get('sets_dtype', False) + ] + if sets_dtype: + assert len(sets_dtype) == 1, 'at most field can set dtype' + field_value = getattr(instance, sets_dtype[0]) + elif instance.same_dtype: + field_value = getattr(instance, fields[0].name) + else: + # Should this be Value Error? + raise AttributeError('Trying to access Dtype on Struct of Array without' + 'either "same_dtype" or field setting dtype') + + if hasattr(field_value, 'dtype'): + return field_value.dtype + else: + # Should this be Value Error? + raise AttributeError(f'field_value {field_value} does not have dtype') + + +def replace(instance, **kwargs): + return dataclasses.replace(instance, **kwargs) + + +def post_init(instance): + """Validate instance has same shapes & dtypes.""" + array_fields = get_array_fields(instance) + arrays = list(get_array_fields(instance, return_values=True).values()) + first_field = array_fields[0] + # These slightly weird constructions about checking whether the leaves are + # actual arrays is since e.g. vmap internally relies on being able to + # construct pytree's with object() as leaves, this would break the checking + # as such we are only validating the object when the entries in the dataclass + # Are arrays or other dataclasses of arrays. + try: + dtype = instance.dtype + except AttributeError: + dtype = None + if dtype is not None: + first_shape = instance.shape + for array, field in zip(arrays, array_fields): + field_shape = array.shape + num_trailing_dims = field.metadata.get('num_trailing_dims', None) + if num_trailing_dims: + array_shape = array.shape + field_shape = array_shape[:-num_trailing_dims] + msg = (f'field {field} should have number of trailing dims' + ' {num_trailing_dims}') + assert len(array_shape) == len(first_shape) + num_trailing_dims, msg + else: + field_shape = array.shape + + shape_msg = (f"Stripped Shape {field_shape} of field {field} doesn't " + f"match shape {first_shape} of field {first_field}") + assert field_shape == first_shape, shape_msg + + field_dtype = array.dtype + + allowed_metadata_dtypes = field.metadata.get('allowed_dtypes', []) + if allowed_metadata_dtypes: + msg = f'Dtype is {field_dtype} but must be in {allowed_metadata_dtypes}' + assert field_dtype in allowed_metadata_dtypes, msg + + if 'dtype' in field.metadata: + target_dtype = field.metadata['dtype'] + else: + target_dtype = dtype + + msg = f'Dtype is {field_dtype} but must be {target_dtype}' + assert field_dtype == target_dtype, msg + + +def flatten(instance): + """Flatten Struct of Array instance.""" + array_likes = list(get_array_fields(instance, return_values=True).values()) + flat_array_likes = [] + inner_treedefs = [] + num_arrays = [] + for array_like in array_likes: + flat_array_like, inner_treedef = jax.tree_flatten(array_like) + inner_treedefs.append(inner_treedef) + flat_array_likes += flat_array_like + num_arrays.append(len(flat_array_like)) + metadata = get_metadata_fields(instance, return_values=True) + metadata = type(instance).metadata_cls(**metadata) + return flat_array_likes, (inner_treedefs, metadata, num_arrays) + + +def make_metadata_class(cls): + metadata_fields = get_fields(cls, + lambda x: x.metadata.get('is_metadata', False)) + metadata_cls = dataclasses.make_dataclass( + cls_name='Meta' + cls.__name__, + fields=[(field.name, field.type, field) for field in metadata_fields], + frozen=True, + eq=True) + return metadata_cls + + +def get_fields(cls_or_instance, filterfn, return_values=False): + fields = dataclasses.fields(cls_or_instance) + fields = [field for field in fields if filterfn(field)] + if return_values: + return { + field.name: getattr(cls_or_instance, field.name) for field in fields + } + else: + return fields + + +def get_array_fields(cls, return_values=False): + return get_fields( + cls, + lambda x: not x.metadata.get('is_metadata', False), + return_values=return_values) + + +def get_metadata_fields(cls, return_values=False): + return get_fields( + cls, + lambda x: x.metadata.get('is_metadata', False), + return_values=return_values) + + +class StructOfArray: + """Class Decorator for Struct Of Arrays.""" + + def __init__(self, same_dtype=True): + self.same_dtype = same_dtype + + def __call__(self, cls): + cls.__array_ufunc__ = None + cls.replace = replace + cls.same_dtype = self.same_dtype + cls.dtype = get_dtype + cls.shape = get_shape + cls.__len__ = get_len + cls.__getitem__ = get_item + cls.__post_init__ = post_init + new_cls = dataclasses.dataclass(cls, frozen=True, eq=False) # pytype: disable=wrong-keyword-args + # pytree claims to require metadata to be hashable, not sure why, + # But making derived dataclass that can just hold metadata + new_cls.metadata_cls = make_metadata_class(new_cls) + + def unflatten(aux, data): + inner_treedefs, metadata, num_arrays = aux + array_fields = [field.name for field in get_array_fields(new_cls)] + value_dict = {} + array_start = 0 + for num_array, inner_treedef, array_field in zip(num_arrays, + inner_treedefs, + array_fields): + value_dict[array_field] = jax.tree_unflatten( + inner_treedef, data[array_start:array_start + num_array]) + array_start += num_array + metadata_fields = get_metadata_fields(new_cls) + for field in metadata_fields: + value_dict[field.name] = getattr(metadata, field.name) + + return new_cls(**value_dict) + + jax.tree_util.register_pytree_node( + nodetype=new_cls, flatten_func=flatten, unflatten_func=unflatten) + return new_cls diff --git a/colabdesign/af/alphafold/model/geometry/test_utils.py b/colabdesign/af/alphafold/model/geometry/test_utils.py new file mode 100644 index 00000000..18de0741 --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/test_utils.py @@ -0,0 +1,98 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Shared utils for tests.""" + +import dataclasses + +from colabdesign.af.alphafold.model.geometry import rigid_matrix_vector +from colabdesign.af.alphafold.model.geometry import rotation_matrix +from colabdesign.af.alphafold.model.geometry import vector +import jax.numpy as jnp +import numpy as np + + +def assert_rotation_matrix_equal(matrix1: rotation_matrix.Rot3Array, + matrix2: rotation_matrix.Rot3Array): + for field in dataclasses.fields(rotation_matrix.Rot3Array): + field = field.name + np.testing.assert_array_equal( + getattr(matrix1, field), getattr(matrix2, field)) + + +def assert_rotation_matrix_close(mat1: rotation_matrix.Rot3Array, + mat2: rotation_matrix.Rot3Array): + np.testing.assert_array_almost_equal(mat1.to_array(), mat2.to_array(), 6) + + +def assert_array_equal_to_rotation_matrix(array: jnp.ndarray, + matrix: rotation_matrix.Rot3Array): + """Check that array and Matrix match.""" + np.testing.assert_array_equal(matrix.xx, array[..., 0, 0]) + np.testing.assert_array_equal(matrix.xy, array[..., 0, 1]) + np.testing.assert_array_equal(matrix.xz, array[..., 0, 2]) + np.testing.assert_array_equal(matrix.yx, array[..., 1, 0]) + np.testing.assert_array_equal(matrix.yy, array[..., 1, 1]) + np.testing.assert_array_equal(matrix.yz, array[..., 1, 2]) + np.testing.assert_array_equal(matrix.zx, array[..., 2, 0]) + np.testing.assert_array_equal(matrix.zy, array[..., 2, 1]) + np.testing.assert_array_equal(matrix.zz, array[..., 2, 2]) + + +def assert_array_close_to_rotation_matrix(array: jnp.ndarray, + matrix: rotation_matrix.Rot3Array): + np.testing.assert_array_almost_equal(matrix.to_array(), array, 6) + + +def assert_vectors_equal(vec1: vector.Vec3Array, vec2: vector.Vec3Array): + np.testing.assert_array_equal(vec1.x, vec2.x) + np.testing.assert_array_equal(vec1.y, vec2.y) + np.testing.assert_array_equal(vec1.z, vec2.z) + + +def assert_vectors_close(vec1: vector.Vec3Array, vec2: vector.Vec3Array): + np.testing.assert_allclose(vec1.x, vec2.x, atol=1e-6, rtol=0.) + np.testing.assert_allclose(vec1.y, vec2.y, atol=1e-6, rtol=0.) + np.testing.assert_allclose(vec1.z, vec2.z, atol=1e-6, rtol=0.) + + +def assert_array_close_to_vector(array: jnp.ndarray, vec: vector.Vec3Array): + np.testing.assert_allclose(vec.to_array(), array, atol=1e-6, rtol=0.) + + +def assert_array_equal_to_vector(array: jnp.ndarray, vec: vector.Vec3Array): + np.testing.assert_array_equal(vec.to_array(), array) + + +def assert_rigid_equal_to_rigid(rigid1: rigid_matrix_vector.Rigid3Array, + rigid2: rigid_matrix_vector.Rigid3Array): + assert_rot_trans_equal_to_rigid(rigid1.rotation, rigid1.translation, rigid2) + + +def assert_rigid_close_to_rigid(rigid1: rigid_matrix_vector.Rigid3Array, + rigid2: rigid_matrix_vector.Rigid3Array): + assert_rot_trans_close_to_rigid(rigid1.rotation, rigid1.translation, rigid2) + + +def assert_rot_trans_equal_to_rigid(rot: rotation_matrix.Rot3Array, + trans: vector.Vec3Array, + rigid: rigid_matrix_vector.Rigid3Array): + assert_rotation_matrix_equal(rot, rigid.rotation) + assert_vectors_equal(trans, rigid.translation) + + +def assert_rot_trans_close_to_rigid(rot: rotation_matrix.Rot3Array, + trans: vector.Vec3Array, + rigid: rigid_matrix_vector.Rigid3Array): + assert_rotation_matrix_close(rot, rigid.rotation) + assert_vectors_close(trans, rigid.translation) diff --git a/colabdesign/af/alphafold/model/geometry/utils.py b/colabdesign/af/alphafold/model/geometry/utils.py new file mode 100644 index 00000000..64c4a649 --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/utils.py @@ -0,0 +1,23 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Utils for geometry library.""" + +from typing import List + +import jax.numpy as jnp + + +def unstack(value: jnp.ndarray, axis: int = -1) -> List[jnp.ndarray]: + return [jnp.squeeze(v, axis=axis) + for v in jnp.split(value, value.shape[axis], axis=axis)] diff --git a/colabdesign/af/alphafold/model/geometry/vector.py b/colabdesign/af/alphafold/model/geometry/vector.py new file mode 100644 index 00000000..8b5e653b --- /dev/null +++ b/colabdesign/af/alphafold/model/geometry/vector.py @@ -0,0 +1,217 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Vec3Array Class.""" + +from __future__ import annotations +import dataclasses +from typing import Union + +from colabdesign.af.alphafold.model.geometry import struct_of_array +from colabdesign.af.alphafold.model.geometry import utils +import jax +import jax.numpy as jnp +import numpy as np + +Float = Union[float, jnp.ndarray] + +VERSION = '0.1' + + +@struct_of_array.StructOfArray(same_dtype=True) +class Vec3Array: + """Vec3Array in 3 dimensional Space implemented as struct of arrays. + + This is done in order to improve performance and precision. + On TPU small matrix multiplications are very suboptimal and will waste large + compute ressources, furthermore any matrix multiplication on tpu happen in + mixed bfloat16/float32 precision, which is often undesirable when handling + physical coordinates. + In most cases this will also be faster on cpu's/gpu's since it allows for + easier use of vector instructions. + """ + + x: jnp.ndarray = dataclasses.field(metadata={'dtype': jnp.float32}) + y: jnp.ndarray + z: jnp.ndarray + + def __post_init__(self): + if hasattr(self.x, 'dtype'): + assert self.x.dtype == self.y.dtype + assert self.x.dtype == self.z.dtype + assert all([x == y for x, y in zip(self.x.shape, self.y.shape)]) + assert all([x == z for x, z in zip(self.x.shape, self.z.shape)]) + + def __add__(self, other: Vec3Array) -> Vec3Array: + return jax.tree_map(lambda x, y: x + y, self, other) + + def __sub__(self, other: Vec3Array) -> Vec3Array: + return jax.tree_map(lambda x, y: x - y, self, other) + + def __mul__(self, other: Float) -> Vec3Array: + return jax.tree_map(lambda x: x * other, self) + + def __rmul__(self, other: Float) -> Vec3Array: + return self * other + + def __truediv__(self, other: Float) -> Vec3Array: + return jax.tree_map(lambda x: x / other, self) + + def __neg__(self) -> Vec3Array: + return jax.tree_map(lambda x: -x, self) + + def __pos__(self) -> Vec3Array: + return jax.tree_map(lambda x: x, self) + + def cross(self, other: Vec3Array) -> Vec3Array: + """Compute cross product between 'self' and 'other'.""" + new_x = self.y * other.z - self.z * other.y + new_y = self.z * other.x - self.x * other.z + new_z = self.x * other.y - self.y * other.x + return Vec3Array(new_x, new_y, new_z) + + def dot(self, other: Vec3Array) -> Float: + """Compute dot product between 'self' and 'other'.""" + return self.x * other.x + self.y * other.y + self.z * other.z + + def norm(self, epsilon: float = 1e-6) -> Float: + """Compute Norm of Vec3Array, clipped to epsilon.""" + # To avoid NaN on the backward pass, we must use maximum before the sqrt + norm2 = self.dot(self) + if epsilon: + norm2 = jnp.maximum(norm2, epsilon**2) + return jnp.sqrt(norm2) + + def norm2(self): + return self.dot(self) + + def normalized(self, epsilon: float = 1e-6) -> Vec3Array: + """Return unit vector with optional clipping.""" + return self / self.norm(epsilon) + + @classmethod + def zeros(cls, shape, dtype=jnp.float32): + """Return Vec3Array corresponding to zeros of given shape.""" + return cls( + jnp.zeros(shape, dtype), jnp.zeros(shape, dtype), + jnp.zeros(shape, dtype)) # pytype: disable=wrong-arg-count # trace-all-classes + + def to_array(self) -> jnp.ndarray: + return jnp.stack([self.x, self.y, self.z], axis=-1) + + @classmethod + def from_array(cls, array): + return cls(*utils.unstack(array)) + + def __getstate__(self): + return (VERSION, + [np.asarray(self.x), + np.asarray(self.y), + np.asarray(self.z)]) + + def __setstate__(self, state): + version, state = state + del version + for i, letter in enumerate('xyz'): + object.__setattr__(self, letter, state[i]) + + +def square_euclidean_distance(vec1: Vec3Array, + vec2: Vec3Array, + epsilon: float = 1e-6) -> Float: + """Computes square of euclidean distance between 'vec1' and 'vec2'. + + Args: + vec1: Vec3Array to compute distance to + vec2: Vec3Array to compute distance from, should be + broadcast compatible with 'vec1' + epsilon: distance is clipped from below to be at least epsilon + + Returns: + Array of square euclidean distances; + shape will be result of broadcasting 'vec1' and 'vec2' + """ + difference = vec1 - vec2 + distance = difference.dot(difference) + if epsilon: + distance = jnp.maximum(distance, epsilon) + return distance + + +def dot(vector1: Vec3Array, vector2: Vec3Array) -> Float: + return vector1.dot(vector2) + + +def cross(vector1: Vec3Array, vector2: Vec3Array) -> Float: + return vector1.cross(vector2) + + +def norm(vector: Vec3Array, epsilon: float = 1e-6) -> Float: + return vector.norm(epsilon) + + +def normalized(vector: Vec3Array, epsilon: float = 1e-6) -> Vec3Array: + return vector.normalized(epsilon) + + +def euclidean_distance(vec1: Vec3Array, + vec2: Vec3Array, + epsilon: float = 1e-6) -> Float: + """Computes euclidean distance between 'vec1' and 'vec2'. + + Args: + vec1: Vec3Array to compute euclidean distance to + vec2: Vec3Array to compute euclidean distance from, should be + broadcast compatible with 'vec1' + epsilon: distance is clipped from below to be at least epsilon + + Returns: + Array of euclidean distances; + shape will be result of broadcasting 'vec1' and 'vec2' + """ + distance_sq = square_euclidean_distance(vec1, vec2, epsilon**2) + distance = jnp.sqrt(distance_sq) + return distance + + +def dihedral_angle(a: Vec3Array, b: Vec3Array, c: Vec3Array, + d: Vec3Array) -> Float: + """Computes torsion angle for a quadruple of points. + + For points (a, b, c, d), this is the angle between the planes defined by + points (a, b, c) and (b, c, d). It is also known as the dihedral angle. + + Arguments: + a: A Vec3Array of coordinates. + b: A Vec3Array of coordinates. + c: A Vec3Array of coordinates. + d: A Vec3Array of coordinates. + + Returns: + A tensor of angles in radians: [-pi, pi]. + """ + v1 = a - b + v2 = b - c + v3 = d - c + + c1 = v1.cross(v2) + c2 = v3.cross(v2) + c3 = c2.cross(c1) + + v2_mag = v2.norm() + return jnp.arctan2(c3.dot(v2), v2_mag * c1.dot(c2)) + + +def random_gaussian_vector(shape, key, dtype=jnp.float32): + vec_array = jax.random.normal(key, shape + (3,), dtype) + return Vec3Array.from_array(vec_array) diff --git a/colabdesign/af/alphafold/model/model.py b/colabdesign/af/alphafold/model/model.py index 7f0c401e..5ec1c027 100644 --- a/colabdesign/af/alphafold/model/model.py +++ b/colabdesign/af/alphafold/model/model.py @@ -16,35 +16,16 @@ from typing import Any, Mapping, Optional, Union from absl import logging -from colabdesign.af.alphafold.common import confidence from colabdesign.af.alphafold.model import features from colabdesign.af.alphafold.model import modules +from colabdesign.af.alphafold.model import modules_multimer + import haiku as hk import jax import ml_collections import numpy as np -import tensorflow.compat.v1 as tf import tree - -def get_confidence_metrics( - prediction_result: Mapping[str, Any]) -> Mapping[str, Any]: - """Post processes prediction_result to get confidence metrics.""" - - confidence_metrics = {} - confidence_metrics['plddt'] = confidence.compute_plddt( - prediction_result['predicted_lddt']['logits']) - if 'predicted_aligned_error' in prediction_result: - confidence_metrics.update(confidence.compute_predicted_aligned_error( - prediction_result['predicted_aligned_error']['logits'], - prediction_result['predicted_aligned_error']['breaks'])) - confidence_metrics['ptm'] = confidence.predicted_tm_score( - prediction_result['predicted_aligned_error']['logits'], - prediction_result['predicted_aligned_error']['breaks']) - - return confidence_metrics - - class RunModel: """Container for JAX model.""" @@ -53,27 +34,23 @@ def __init__(self, params: Optional[Mapping[str, Mapping[str, np.ndarray]]] = None, is_training=True, return_representations=True, - recycle_mode=None): + recycle_mode=None, + use_multimer=False): self.config = config self.params = params self.mode = recycle_mode - if self.mode is None: - self.mode = [] - # backward compatibility - if self.config.model.add_prev: - self.mode.append("add_prev") - if self.config.model.backprop_recycle: - self.mode.append("backprop") + if self.mode is None: self.mode = [] def _forward_fn(batch): - model = modules.AlphaFold(self.config.model) + if use_multimer: + model = modules_multimer.AlphaFold(self.config.model) + else: + model = modules.AlphaFold(self.config.model) return model( batch, is_training=is_training, - compute_loss=False, - ensemble_representations=False, return_representations=return_representations) self.init = jax.jit(hk.transform(_forward_fn).init) @@ -84,34 +61,19 @@ def apply(params, key, feat): if "prev" in feat: prev = feat["prev"] else: - L = feat['aatype'].shape[1] + L = feat['aatype'].shape[0] prev = {'prev_msa_first_row': np.zeros([L,256]), 'prev_pair': np.zeros([L,L,128])} if self.config.model.use_struct: prev['prev_pos'] = np.zeros([L,37,3]) else: prev['prev_dgram'] = np.zeros([L,L,64]) + feat["prev"] = prev ################################ # decide how to run recycles ################################ - if "num_iter_recycling" in feat: - # use while_loop() - num_recycles = feat.pop("num_iter_recycling")[0] - def body(x): - i,prev,key = x - key, sub_key = jax.random.split(key) - feat["prev"] = prev - prev = self.apply_fn(params, sub_key, feat)["prev"] - prev = jax.lax.stop_gradient(prev) - return (i+1, prev, key) - - init = (0,prev,key) - _, feat["prev"], key = jax.lax.while_loop(lambda x: x[0] < num_recycles, body, init) - key, sub_key = jax.random.split(key) - results = self.apply_fn(params, sub_key, feat) - - elif self.config.model.num_recycle: + if self.config.model.num_recycle: # use scan() def loop(prev, sub_key): feat["prev"] = prev @@ -136,75 +98,4 @@ def loop(prev, sub_key): return results - self.apply = jax.jit(apply) - - def init_params(self, feat: features.FeatureDict, random_seed: int = 0): - """Initializes the model parameters. - - If none were provided when this class was instantiated then the parameters - are randomly initialized. - - Args: - feat: A dictionary of NumPy feature arrays as output by - RunModel.process_features. - random_seed: A random seed to use to initialize the parameters if none - were set when this class was initialized. - """ - if not self.params: - # Init params randomly. - rng = jax.random.PRNGKey(random_seed) - self.params = hk.data_structures.to_mutable_dict( - self.init(rng, feat)) - logging.warning('Initialized parameters randomly') - - def process_features( - self, - raw_features: Union[tf.train.Example, features.FeatureDict], - random_seed: int) -> features.FeatureDict: - """Processes features to prepare for feeding them into the model. - - Args: - raw_features: The output of the data pipeline either as a dict of NumPy - arrays or as a tf.train.Example. - random_seed: The random seed to use when processing the features. - - Returns: - A dict of NumPy feature arrays suitable for feeding into the model. - """ - if isinstance(raw_features, dict): - return features.np_example_to_features( - np_example=raw_features, - config=self.config, - random_seed=random_seed) - else: - return features.tf_example_to_features( - tf_example=raw_features, - config=self.config, - random_seed=random_seed) - - def eval_shape(self, feat: features.FeatureDict) -> jax.ShapeDtypeStruct: - self.init_params(feat) - logging.info('Running eval_shape with shape(feat) = %s', tree.map_structure(lambda x: x.shape, feat)) - shape = jax.eval_shape(self.apply, self.params, jax.random.PRNGKey(0), feat) - logging.info('Output shape was %s', shape) - return shape - - def predict(self, feat: features.FeatureDict) -> Mapping[str, Any]: - """Makes a prediction by inferencing the model on the provided features. - - Args: - feat: A dictionary of NumPy feature arrays as output by - RunModel.process_features. - - Returns: - A dictionary of model outputs. - """ - self.init_params(feat) - logging.info('Running predict with shape(feat) = %s', tree.map_structure(lambda x: x.shape, feat)) - - result = self.apply(self.params, jax.random.PRNGKey(0), feat) - if self.config.model.use_struct: - result.update(get_confidence_metrics(result)) - - logging.info('Output shape was %s', tree.map_structure(lambda x: x.shape, result)) - return result + self.apply = jax.jit(apply) \ No newline at end of file diff --git a/colabdesign/af/alphafold/model/modules.py b/colabdesign/af/alphafold/model/modules.py index ebfc9ecb..5ed5306d 100644 --- a/colabdesign/af/alphafold/model/modules.py +++ b/colabdesign/af/alphafold/model/modules.py @@ -33,22 +33,6 @@ from colabdesign.af.alphafold.model.r3 import Rigids, Rots, Vecs - -def softmax_cross_entropy(logits, labels): - """Computes softmax cross entropy given logits and one-hot class labels.""" - loss = -jnp.sum(labels * jax.nn.log_softmax(logits), axis=-1) - return jnp.asarray(loss) - - -def sigmoid_cross_entropy(logits, labels): - """Computes sigmoid cross entropy given logits and multiple class labels.""" - log_p = jax.nn.log_sigmoid(logits) - # log(1 - sigmoid(x)) = log_sigmoid(-x), the latter is more numerically stable - log_not_p = jax.nn.log_sigmoid(-logits) - loss = -labels * log_p - (1. - labels) * log_not_p - return jnp.asarray(loss) - - def apply_dropout(*, tensor, safe_key, rate, is_training, broadcast_dim=None): """Applies dropout to a tensor.""" if is_training: # and rate != 0.0: @@ -128,8 +112,7 @@ class AlphaFoldIteration(hk.Module): Computes ensembled (averaged) representations from the provided features. These representations are then passed to the various heads - that have been requested by the configuration file. Each head also returns a - loss which is combined as a weighted sum to produce the total loss. + that have been requested by the configuration file. Jumper et al. (2021) Suppl. Alg. 2 "Inference" lines 3-22 """ @@ -140,17 +123,13 @@ def __init__(self, config, global_config, name='alphafold_iteration'): self.global_config = global_config def __call__(self, - ensembled_batch, - non_ensembled_batch, + batch, is_training, - compute_loss=False, - ensemble_representations=False, return_representations=False): # Compute representations for each batch element and average. evoformer_module = EmbeddingsAndEvoformer(self.config.embeddings_and_evoformer, self.global_config) - batch0 = {**ensembled_batch, **non_ensembled_batch} - representations = evoformer_module(batch0, is_training) + representations = evoformer_module(batch, is_training) # MSA representations are not ensembled so # we don't pass tensor into the loop. @@ -158,12 +137,11 @@ def __call__(self, del representations['msa'] representations['msa'] = msa_representation - batch = batch0 # We are not ensembled from here on. - if jnp.issubdtype(ensembled_batch['aatype'].dtype, jnp.integer): - num_residues = ensembled_batch['aatype'].shape + if jnp.issubdtype(batch['aatype'].dtype, jnp.integer): + num_residues = batch['aatype'].shape else: - num_residues, _ = ensembled_batch['aatype'].shape + num_residues, _ = batch['aatype'].shape if self.config.use_struct: struct_module = folding.StructureModule @@ -177,7 +155,7 @@ def __call__(self, head_factory = { 'masked_msa': MaskedMsaHead, 'distogram': DistogramHead, - 'structure_module': functools.partial(struct_module, compute_loss=compute_loss), + 'structure_module': struct_module, 'predicted_lddt': PredictedLDDTHead, 'predicted_aligned_error': PredictedAlignedErrorHead, 'experimentally_resolved': ExperimentallyResolvedHead, @@ -185,20 +163,9 @@ def __call__(self, heads[head_name] = (head_config, head_factory(head_config, self.global_config)) - total_loss = 0. ret = {} ret['representations'] = representations - def loss(module, head_config, ret, name, filter_ret=True): - if filter_ret: - value = ret[name] - else: - value = ret - loss_output = module.loss(value, batch) - ret[name].update(loss_output) - loss = head_config.weight * ret[name]['loss'] - return loss - for name, (head_config, module) in heads.items(): # Skip PredictedLDDTHead and PredictedAlignedErrorHead until # StructureModule is executed. @@ -210,8 +177,6 @@ def loss(module, head_config, ret, name, filter_ret=True): # Extra representations from the head. Used by the structure module # to provide activations for the PredictedLDDTHead. representations.update(ret[name].pop('representations')) - if compute_loss: - total_loss += loss(module, head_config, ret, name) if self.config.use_struct: if self.config.heads.get('predicted_lddt.weight', 0.0): @@ -220,8 +185,6 @@ def loss(module, head_config, ret, name, filter_ret=True): # Feed all previous results to give access to structure_module result. head_config, module = heads[name] ret[name] = module(representations, batch, is_training) - if compute_loss: - total_loss += loss(module, head_config, ret, name, filter_ret=False) if ('predicted_aligned_error' in self.config.heads and self.config.heads.get('predicted_aligned_error.weight', 0.0)): @@ -230,13 +193,8 @@ def loss(module, head_config, ret, name, filter_ret=True): # Feed all previous results to give access to structure_module result. head_config, module = heads[name] ret[name] = module(representations, batch, is_training) - if compute_loss: - total_loss += loss(module, head_config, ret, name, filter_ret=False) - if compute_loss: - return ret, total_loss - else: - return ret + return ret class AlphaFold(hk.Module): """AlphaFold model with recycling. @@ -253,36 +211,28 @@ def __call__( self, batch, is_training, - compute_loss=False, - ensemble_representations=False, return_representations=False): """Run the AlphaFold model. Arguments: batch: Dictionary with inputs to the AlphaFold model. is_training: Whether the system is in training or inference mode. - compute_loss: Whether to compute losses (requires extra features - to be present in the batch and knowing the true structure). - ensemble_representations: Whether to use ensembling of representations. return_representations: Whether to also return the intermediate representations. Returns: - When compute_loss is True: - a tuple of loss and output of AlphaFoldIteration. - When compute_loss is False: - just output of AlphaFoldIteration. + just output of AlphaFoldIteration. The output of AlphaFoldIteration is a nested dictionary containing predictions from the various heads. """ - if "dropout_scale" not in batch: batch["dropout_scale"] = jnp.ones((1,)) - impl = AlphaFoldIteration(self.config, self.global_config) - if jnp.issubdtype(batch['aatype'].dtype, jnp.integer): - batch_size, num_residues = batch['aatype'].shape + num_res = batch['aatype'].shape else: - batch_size, num_residues, _ = batch['aatype'].shape + num_res, _ = batch['aatype'].shape + + impl = AlphaFoldIteration(self.config, self.global_config) + def get_prev(ret): new_prev = { @@ -293,33 +243,12 @@ def get_prev(ret): new_prev['prev_pos'] = ret['structure_module']['final_atom_positions'] else: new_prev['prev_dgram'] = ret["distogram"]["logits"] - return new_prev emb_config = self.config.embeddings_and_evoformer - prev = { - 'prev_msa_first_row': jnp.zeros([num_residues, emb_config.msa_channel]), - 'prev_pair': jnp.zeros([num_residues, num_residues, emb_config.pair_channel]) - } - if self.config.use_struct: - prev['prev_pos'] = jnp.zeros([num_residues, residue_constants.atom_type_num, 3]) - else: - prev['prev_dgram'] = jnp.zeros([num_residues, num_residues, 64]) - - # copy previous from input batch (if defined) - if "prev" in batch: - prev.update(batch.pop("prev")) - - # backward compatibility - for k in ["pos","msa_first_row","pair","dgram"]: - if f"init_{k}" in batch: - prev[f"prev_{k}"] = batch.pop(f"init_{k}")[0] - - ret = impl(ensembled_batch=jax.tree_map(lambda x:x[0], batch), - non_ensembled_batch=prev, - is_training=is_training, - compute_loss=compute_loss, - ensemble_representations=ensemble_representations) + prev = batch.pop("prev") + ret = impl(batch={**batch, **prev}, + is_training=is_training) ret["prev"] = get_prev(ret) return ret @@ -899,6 +828,11 @@ def __init__(self, config, global_config, name='masked_msa_head'): self.config = config self.global_config = global_config + if global_config.multimer_mode: + self.num_output = len(residue_constants.restypes_with_x_and_gap) + else: + self.num_output = config.num_output + def __call__(self, representations, batch, is_training): """Builds MaskedMsaHead module. @@ -915,21 +849,12 @@ def __call__(self, representations, batch, is_training): """ del batch logits = common_modules.Linear( - self.config.num_output, + self.num_output, initializer=utils.final_init(self.global_config), name='logits')( representations['msa']) return dict(logits=logits) - def loss(self, value, batch): - errors = softmax_cross_entropy( - labels=jax.nn.one_hot(batch['true_msa'], num_classes=23), - logits=value['logits']) - loss = (jnp.sum(errors * batch['bert_mask'], axis=(-2, -1)) / - (1e-8 + jnp.sum(batch['bert_mask'], axis=(-2, -1)))) - return {'loss': loss} - - class PredictedLDDTHead(hk.Module): """Head to predict the per-residue LDDT to be used as a confidence measure. @@ -988,51 +913,6 @@ def __call__(self, representations, batch, is_training): # Shape (batch_size, num_res, num_bins) return dict(logits=logits) - def loss(self, value, batch): - # Shape (num_res, 37, 3) - pred_all_atom_pos = value['structure_module']['final_atom_positions'] - # Shape (num_res, 37, 3) - true_all_atom_pos = batch['all_atom_positions'] - # Shape (num_res, 37) - all_atom_mask = batch['all_atom_mask'] - - # Shape (num_res,) - lddt_ca = lddt.lddt( - # Shape (batch_size, num_res, 3) - predicted_points=pred_all_atom_pos[None, :, 1, :], - # Shape (batch_size, num_res, 3) - true_points=true_all_atom_pos[None, :, 1, :], - # Shape (batch_size, num_res, 1) - true_points_mask=all_atom_mask[None, :, 1:2].astype(jnp.float32), - cutoff=15., - per_residue=True)[0] - lddt_ca = jax.lax.stop_gradient(lddt_ca) - - num_bins = self.config.num_bins - bin_index = jnp.floor(lddt_ca * num_bins).astype(jnp.int32) - - # protect against out of range for lddt_ca == 1 - bin_index = jnp.minimum(bin_index, num_bins - 1) - lddt_ca_one_hot = jax.nn.one_hot(bin_index, num_classes=num_bins) - - # Shape (num_res, num_channel) - logits = value['predicted_lddt']['logits'] - errors = softmax_cross_entropy(labels=lddt_ca_one_hot, logits=logits) - - # Shape (num_res,) - mask_ca = all_atom_mask[:, residue_constants.atom_order['CA']] - mask_ca = mask_ca.astype(jnp.float32) - loss = jnp.sum(errors * mask_ca) / (jnp.sum(mask_ca) + 1e-8) - - if self.config.filter_by_resolution: - # NMR & distillation have resolution = 0 - loss *= ((batch['resolution'] >= self.config.min_resolution) - & (batch['resolution'] <= self.config.max_resolution)).astype( - jnp.float32) - - output = {'loss': loss} - return output - class PredictedAlignedErrorHead(hk.Module): """Head to predict the distance errors in the backbone alignment frames. @@ -1074,55 +954,6 @@ def __call__(self, representations, batch, is_training): 0., self.config.max_error_bin, self.config.num_bins - 1) return dict(logits=logits, breaks=breaks) - def loss(self, value, batch): - # Shape (num_res, 7) - predicted_affine = quat_affine.QuatAffine.from_tensor( - value['structure_module']['final_affines']) - # Shape (num_res, 7) - true_affine = quat_affine.QuatAffine.from_tensor( - batch['backbone_affine_tensor']) - # Shape (num_res) - mask = batch['backbone_affine_mask'] - # Shape (num_res, num_res) - square_mask = mask[:, None] * mask[None, :] - num_bins = self.config.num_bins - # (1, num_bins - 1) - breaks = value['predicted_aligned_error']['breaks'] - # (1, num_bins) - logits = value['predicted_aligned_error']['logits'] - - # Compute the squared error for each alignment. - def _local_frame_points(affine): - points = [jnp.expand_dims(x, axis=-2) for x in affine.translation] - return affine.invert_point(points, extra_dims=1) - error_dist2_xyz = [ - jnp.square(a - b) - for a, b in zip(_local_frame_points(predicted_affine), - _local_frame_points(true_affine))] - error_dist2 = sum(error_dist2_xyz) - # Shape (num_res, num_res) - # First num_res are alignment frames, second num_res are the residues. - error_dist2 = jax.lax.stop_gradient(error_dist2) - - sq_breaks = jnp.square(breaks) - true_bins = jnp.sum(( - error_dist2[..., None] > sq_breaks).astype(jnp.int32), axis=-1) - - errors = softmax_cross_entropy( - labels=jax.nn.one_hot(true_bins, num_bins, axis=-1), logits=logits) - - loss = (jnp.sum(errors * square_mask, axis=(-2, -1)) / - (1e-8 + jnp.sum(square_mask, axis=(-2, -1)))) - - if self.config.filter_by_resolution: - # NMR & distillation have resolution = 0 - loss *= ((batch['resolution'] >= self.config.min_resolution) - & (batch['resolution'] <= self.config.max_resolution)).astype( - jnp.float32) - - output = {'loss': loss} - return output - class ExperimentallyResolvedHead(hk.Module): """Predicts if an atom is experimentally resolved in a high-res structure. @@ -1158,28 +989,6 @@ def __call__(self, representations, batch, is_training): name='logits')(representations['single']) return dict(logits=logits) - def loss(self, value, batch): - logits = value['logits'] - assert len(logits.shape) == 2 - - # Does the atom appear in the amino acid? - atom_exists = batch['atom37_atom_exists'] - # Is the atom resolved in the experiment? Subset of atom_exists, - # *except for OXT* - all_atom_mask = batch['all_atom_mask'].astype(jnp.float32) - - xent = sigmoid_cross_entropy(labels=all_atom_mask, logits=logits) - loss = jnp.sum(xent * atom_exists) / (1e-8 + jnp.sum(atom_exists)) - - if self.config.filter_by_resolution: - # NMR & distillation examples have resolution = 0. - loss *= ((batch['resolution'] >= self.config.min_resolution) - & (batch['resolution'] <= self.config.max_resolution)).astype( - jnp.float32) - - output = {'loss': loss} - return output - class TriangleMultiplication(hk.Module): """Triangle multiplication layer ("outgoing" or "incoming"). @@ -1308,42 +1117,6 @@ def __call__(self, representations, batch, is_training): return dict(logits=logits, bin_edges=breaks) - def loss(self, value, batch): - return _distogram_log_loss(value['logits'], value['bin_edges'], - batch, self.config.num_bins) - - -def _distogram_log_loss(logits, bin_edges, batch, num_bins): - """Log loss of a distogram.""" - - assert len(logits.shape) == 3 - positions = batch['pseudo_beta'] - mask = batch['pseudo_beta_mask'] - - assert positions.shape[-1] == 3 - - sq_breaks = jnp.square(bin_edges) - - dist2 = jnp.sum( - jnp.square( - jnp.expand_dims(positions, axis=-2) - - jnp.expand_dims(positions, axis=-3)), - axis=-1, - keepdims=True) - - true_bins = jnp.sum(dist2 > sq_breaks, axis=-1) - - errors = softmax_cross_entropy( - labels=jax.nn.one_hot(true_bins, num_bins), logits=logits) - - square_mask = jnp.expand_dims(mask, axis=-2) * jnp.expand_dims(mask, axis=-1) - - avg_error = ( - jnp.sum(errors * square_mask, axis=(-2, -1)) / - (1e-6 + jnp.sum(square_mask, axis=(-2, -1)))) - dist2 = dist2[..., 0] - return dict(loss=avg_error, true_dist=jnp.sqrt(1e-6 + dist2)) - class OuterProductMean(hk.Module): """Computes mean outer product. @@ -1465,7 +1238,7 @@ def dgram_from_positions_soft(positions, num_bins, min_bin, max_bin, temp=2.0): o = o/(o.sum(-1,keepdims=True) + 1e-8) return o[...,1:] -def pseudo_beta_fn(aatype, all_atom_positions, all_atom_masks): +def pseudo_beta_fn(aatype, all_atom_positions, all_atom_mask): """Create pseudo beta features.""" ca_idx = residue_constants.atom_order['CA'] @@ -1476,8 +1249,8 @@ def pseudo_beta_fn(aatype, all_atom_positions, all_atom_masks): is_gly_tile = jnp.tile(is_gly[..., None], [1] * len(is_gly.shape) + [3]) pseudo_beta = jnp.where(is_gly_tile, all_atom_positions[..., ca_idx, :], all_atom_positions[..., cb_idx, :]) - if all_atom_masks is not None: - pseudo_beta_mask = jnp.where(is_gly, all_atom_masks[..., ca_idx], all_atom_masks[..., cb_idx]) + if all_atom_mask is not None: + pseudo_beta_mask = jnp.where(is_gly, all_atom_mask[..., ca_idx], all_atom_mask[..., cb_idx]) pseudo_beta_mask = pseudo_beta_mask.astype(jnp.float32) return pseudo_beta, pseudo_beta_mask else: @@ -1487,9 +1260,9 @@ def pseudo_beta_fn(aatype, all_atom_positions, all_atom_masks): ca_pos = all_atom_positions[...,ca_idx,:] cb_pos = all_atom_positions[...,cb_idx,:] pseudo_beta = is_gly[...,None] * ca_pos + (1-is_gly[...,None]) * cb_pos - if all_atom_masks is not None: - ca_mask = all_atom_masks[...,ca_idx] - cb_mask = all_atom_masks[...,cb_idx] + if all_atom_mask is not None: + ca_mask = all_atom_mask[...,ca_idx] + cb_mask = all_atom_mask[...,cb_idx] pseudo_beta_mask = is_gly * ca_mask + (1-is_gly) * cb_mask return pseudo_beta, pseudo_beta_mask else: @@ -1638,22 +1411,15 @@ def __call__(self, batch, is_training, safe_key=None): # Embed clustered MSA. # Jumper et al. (2021) Suppl. Alg. 2 "Inference" line 5 # Jumper et al. (2021) Suppl. Alg. 3 "InputEmbedder" - preprocess_1d = common_modules.Linear( - c.msa_channel, name='preprocess_1d')( - batch['target_feat']) - - preprocess_msa = common_modules.Linear( - c.msa_channel, name='preprocess_msa')( - batch['msa_feat']) - - msa_activations = jnp.expand_dims(preprocess_1d, axis=0) + preprocess_msa - - left_single = common_modules.Linear( - c.pair_channel, name='left_single')( - batch['target_feat']) - right_single = common_modules.Linear( - c.pair_channel, name='right_single')( - batch['target_feat']) + + target_feat = batch["msa_feat"][0,:,:21] + target_feat = jnp.pad(target_feat,[[0,0],[1,0]]) + preprocess_1d = common_modules.Linear(c.msa_channel, name='preprocess_1d')(target_feat) + preprocess_msa = common_modules.Linear(c.msa_channel, name='preprocess_msa')(batch['msa_feat']) + msa_activations = preprocess_1d[None] + preprocess_msa + + left_single = common_modules.Linear(c.pair_channel, name='left_single')(target_feat) + right_single = common_modules.Linear(c.pair_channel, name='right_single')(target_feat) pair_activations = left_single[:, None] + right_single[None] mask_2d = batch['seq_mask'][:, None] * batch['seq_mask'][None, :] @@ -1719,10 +1485,15 @@ def __call__(self, batch, is_training, safe_key=None): if c.template.enabled: template_batch = {k: batch[k] for k in batch if k.startswith('template_')} + + multichain_mask = batch['asym_id'][:, None] == batch['asym_id'][None, :] + multichain_mask = jnp.where(batch["mask_template_interchain"], multichain_mask, 1) + template_pair_representation = TemplateEmbedding(c.template, gc)( pair_activations, template_batch, mask_2d, + multichain_mask, is_training=is_training, dropout_scale=batch["dropout_scale"]) @@ -1774,7 +1545,7 @@ def extra_msa_stack_fn(x): ret = all_atom.atom37_to_torsion_angles( aatype=aatype, all_atom_pos=batch['template_all_atom_positions'], - all_atom_mask=batch['template_all_atom_masks'], + all_atom_mask=batch['template_all_atom_mask'], # Ensure consistent behaviour during testing: placeholder_for_undefined=not gc.zero_init) @@ -1849,7 +1620,7 @@ def __init__(self, config, global_config, name='single_template_embedding'): self.config = config self.global_config = global_config - def __call__(self, query_embedding, batch, mask_2d, is_training, dropout_scale=1.0): + def __call__(self, query_embedding, batch, mask_2d, multichain_mask_2d, is_training, dropout_scale=1.0): """Build the single template embedding. Arguments: query_embedding: Query pair representation, shape [N_res, N_res, c_z]. @@ -1868,6 +1639,7 @@ def __call__(self, query_embedding, batch, mask_2d, is_training, dropout_scale=1 .triangle_attention_ending_node.value_dim) template_mask = batch['template_pseudo_beta_mask'] template_mask_2d = template_mask[:, None] * template_mask[None, :] + template_mask_2d = template_mask_2d * multichain_mask_2d template_mask_2d = template_mask_2d.astype(dtype) if "template_dgram" in batch: @@ -1880,8 +1652,8 @@ def __call__(self, query_embedding, batch, mask_2d, is_training, dropout_scale=1 else: template_dgram = dgram_from_positions(batch['template_pseudo_beta'], **self.config.dgram_features) + template_dgram *= template_mask_2d[..., None] template_dgram = template_dgram.astype(dtype) - to_concat = [template_dgram, template_mask_2d[:, :, None]] if jnp.issubdtype(batch['template_aatype'].dtype, jnp.integer): @@ -1896,9 +1668,9 @@ def __call__(self, query_embedding, batch, mask_2d, is_training, dropout_scale=1 # (the template mask defined above only considers pseudo CB). n, ca, c = [residue_constants.atom_order[a] for a in ('N', 'CA', 'C')] template_mask = ( - batch['template_all_atom_masks'][..., n] * - batch['template_all_atom_masks'][..., ca] * - batch['template_all_atom_masks'][..., c]) + batch['template_all_atom_mask'][..., n] * + batch['template_all_atom_mask'][..., ca] * + batch['template_all_atom_mask'][..., c]) template_mask_2d = template_mask[:, None] * template_mask[None, :] # compute unit_vector (not used by default) @@ -1957,7 +1729,8 @@ def __init__(self, config, global_config, name='template_embedding'): self.config = config self.global_config = global_config - def __call__(self, query_embedding, template_batch, mask_2d, is_training, dropout_scale=1.0): + def __call__(self, query_embedding, template_batch, mask_2d, multichain_mask_2d, + is_training, dropout_scale=1.0): """Build TemplateEmbedding module. Arguments: query_embedding: Query pair representation, shape [N_res, N_res, c_z]. @@ -1986,7 +1759,8 @@ def __call__(self, query_embedding, template_batch, mask_2d, is_training, dropou template_embedder = SingleTemplateEmbedding(self.config, self.global_config) def map_fn(batch): - return template_embedder(query_embedding, batch, mask_2d, is_training, dropout_scale=dropout_scale) + return template_embedder(query_embedding, batch, mask_2d, multichain_mask_2d, + is_training, dropout_scale=dropout_scale) template_pair_representation = mapping.sharded_map(map_fn, in_axes=0)(template_batch) diff --git a/colabdesign/af/alphafold/model/modules_multimer.py b/colabdesign/af/alphafold/model/modules_multimer.py new file mode 100644 index 00000000..4f31d6ac --- /dev/null +++ b/colabdesign/af/alphafold/model/modules_multimer.py @@ -0,0 +1,827 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Core modules, which have been refactored in AlphaFold-Multimer. + +The main difference is that MSA sampling pipeline is moved inside the JAX model +for easier implementation of recycling and ensembling. + +Lower-level modules up to EvoformerIteration are reused from modules.py. +""" + +import functools +from typing import Sequence + +from colabdesign.af.alphafold.common import residue_constants +from colabdesign.af.alphafold.model import all_atom_multimer +from colabdesign.af.alphafold.model import common_modules +from colabdesign.af.alphafold.model import folding_multimer +from colabdesign.af.alphafold.model import geometry +from colabdesign.af.alphafold.model import layer_stack +from colabdesign.af.alphafold.model import modules +from colabdesign.af.alphafold.model import prng +from colabdesign.af.alphafold.model import utils + +import haiku as hk +import jax +import jax.numpy as jnp +import numpy as np + +def create_extra_msa_feature(batch, num_extra_msa): + """Expand extra_msa into 1hot and concat with other extra msa features. + We do this as late as possible as the one_hot extra msa can be very large. + Args: + batch: a dictionary with the following keys: + * 'extra_msa': [num_seq, num_res] MSA that wasn't selected as a cluster + centre. Note - This isn't one-hotted. + * 'extra_deletion_matrix': [num_seq, num_res] Number of deletions at given + position. + num_extra_msa: Number of extra msa to use. + Returns: + Concatenated tensor of extra MSA features. + """ + # 23 = 20 amino acids + 'X' for unknown + gap + bert mask + extra_msa = batch['extra_msa'][:num_extra_msa] + deletion_matrix = batch['extra_deletion_value'][:num_extra_msa] + msa_1hot = jax.nn.one_hot(extra_msa, 23) + has_deletion = jnp.clip(deletion_matrix, 0., 1.)[..., None] + deletion_value = (jnp.arctan(deletion_matrix / 3.) * (2. / jnp.pi))[..., None] + extra_msa_mask = batch['extra_msa_mask'][:num_extra_msa] + return jnp.concatenate([msa_1hot, has_deletion, deletion_value], + axis=-1), extra_msa_mask + +class AlphaFoldIteration(hk.Module): + """A single recycling iteration of AlphaFold architecture. + + Computes ensembled (averaged) representations from the provided features. + These representations are then passed to the various heads + that have been requested by the configuration file. + """ + + def __init__(self, config, global_config, name='alphafold_iteration'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__(self, + batch, + is_training, + return_representations=False, + safe_key=None): + + + # Compute representations for each MSA sample and average. + embedding_module = EmbeddingsAndEvoformer( + self.config.embeddings_and_evoformer, self.global_config) + + safe_key, safe_subkey = safe_key.split() + representations = embedding_module(batch, is_training, safe_key=safe_subkey) + + self.representations = representations + self.batch = batch + self.heads = {} + for head_name, head_config in sorted(self.config.heads.items()): + if not head_config.weight: + continue # Do not instantiate zero-weight heads. + + head_factory = { + 'masked_msa': + modules.MaskedMsaHead, + 'distogram': + modules.DistogramHead, + 'structure_module': + folding_multimer.StructureModule, + 'predicted_aligned_error': + modules.PredictedAlignedErrorHead, + 'predicted_lddt': + modules.PredictedLDDTHead, + 'experimentally_resolved': + modules.ExperimentallyResolvedHead, + }[head_name] + self.heads[head_name] = (head_config, + head_factory(head_config, self.global_config)) + + structure_module_output = None + if 'entity_id' in batch and 'all_atom_positions' in batch: + _, fold_module = self.heads['structure_module'] + structure_module_output = fold_module(representations, batch, is_training) + + + ret = {} + ret['representations'] = representations + + for name, (head_config, module) in self.heads.items(): + if name == 'structure_module' and structure_module_output is not None: + ret[name] = structure_module_output + representations['structure_module'] = structure_module_output.pop('act') + # Skip confidence heads until StructureModule is executed. + elif name in {'predicted_lddt', 'predicted_aligned_error', + 'experimentally_resolved'}: + continue + else: + ret[name] = module(representations, batch, is_training) + + + # Add confidence heads after StructureModule is executed. + if self.config.heads.get('predicted_lddt.weight', 0.0): + name = 'predicted_lddt' + head_config, module = self.heads[name] + ret[name] = module(representations, batch, is_training) + + if self.config.heads.experimentally_resolved.weight: + name = 'experimentally_resolved' + head_config, module = self.heads[name] + ret[name] = module(representations, batch, is_training) + + if self.config.heads.get('predicted_aligned_error.weight', 0.0): + name = 'predicted_aligned_error' + head_config, module = self.heads[name] + ret[name] = module(representations, batch, is_training) + # Will be used for ipTM computation. + ret[name]['asym_id'] = batch['asym_id'] + + return ret + +class AlphaFold(hk.Module): + """AlphaFold-Multimer model with recycling. + """ + + def __init__(self, config, name='alphafold'): + super().__init__(name=name) + self.config = config + self.global_config = config.global_config + + def __call__( + self, + batch, + is_training, + return_representations=False, + safe_key=None): + + c = self.config + impl = AlphaFoldIteration(c, self.global_config) + + if safe_key is None: + safe_key = prng.SafeKey(hk.next_rng_key()) + elif isinstance(safe_key, jnp.ndarray): + safe_key = prng.SafeKey(safe_key) + + assert isinstance(batch, dict) + num_res = batch['aatype'].shape[0] + + def get_prev(ret): + new_prev = { + 'prev_pos': ret['structure_module']['final_atom_positions'], + 'prev_msa_first_row': ret['representations']['msa_first_row'], + 'prev_pair': ret['representations']['pair'], + } + return new_prev + + def apply_network(prev, safe_key): + recycled_batch = {**batch, **prev} + return impl( + batch=recycled_batch, + is_training=is_training, + safe_key=safe_key) + + ret = apply_network(prev=batch.pop("prev"), safe_key=safe_key) + ret["prev"] = get_prev(ret) + + if not return_representations: + del ret['representations'] + return ret + +class EmbeddingsAndEvoformer(hk.Module): + """Embeds the input data and runs Evoformer. + + Produces the MSA, single and pair representations. + """ + + def __init__(self, config, global_config, name='evoformer'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def _relative_encoding(self, batch): + """Add relative position encodings. + + For position (i, j), the value is (i-j) clipped to [-k, k] and one-hotted. + + When not using 'use_chain_relative' the residue indices are used as is, e.g. + for heteromers relative positions will be computed using the positions in + the corresponding chains. + + When using 'use_chain_relative' we add an extra bin that denotes + 'different chain'. Furthermore we also provide the relative chain index + (i.e. sym_id) clipped and one-hotted to the network. And an extra feature + which denotes whether they belong to the same chain type, i.e. it's 0 if + they are in different heteromer chains and 1 otherwise. + + Args: + batch: batch. + Returns: + Feature embedding using the features as described before. + """ + c = self.config + rel_feats = [] + asym_id = batch['asym_id'] + asym_id_same = jnp.equal(asym_id[:, None], asym_id[None, :]) + + if "offset" in batch: + offset = batch['offset'] + else: + pos = batch['residue_index'] + offset = pos[:, None] - pos[None, :] + + clipped_offset = jnp.clip( + offset + c.max_relative_idx, a_min=0, a_max=2 * c.max_relative_idx) + + if c.use_chain_relative: + + final_offset = jnp.where(asym_id_same, clipped_offset, + (2 * c.max_relative_idx + 1) * + jnp.ones_like(clipped_offset)) + + rel_pos = jax.nn.one_hot(final_offset, 2 * c.max_relative_idx + 2) + + rel_feats.append(rel_pos) + + entity_id = batch['entity_id'] + entity_id_same = jnp.equal(entity_id[:, None], entity_id[None, :]) + rel_feats.append(entity_id_same.astype(rel_pos.dtype)[..., None]) + + sym_id = batch['sym_id'] + rel_sym_id = sym_id[:, None] - sym_id[None, :] + + max_rel_chain = c.max_relative_chain + + clipped_rel_chain = jnp.clip( + rel_sym_id + max_rel_chain, a_min=0, a_max=2 * max_rel_chain) + + final_rel_chain = jnp.where(entity_id_same, clipped_rel_chain, + (2 * max_rel_chain + 1) * + jnp.ones_like(clipped_rel_chain)) + rel_chain = jax.nn.one_hot(final_rel_chain, 2 * c.max_relative_chain + 2) + + rel_feats.append(rel_chain) + + else: + rel_pos = jax.nn.one_hot(clipped_offset, 2 * c.max_relative_idx + 1) + rel_feats.append(rel_pos) + + rel_feat = jnp.concatenate(rel_feats, axis=-1) + + return common_modules.Linear( + c.pair_channel, + name='position_activations')( + rel_feat) + + def __call__(self, batch, is_training, safe_key=None): + + c = self.config + gc = self.global_config + + batch = dict(batch) + + if safe_key is None: + safe_key = prng.SafeKey(hk.next_rng_key()) + + output = {} + + target_feat = batch['msa_feat'][0,:,:21] + msa_feat = batch['msa_feat'] + preprocess_1d = common_modules.Linear(c.msa_channel, name='preprocess_1d')(target_feat) + preprocess_msa = common_modules.Linear(c.msa_channel, name='preprocess_msa')(msa_feat) + msa_activations = preprocess_1d[None] + preprocess_msa + + left_single = common_modules.Linear(c.pair_channel, name='left_single')(target_feat) + right_single = common_modules.Linear(c.pair_channel, name='right_single')(target_feat) + pair_activations = left_single[:, None] + right_single[None] + mask_2d = batch['seq_mask'][:, None] * batch['seq_mask'][None, :] + mask_2d = mask_2d.astype(jnp.float32) + + if c.recycle_pos: + prev_pseudo_beta = modules.pseudo_beta_fn( + batch['aatype'], batch['prev_pos'], None) + + dgram = modules.dgram_from_positions( + prev_pseudo_beta, **self.config.prev_pos) + pair_activations += common_modules.Linear( + c.pair_channel, name='prev_pos_linear')( + dgram) + + if c.recycle_features: + prev_msa_first_row = hk.LayerNorm( + axis=[-1], + create_scale=True, + create_offset=True, + name='prev_msa_first_row_norm')( + batch['prev_msa_first_row']) + msa_activations = msa_activations.at[0].add(prev_msa_first_row) + + pair_activations += hk.LayerNorm( + axis=[-1], + create_scale=True, + create_offset=True, + name='prev_pair_norm')( + batch['prev_pair']) + + if c.max_relative_idx: + pair_activations += self._relative_encoding(batch) + + if c.template.enabled: + template_module = TemplateEmbedding(c.template, gc) + template_batch = { + 'template_aatype': batch['template_aatype'], + 'template_all_atom_positions': batch['template_all_atom_positions'], + 'template_all_atom_mask': batch['template_all_atom_mask'] + } + # Construct a mask such that only intra-chain template features are + # computed, since all templates are for each chain individually. + multichain_mask = batch['asym_id'][:, None] == batch['asym_id'][None, :] + multichain_mask = jnp.where(batch["mask_template_interchain"], multichain_mask, 1) + + safe_key, safe_subkey = safe_key.split() + template_act = template_module( + query_embedding=pair_activations, + template_batch=template_batch, + padding_mask_2d=mask_2d, + multichain_mask_2d=multichain_mask, + is_training=is_training, + dropout_scale=batch["dropout_scale"], + safe_key=safe_subkey) + pair_activations += template_act + + # Extra MSA stack. + (extra_msa_feat, + extra_msa_mask) = create_extra_msa_feature(batch, c.num_extra_msa) + extra_msa_activations = common_modules.Linear( + c.extra_msa_channel, + name='extra_msa_activations')( + extra_msa_feat) + extra_msa_mask = extra_msa_mask.astype(jnp.float32) + + extra_evoformer_input = { + 'msa': extra_msa_activations, + 'pair': pair_activations, + } + extra_masks = {'msa': extra_msa_mask, 'pair': mask_2d} + + extra_evoformer_iteration = modules.EvoformerIteration( + c.evoformer, gc, is_extra_msa=True, name='extra_msa_stack') + + def extra_evoformer_fn(x): + act, safe_key = x + safe_key, safe_subkey = safe_key.split() + extra_evoformer_output = extra_evoformer_iteration( + activations=act, + masks=extra_masks, + is_training=is_training, + dropout_scale=batch["dropout_scale"], + safe_key=safe_subkey) + return (extra_evoformer_output, safe_key) + + if gc.use_remat: + extra_evoformer_fn = hk.remat(extra_evoformer_fn) + + safe_key, safe_subkey = safe_key.split() + extra_evoformer_stack = layer_stack.layer_stack( + c.extra_msa_stack_num_block)( + extra_evoformer_fn) + extra_evoformer_output, safe_key = extra_evoformer_stack( + (extra_evoformer_input, safe_subkey)) + + pair_activations = extra_evoformer_output['pair'] + + # Get the size of the MSA before potentially adding templates, so we + # can crop out the templates later. + num_msa_sequences = msa_activations.shape[0] + evoformer_input = { + 'msa': msa_activations, + 'pair': pair_activations, + } + evoformer_masks = {'msa': batch['msa_mask'].astype(jnp.float32), + 'pair': mask_2d} + + if c.template.enabled: + template_features, template_masks = ( + template_embedding_1d(batch=batch, num_channel=c.msa_channel)) + + evoformer_input['msa'] = jnp.concatenate( + [evoformer_input['msa'], template_features], axis=0) + evoformer_masks['msa'] = jnp.concatenate( + [evoformer_masks['msa'], template_masks], axis=0) + + evoformer_iteration = modules.EvoformerIteration( + c.evoformer, gc, is_extra_msa=False, name='evoformer_iteration') + + def evoformer_fn(x): + act, safe_key = x + safe_key, safe_subkey = safe_key.split() + evoformer_output = evoformer_iteration( + activations=act, + masks=evoformer_masks, + is_training=is_training, + dropout_scale=batch["dropout_scale"], + safe_key=safe_subkey) + return (evoformer_output, safe_key) + + if gc.use_remat: + evoformer_fn = hk.remat(evoformer_fn) + + safe_key, safe_subkey = safe_key.split() + evoformer_stack = layer_stack.layer_stack(c.evoformer_num_block)( + evoformer_fn) + + def run_evoformer(evoformer_input): + evoformer_output, _ = evoformer_stack((evoformer_input, safe_subkey)) + return evoformer_output + + evoformer_output = run_evoformer(evoformer_input) + + msa_activations = evoformer_output['msa'] + pair_activations = evoformer_output['pair'] + + single_activations = common_modules.Linear( + c.seq_channel, name='single_activations')( + msa_activations[0]) + + output.update({ + 'single': + single_activations, + 'pair': + pair_activations, + # Crop away template rows such that they are not used in MaskedMsaHead. + 'msa': + msa_activations[:num_msa_sequences, :, :], + 'msa_first_row': + msa_activations[0], + }) + + return output + + +class TemplateEmbedding(hk.Module): + """Embed a set of templates.""" + + def __init__(self, config, global_config, name='template_embedding'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__(self, query_embedding, template_batch, padding_mask_2d, + multichain_mask_2d, is_training, dropout_scale, + safe_key=None): + """Generate an embedding for a set of templates. + + Args: + query_embedding: [num_res, num_res, num_channel] a query tensor that will + be used to attend over the templates to remove the num_templates + dimension. + template_batch: A dictionary containing: + `template_aatype`: [num_templates, num_res] aatype for each template. + `template_all_atom_positions`: [num_templates, num_res, 37, 3] atom + positions for all templates. + `template_all_atom_mask`: [num_templates, num_res, 37] mask for each + template. + padding_mask_2d: [num_res, num_res] Pair mask for attention operations. + multichain_mask_2d: [num_res, num_res] Mask indicating which residue pairs + are intra-chain, used to mask out residue distance based features + between chains. + is_training: bool indicating where we are running in training mode. + safe_key: random key generator. + + Returns: + An embedding of size [num_res, num_res, num_channels] + """ + c = self.config + if safe_key is None: + safe_key = prng.SafeKey(hk.next_rng_key()) + + num_templates = template_batch['template_aatype'].shape[0] + num_res, _, query_num_channels = query_embedding.shape + + # Embed each template separately. + template_embedder = SingleTemplateEmbedding(self.config, self.global_config) + def partial_template_embedder(template_aatype, + template_all_atom_positions, + template_all_atom_mask, + unsafe_key): + safe_key = prng.SafeKey(unsafe_key) + return template_embedder(query_embedding, + template_aatype, + template_all_atom_positions, + template_all_atom_mask, + padding_mask_2d, + multichain_mask_2d, + is_training, + dropout_scale, + safe_key) + + safe_key, unsafe_key = safe_key.split() + unsafe_keys = jax.random.split(unsafe_key._key, num_templates) + + def scan_fn(carry, x): + return carry + partial_template_embedder(*x), None + + scan_init = jnp.zeros((num_res, num_res, c.num_channels), + dtype=query_embedding.dtype) + summed_template_embeddings, _ = hk.scan( + scan_fn, scan_init, + (template_batch['template_aatype'], + template_batch['template_all_atom_positions'], + template_batch['template_all_atom_mask'], unsafe_keys)) + + embedding = summed_template_embeddings / num_templates + embedding = jax.nn.relu(embedding) + embedding = common_modules.Linear( + query_num_channels, + initializer='relu', + name='output_linear')(embedding) + + return embedding + + +class SingleTemplateEmbedding(hk.Module): + """Embed a single template.""" + + def __init__(self, config, global_config, name='single_template_embedding'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__(self, query_embedding, template_aatype, + template_all_atom_positions, template_all_atom_mask, + padding_mask_2d, multichain_mask_2d, is_training, dropout_scale, + safe_key): + """Build the single template embedding graph. + + Args: + query_embedding: (num_res, num_res, num_channels) - embedding of the + query sequence/msa. + template_aatype: [num_res] aatype for each template. + template_all_atom_positions: [num_res, 37, 3] atom positions for all + templates. + template_all_atom_mask: [num_res, 37] mask for each template. + padding_mask_2d: Padding mask (Note: this doesn't care if a template + exists, unlike the template_pseudo_beta_mask). + multichain_mask_2d: A mask indicating intra-chain residue pairs, used + to mask out between chain distances/features when templates are for + single chains. + is_training: Are we in training mode. + safe_key: Random key generator. + + Returns: + A template embedding (num_res, num_res, num_channels). + """ + gc = self.global_config + c = self.config + assert padding_mask_2d.dtype == query_embedding.dtype + dtype = query_embedding.dtype + num_channels = self.config.num_channels + + def construct_input(query_embedding, template_aatype, + template_all_atom_positions, template_all_atom_mask, + multichain_mask_2d): + + # Compute distogram feature for the template. + template_positions, pseudo_beta_mask = modules.pseudo_beta_fn( + template_aatype, template_all_atom_positions, template_all_atom_mask) + pseudo_beta_mask_2d = (pseudo_beta_mask[:, None] * + pseudo_beta_mask[None, :]) + pseudo_beta_mask_2d *= multichain_mask_2d + template_dgram = modules.dgram_from_positions( + template_positions, **self.config.dgram_features) + template_dgram *= pseudo_beta_mask_2d[..., None] + template_dgram = template_dgram.astype(dtype) + pseudo_beta_mask_2d = pseudo_beta_mask_2d.astype(dtype) + to_concat = [(template_dgram, 1), (pseudo_beta_mask_2d, 0)] + + aatype = jax.nn.one_hot(template_aatype, 22, axis=-1, dtype=dtype) + to_concat.append((aatype[None, :, :], 1)) + to_concat.append((aatype[:, None, :], 1)) + + # Compute a feature representing the normalized vector between each + # backbone affine - i.e. in each residues local frame, what direction are + # each of the other residues. + raw_atom_pos = template_all_atom_positions + + atom_pos = geometry.Vec3Array.from_array(raw_atom_pos) + rigid, backbone_mask = folding_multimer.make_backbone_affine( + atom_pos, + template_all_atom_mask, + template_aatype) + points = rigid.translation + rigid_vec = rigid[:, None].inverse().apply_to_point(points) + unit_vector = rigid_vec.normalized() + unit_vector = [unit_vector.x, unit_vector.y, unit_vector.z] + + backbone_mask_2d = backbone_mask[:, None] * backbone_mask[None, :] + backbone_mask_2d *= multichain_mask_2d + unit_vector = [x*backbone_mask_2d for x in unit_vector] + + # Note that the backbone_mask takes into account C, CA and N (unlike + # pseudo beta mask which just needs CB) so we add both masks as features. + to_concat.extend([(x, 0) for x in unit_vector]) + to_concat.append((backbone_mask_2d, 0)) + + query_embedding = hk.LayerNorm( + axis=[-1], + create_scale=True, + create_offset=True, + name='query_embedding_norm')( + query_embedding) + # Allow the template embedder to see the query embedding. Note this + # contains the position relative feature, so this is how the network knows + # which residues are next to each other. + to_concat.append((query_embedding, 1)) + + act = 0 + + for i, (x, n_input_dims) in enumerate(to_concat): + + act += common_modules.Linear( + num_channels, + num_input_dims=n_input_dims, + initializer='relu', + name=f'template_pair_embedding_{i}')(x) + return act + + act = construct_input(query_embedding, template_aatype, + template_all_atom_positions, template_all_atom_mask, + multichain_mask_2d) + + template_iteration = TemplateEmbeddingIteration( + c.template_pair_stack, gc, name='template_embedding_iteration') + + def template_iteration_fn(x): + act, safe_key = x + + safe_key, safe_subkey = safe_key.split() + act = template_iteration( + act=act, + pair_mask=padding_mask_2d, + is_training=is_training, + dropout_scale=dropout_scale, + safe_key=safe_subkey) + return (act, safe_key) + + if gc.use_remat: + template_iteration_fn = hk.remat(template_iteration_fn) + + safe_key, safe_subkey = safe_key.split() + template_stack = layer_stack.layer_stack( + c.template_pair_stack.num_block)( + template_iteration_fn) + act, safe_key = template_stack((act, safe_subkey)) + + act = hk.LayerNorm( + axis=[-1], + create_scale=True, + create_offset=True, + name='output_layer_norm')( + act) + return act + + +class TemplateEmbeddingIteration(hk.Module): + """Single Iteration of Template Embedding.""" + + def __init__(self, config, global_config, + name='template_embedding_iteration'): + super().__init__(name=name) + self.config = config + self.global_config = global_config + + def __call__(self, act, pair_mask, is_training=True, dropout_scale=1.0, + safe_key=None): + """Build a single iteration of the template embedder. + + Args: + act: [num_res, num_res, num_channel] Input pairwise activations. + pair_mask: [num_res, num_res] padding mask. + is_training: Whether to run in training mode. + safe_key: Safe pseudo-random generator key. + + Returns: + [num_res, num_res, num_channel] tensor of activations. + """ + c = self.config + gc = self.global_config + + if safe_key is None: + safe_key = prng.SafeKey(hk.next_rng_key()) + + dropout_wrapper_fn = functools.partial( + modules.dropout_wrapper, + is_training=is_training, + dropout_scale=dropout_scale, + global_config=gc) + + safe_key, *sub_keys = safe_key.split(20) + sub_keys = iter(sub_keys) + + act = dropout_wrapper_fn( + modules.TriangleMultiplication(c.triangle_multiplication_outgoing, gc, + name='triangle_multiplication_outgoing'), + act, + pair_mask, + safe_key=next(sub_keys)) + + act = dropout_wrapper_fn( + modules.TriangleMultiplication(c.triangle_multiplication_incoming, gc, + name='triangle_multiplication_incoming'), + act, + pair_mask, + safe_key=next(sub_keys)) + + act = dropout_wrapper_fn( + modules.TriangleAttention(c.triangle_attention_starting_node, gc, + name='triangle_attention_starting_node'), + act, + pair_mask, + safe_key=next(sub_keys)) + + act = dropout_wrapper_fn( + modules.TriangleAttention(c.triangle_attention_ending_node, gc, + name='triangle_attention_ending_node'), + act, + pair_mask, + safe_key=next(sub_keys)) + + act = dropout_wrapper_fn( + modules.Transition(c.pair_transition, gc, + name='pair_transition'), + act, + pair_mask, + safe_key=next(sub_keys)) + + return act + + +def template_embedding_1d(batch, num_channel): + """Embed templates into an (num_res, num_templates, num_channels) embedding. + + Args: + batch: A batch containing: + template_aatype, (num_templates, num_res) aatype for the templates. + template_all_atom_positions, (num_templates, num_residues, 37, 3) atom + positions for the templates. + template_all_atom_mask, (num_templates, num_residues, 37) atom mask for + each template. + num_channel: The number of channels in the output. + + Returns: + An embedding of shape (num_templates, num_res, num_channels) and a mask of + shape (num_templates, num_res). + """ + + # Embed the templates aatypes. + aatype_one_hot = jax.nn.one_hot(batch['template_aatype'], 22, axis=-1) + + num_templates = batch['template_aatype'].shape[0] + all_chi_angles = [] + all_chi_masks = [] + for i in range(num_templates): + atom_pos = geometry.Vec3Array.from_array( + batch['template_all_atom_positions'][i, :, :, :]) + template_chi_angles, template_chi_mask = all_atom_multimer.compute_chi_angles( + atom_pos, + batch['template_all_atom_mask'][i, :, :], + batch['template_aatype'][i, :]) + all_chi_angles.append(template_chi_angles) + all_chi_masks.append(template_chi_mask) + chi_angles = jnp.stack(all_chi_angles, axis=0) + chi_mask = jnp.stack(all_chi_masks, axis=0) + + template_features = jnp.concatenate([ + aatype_one_hot, + jnp.sin(chi_angles) * chi_mask, + jnp.cos(chi_angles) * chi_mask, + chi_mask], axis=-1) + + template_mask = chi_mask[:, :, 0] + + template_activations = common_modules.Linear( + num_channel, + initializer='relu', + name='template_single_embedding')( + template_features) + template_activations = jax.nn.relu(template_activations) + template_activations = common_modules.Linear( + num_channel, + initializer='relu', + name='template_projection')( + template_activations) + return template_activations, template_mask diff --git a/colabdesign/af/alphafold/model/tf/data_transforms.py b/colabdesign/af/alphafold/model/tf/data_transforms.py index 699e4ccf..3e9c7e31 100644 --- a/colabdesign/af/alphafold/model/tf/data_transforms.py +++ b/colabdesign/af/alphafold/model/tf/data_transforms.py @@ -119,7 +119,7 @@ def squeeze_features(protein): for k in [ 'domain_name', 'msa', 'num_alignments', 'seq_length', 'sequence', 'superfamily', 'deletion_matrix', 'resolution', - 'between_segment_residues', 'residue_index', 'template_all_atom_masks']: + 'between_segment_residues', 'residue_index', 'template_all_atom_mask']: if k in protein: final_dim = shape_helpers.shape_list(protein[k])[-1] if isinstance(final_dim, int) and final_dim == 1: @@ -318,7 +318,7 @@ def make_msa_mask(protein): return protein -def pseudo_beta_fn(aatype, all_atom_positions, all_atom_masks): +def pseudo_beta_fn(aatype, all_atom_positions, all_atom_mask): """Create pseudo beta features.""" is_gly = tf.equal(aatype, residue_constants.restype_order['G']) ca_idx = residue_constants.atom_order['CA'] @@ -328,9 +328,9 @@ def pseudo_beta_fn(aatype, all_atom_positions, all_atom_masks): all_atom_positions[..., ca_idx, :], all_atom_positions[..., cb_idx, :]) - if all_atom_masks is not None: + if all_atom_mask is not None: pseudo_beta_mask = tf.where( - is_gly, all_atom_masks[..., ca_idx], all_atom_masks[..., cb_idx]) + is_gly, all_atom_mask[..., ca_idx], all_atom_mask[..., cb_idx]) pseudo_beta_mask = tf.cast(pseudo_beta_mask, tf.float32) return pseudo_beta, pseudo_beta_mask else: @@ -345,7 +345,7 @@ def make_pseudo_beta(protein, prefix=''): pseudo_beta_fn( protein['template_aatype' if prefix else 'all_atom_aatype'], protein[prefix + 'all_atom_positions'], - protein['template_all_atom_masks' if prefix else 'all_atom_mask'])) + protein['template_all_atom_mask' if prefix else 'all_atom_mask'])) return protein diff --git a/colabdesign/af/alphafold/model/tf/protein_features.py b/colabdesign/af/alphafold/model/tf/protein_features.py index eb58bd0c..6498e578 100644 --- a/colabdesign/af/alphafold/model/tf/protein_features.py +++ b/colabdesign/af/alphafold/model/tf/protein_features.py @@ -59,7 +59,7 @@ class FeatureType(enum.Enum): "template_all_atom_positions": (tf.float32, [ NUM_TEMPLATES, NUM_RES, residue_constants.atom_type_num, 3 ]), - "template_all_atom_masks": (tf.float32, [ + "template_all_atom_mask": (tf.float32, [ NUM_TEMPLATES, NUM_RES, residue_constants.atom_type_num, 1 ]), } diff --git a/colabdesign/af/alphafold/model/utils.py b/colabdesign/af/alphafold/model/utils.py index 8ed5361e..ae83ba06 100644 --- a/colabdesign/af/alphafold/model/utils.py +++ b/colabdesign/af/alphafold/model/utils.py @@ -30,10 +30,9 @@ def final_init(config): else: return 'linear' - def batched_gather(params, indices, axis=0, batch_dims=0): """Implements a JAX equivalent of `tf.gather` with `axis` and `batch_dims`.""" - take_fn = lambda p, i: jnp.take(p, i, axis=axis) + take_fn = lambda p, i: jnp.take(p, i, axis=axis, mode="clip") for _ in range(batch_dims): take_fn = jax.vmap(take_fn) return take_fn(params, indices) diff --git a/colabdesign/af/crop.py b/colabdesign/af/crop.py new file mode 100644 index 00000000..6e8050e1 --- /dev/null +++ b/colabdesign/af/crop.py @@ -0,0 +1,98 @@ +import jax +import jax.numpy as jnp +import numpy as np + +from colabdesign.shared.utils import copy_dict +from colabdesign.af.alphafold.model import config + +class _af_crop: + def _crop(self): + ''' determine positions to crop ''' + (L, max_L, mode) = (sum(self._lengths), self._args["crop_len"], self._args["crop_mode"]) + + if max_L is None or max_L >= L: crop = False + elif self._args["copies"] > 1 and not self._args["repeat"]: crop = False + elif self.protocol in ["partial","binder"]: crop = False + elif mode == "dist" and not hasattr(self,"_dist"): crop = False + else: crop = True + + if crop: + if self.protocol == "fixbb": + self._tmp["cmap"] = self._dist < self.opt["cmap_cutoff"] + + if mode == "slide": + i = jax.random.randint(self.key(),[],0,(L-max_L)+1) + p = np.arange(i,i+max_L) + + if mode == "roll": + i = jax.random.randint(self.key(),[],0,L) + p = np.sort(np.roll(np.arange(L),L-i)[:max_L]) + + if mode == "dist": + i = jax.random.randint(self.key(),[],0,(L-max_L)+1) + p = np.sort(self._dist[i].argsort()[1:][:max_L]) + + if mode == "pair": + # pick random pair of interactig crops + max_L = max_L // 2 + + # pick first crop + i_range = np.append(np.arange(0,(L-2*max_L)+1),np.arange(max_L,(L-max_L)+1)) + i = jax.random.choice(self.key(),i_range,[]) + + # pick second crop + j_range = np.append(np.arange(0,(i-max_L)+1),np.arange(i+max_L,(L-max_L)+1)) + if "cmap" in self._tmp: + # if contact map defined, bias to interacting pairs + w = np.array([self._tmp["cmap"][i:i+max_L,j:j+max_L].sum() for j in j_range]) + 1e-8 + j = jax.random.choice(self.key(), j_range, [], p=w/w.sum()) + else: + j = jax.random.choice(self.key(), j_range, []) + + p = np.sort(np.append(np.arange(i,i+max_L),np.arange(j,j+max_L))) + + def callback(self): + # function to apply after run + cmap, pae = (np.array(self.aux[k]) for k in ["cmap","pae"]) + mask = np.isnan(pae) + + b = 0.9 + _pae = self._tmp.get("pae",np.full_like(pae, 31.0)) + self._tmp["pae"] = np.where(mask, _pae, (1-b)*pae + b*_pae) + + if self.protocol == "hallucination": + _cmap = self._tmp.get("cmap",np.zeros_like(cmap)) + self._tmp["cmap"] = np.where(mask, _cmap, (1-b)*cmap + b*_cmap) + + self.aux.update(self._tmp) + + else: + callback = None + p = np.arange(sum(self._lengths)) + + self.opt["crop_pos"] = p + return callback + +def crop_feat(feat, pos): + ''' + crop features to specified [pos]itions + ''' + if feat is None: return None + + def find(x,k): + i = [] + for j,y in enumerate(x): + if y == k: i.append(j) + return i + + shapes = config.CONFIG.data.eval.feat + NUM_RES = "num residues placeholder" + idx = {k:find(v,NUM_RES) for k,v in shapes.items()} + new_feat = copy_dict(feat) + for k in new_feat.keys(): + if k == "batch": + new_feat[k] = crop_feat(feat[k], pos) + if k in idx: + for i in idx[k]: new_feat[k] = jnp.take(new_feat[k], pos, i) + + return new_feat \ No newline at end of file diff --git a/colabdesign/af/design.py b/colabdesign/af/design.py index c23f2819..c1cd76b3 100644 --- a/colabdesign/af/design.py +++ b/colabdesign/af/design.py @@ -67,21 +67,15 @@ def restart(self, seed=None, optimizer="sgd", opt=None, weights=None, self._traj = {"log":[],"seq":[],"xyz":[],"plddt":[],"pae":[]} self._best, self._tmp = {}, {} - def run(self, backprop=True, callback=None): + def run(self, num_recycles=None, backprop=True, callback=None): '''run model to get outputs, losses and gradients''' - callbacks = [self._crop(), callback] + callbacks = [callback] + if self._args["use_crop"]: callbacks.append(self._crop()) # decide which model params to use - ns,ns_name = [],[] - count = {"openfold":0,"alphafold":0} - for n,name in enumerate(self._model_names): - if "openfold" in name: - if self._args["use_openfold"]: ns.append(n); ns_name.append(name); count["openfold"] += 1 - else: - if self._args["use_alphafold"]: ns.append(n); ns_name.append(name); count["alphafold"] += 1 - for k in count: - if self._args[f"use_{k}"] and count[k] == 0: print(f"ERROR: {k} params not found") + ns_name = self._model_names.copy() + ns = list(range(len(ns_name))) # sub select number of model params if self._args["models"] is not None: @@ -101,7 +95,7 @@ def run(self, backprop=True, callback=None): aux = [] for n in model_num: p = self._model_params[n] - aux.append(self._recycle(p, backprop=backprop)) + aux.append(self._recycle(p, num_recycles=num_recycles, backprop=backprop)) aux = jax.tree_map(lambda *x: jnp.stack(x), *aux) # update aux @@ -118,16 +112,21 @@ def run(self, backprop=True, callback=None): if callback is not None: callback(self) # update log - self.aux["log"] = {**self.aux["losses"], "loss":self.aux["loss"], "ptm":self.aux["ptm"]} + self.aux["log"] = {**self.aux["losses"], "loss":self.aux["loss"], + "ptm":self.aux["ptm"], "i_ptm":self.aux["i_ptm"]} self.aux["log"].update({k:self.opt[k] for k in ["hard","soft","temp"]}) # compute sequence recovery if self.protocol in ["fixbb","partial"] or (self.protocol == "binder" and self._args["redesign"]): - if self.protocol == "partial" and "pos" in self.opt: + if self.protocol == "partial": aatype = self.aux["aatype"].argmax(-1)[...,self.opt["pos"]] else: aatype = self.aux["seq"]["pseudo"].argmax(-1) - self.aux["log"]["seqid"] = (aatype == self._wt_aatype).mean() + + mask = self._wt_aatype != -1 + true = self._wt_aatype[mask] + pred = aatype[...,mask] + self.aux["log"]["seqid"] = (true == pred).mean() self.aux["log"] = to_float(self.aux["log"]) self.aux["log"].update({"recycles":int(self.aux["num_recycles"]), @@ -144,66 +143,72 @@ def _single(self, model_params, backprop=True): aux.update({"loss":loss,"grad":grad}) return aux - def _recycle(self, model_params, backprop=True): + def _recycle(self, model_params, num_recycles=None, backprop=True): '''multiple passes through the model (aka recycle)''' - mode = self._args["recycle_mode"] + if num_recycles is None: + num_recycles = self.opt["num_recycles"] + if mode in ["backprop","add_prev"]: - # recycles compiled into model, only need single-pass - num_recycles = self.opt["num_recycles"] = self._cfg.model.num_recycle aux = self._single(model_params, backprop) else: - - # configure number of recycle to run - num_recycles = self.opt["num_recycles"] - if mode == "average": - # run recycles manually, average gradients - if "crop_pos" in self.opt: L = self.opt["crop_pos"].shape[0] - else: L = self._inputs["residue_index"].shape[-1] - self._inputs["prev"] = {'prev_msa_first_row': np.zeros([L,256]), - 'prev_pair': np.zeros([L,L,128]), - 'prev_pos': np.zeros([L,37,3])} - grad = [] - for _ in range(num_recycles+1): - aux = self._single(model_params, backprop) - grad.append(aux["grad"]) - self._inputs["prev"] = aux["prev"] - # average gradients across - aux["grad"] = jax.tree_map(lambda *x: jnp.stack(x).mean(0), *grad) + L = self._inputs["residue_index"].shape[0] + if self._args["use_crop"]: L = self.opt["crop_pos"].shape[0] - elif mode == "sample": - # randomly select number of recycles to run - self.set_opt(num_recycles=jax.random.randint(self.key(),[],0,num_recycles+1)) - aux = self._single(model_params, backprop) - (self.opt["num_recycles"],num_recycles) = (num_recycles,self.opt["num_recycles"]) + # intialize previous + self._inputs["prev"] = {'prev_msa_first_row': np.zeros([L,256]), + 'prev_pair': np.zeros([L,L,128]), + 'prev_pos': np.zeros([L,37,3])} + + # decide which layers to compute gradients for + cycles = (num_recycles + 1) + mask = [0] * cycles + if mode == "sample": mask[jax.random.randint(self.key(),[],0,cycles)] = 1 + if mode == "average": mask = [1/cycles] * cycles + if mode == "last": mask[-1] = 1 + if mode == "first": mask[0] = 1 - else: - aux = self._single(model_params, backprop) + # gather gradients across recycles + grad = [] + for m in mask: + if m == 0: + aux = self._single(model_params, backprop=False) + else: + aux = self._single(model_params, backprop) + grad.append(jax.tree_map(lambda x:x*m, aux["grad"])) + self._inputs["prev"] = aux["prev"] + + aux["grad"] = jax.tree_map(lambda *x: jnp.stack(x).sum(0), *grad) aux["num_recycles"] = num_recycles return aux - def step(self, lr_scale=1.0, backprop=True, repredict=False, - callback=None, save_best=False, verbose=1): + def step(self, lr_scale=1.0, num_recycles=None, backprop=True, + callback=None, stats_correct=False, save_best=False, verbose=1): '''do one step of gradient descent''' # run - self.run(backprop=backprop, callback=callback) + self.run(num_recycles=num_recycles, backprop=backprop, callback=callback) - # normalize gradient + # apply gradient g = self.aux["grad"]["seq"] - gn = jnp.linalg.norm(g,axis=(-1,-2),keepdims=True) - eff_len = (jnp.square(g).sum(-1,keepdims=True) > 0).sum(-2,keepdims=True) - self.aux["grad"]["seq"] *= jnp.sqrt(eff_len)/(gn+1e-7) + + # statistical correction - doi:10.1101/2022.04.29.490102 + if stats_correct: + g = g - g.sum(-2,keepdims=True) / eff_len + + # normalize gradient + gn = jnp.linalg.norm(g,axis=(-1,-2),keepdims=True) + self.aux["grad"]["seq"] = g * jnp.sqrt(eff_len)/(gn+1e-7) # set learning rate lr = self.opt["lr"] * lr_scale self.aux["grad"] = jax.tree_map(lambda x:x*lr, self.aux["grad"]) - # apply gradient + # update state/params self._state = self._update_fun(self._k, self.aux["grad"], self._state) self._params = self._get_params(self._state) @@ -211,7 +216,6 @@ def step(self, lr_scale=1.0, backprop=True, repredict=False, self._k += 1 # save results - if repredict: self.predict(models=None, verbose=False) self._save_results(save_best=save_best, verbose=verbose) def _update_traj(self): @@ -225,8 +229,10 @@ def _update_traj(self): def _print_log(self, print_str=None): keys = ["models","recycles","hard","soft","temp","seqid","loss", - "msa_ent","plddt","pae","helix","con","i_pae","i_con", - "sc_fape","sc_rmsd","dgram_cce","fape","ptm","rmsd"] + "seq_ent","mlm","plddt","pae","exp_res","con","i_con", + "sc_fape","sc_rmsd","dgram_cce","fape","ptm"] + if sum(self._lengths) > 1: keys.append("i_ptm") + keys.append("rmsd") print(dict_to_str(self.aux["log"], filt=self.opt["weights"], print_str=print_str, keys=keys, ok="rmsd")) @@ -235,92 +241,29 @@ def _save_best(self): if "metric" not in self._best or metric < self._best["metric"]: self._best.update({"metric":metric, "aux":self.aux}) - def clear_best(self): self._best = {} - def _save_results(self, save_best=False, verbose=True): self._update_traj() if save_best: self._save_best() if verbose and (self._k % verbose) == 0: self._print_log(f"{self._k}") - def _crop(self): - ''' determine positions to crop ''' - (L, max_L, mode) = (sum(self._lengths), self._args["crop_len"], self._args["crop_mode"]) - - if max_L is None or max_L >= L: crop = False - elif self._args["copies"] > 1 and not self._args["repeat"]: crop = False - elif self.protocol in ["partial","binder"]: crop = False - elif mode == "dist" and not hasattr(self,"_dist"): crop = False - else: crop = True + def predict(self, seq=None, num_recycles=None, num_models=None, models=None, verbose=True): + '''predict structure for input sequence (if provided)''' - if crop: - if self.protocol == "fixbb": - self._tmp["cmap"] = self._dist < self.opt["cmap_cutoff"] - - if mode == "slide": - i = jax.random.randint(self.key(),[],0,(L-max_L)+1) - p = np.arange(i,i+max_L) - - if mode == "roll": - i = jax.random.randint(self.key(),[],0,L) - p = np.sort(np.roll(np.arange(L),L-i)[:max_L]) - - if mode == "dist": - i = jax.random.randint(self.key(),[],0,(L-max_L)+1) - p = np.sort(self._dist[i].argsort()[1:][:max_L]) - - if mode == "pair": - # pick random pair of interactig crops - max_L = max_L // 2 - - # pick first crop - i_range = np.append(np.arange(0,(L-2*max_L)+1),np.arange(max_L,(L-max_L)+1)) - i = jax.random.choice(self.key(),i_range,[]) - - # pick second crop - j_range = np.append(np.arange(0,(i-max_L)+1),np.arange(i+max_L,(L-max_L)+1)) - if "cmap" in self._tmp: - # if contact map defined, bias to interacting pairs - w = np.array([self._tmp["cmap"][i:i+max_L,j:j+max_L].sum() for j in j_range]) + 1e-8 - j = jax.random.choice(self.key(), j_range, [], p=w/w.sum()) - else: - j = jax.random.choice(self.key(), j_range, []) - - p = np.sort(np.append(np.arange(i,i+max_L),np.arange(j,j+max_L))) - - def callback(self): - # function to apply after run - cmap, pae = (np.array(self.aux[k]) for k in ["cmap","pae"]) - mask = np.isnan(pae) - - b = 0.9 - _pae = self._tmp.get("pae",np.full_like(pae, 31.0)) - self._tmp["pae"] = np.where(mask, _pae, (1-b)*pae + b*_pae) - - if self.protocol == "hallucination": - _cmap = self._tmp.get("cmap",np.zeros_like(cmap)) - self._tmp["cmap"] = np.where(mask, _cmap, (1-b)*cmap + b*_cmap) - - self.aux.update(self._tmp) - - else: - callback = None - p = np.arange(sum(self._lengths)) - - self.opt["crop_pos"] = p - return callback - - def predict(self, seq=None, models=None, verbose=True): # save settings (opt, args, params) = (copy_dict(x) for x in [self.opt, self._args, self._params]) # set settings if seq is not None: self.set_seq(seq=seq, set_state=False) - if models is not None: self.set_opt(num_models=len(models) if isinstance(models,list) else 1) - self.set_opt(hard=True, dropout=False, crop=False, sample_models=False, models=models) + if models is None: + models = self._model_names if num_models is None else self._model_names[:num_models] + num_models = len(models) if isinstance(models,list) else 1 + self.set_opt(hard=True, dropout=False, sample_models=False, + models=models, num_models=num_models, + mlm_dropout=0.0, use_crop=False) # run - self.run(backprop=False) + self.run(num_recycles=num_recycles, backprop=False) if verbose: self._print_log("predict") # reset settings @@ -335,11 +278,12 @@ def design(self, iters=100, hard=0.0, e_hard=None, step=1.0, e_step=None, dropout=True, opt=None, weights=None, - repredict=False, backprop=True, callback=None, + mlm_dropout=0.05, num_recycles=None, + backprop=True, callback=None, save_best=False, verbose=1): # update options/settings (if defined) - self.set_opt(opt, dropout=dropout) + self.set_opt(opt, dropout=dropout, mlm_dropout=mlm_dropout) self.set_weights(weights) m = {"soft":[soft,e_soft],"temp":[temp,e_temp], @@ -358,7 +302,7 @@ def design(self, iters=100, # decay learning rate based on temperature lr_scale = step * ((1 - self.opt["soft"]) + (self.opt["soft"] * self.opt["temp"])) - self.step(lr_scale=lr_scale, backprop=backprop, repredict=repredict, + self.step(lr_scale=lr_scale, num_recycles=num_recycles, backprop=backprop, callback=callback, save_best=save_best, verbose=verbose) def design_logits(self, iters=100, **kwargs): @@ -376,34 +320,41 @@ def design_hard(self, iters=100, **kwargs): # --------------------------------------------------------------------------------- # experimental # --------------------------------------------------------------------------------- - - def design_2stage(self, soft_iters=100, temp_iters=100, hard_iters=10, - num_models=1, **kwargs): - '''two stage design (soft→hard)''' - self.set_opt(num_models=num_models, sample_models=True) # sample models - self.design_soft(soft_iters, **kwargs) - self.design_soft(temp_iters, e_temp=1e-2, **kwargs) - self.set_opt(num_models=len(self._model_params)) # use all models - self.design_hard(hard_iters, temp=1e-2, dropout=False, save_best=True, **kwargs) - def design_3stage(self, soft_iters=300, temp_iters=100, hard_iters=10, - num_models=1, **kwargs): + ramp_recycles=True, num_recycles=None, num_models=1, **kwargs): '''three stage design (logits→soft→hard)''' - self.set_opt(num_models=num_models, sample_models=True) # sample models - self.design_logits(soft_iters, e_soft=1, **kwargs) + + # set starting options + kwargs["num_recycles"] = num_recycles + self.set_opt(num_models=num_models, sample_models=True) + + # logits -> softmax(logits/1.0) + if ramp_recycles and self._args["recycle_mode"] not in ["add_prev","backprop"]: + R = self.opt["num_recycles"] if num_recycles is None else num_recycles + p = 1.0 / (R + 1) + iters = soft_iters // (R + 1) + for r in range(R + 1): + kwargs["num_recycles"] = r + self.design_logits(iters, soft=r*p, e_soft=(r+1)*p, **kwargs) + else: + self.design_logits(soft_iters, e_soft=1, **kwargs) + + # softmax(logits/1.0) -> softmax(logits/0.01) self.design_soft(temp_iters, e_temp=1e-2, **kwargs) + self.set_opt(num_models=len(self._model_params)) # use all models - self.design_hard(hard_iters, temp=1e-2, dropout=False, save_best=True, **kwargs) + self.design_hard(hard_iters, temp=1e-2, dropout=False, mlm_dropout=0.0, save_best=True, + num_recycles=kwargs["num_recycles"], verbose=kwargs.get("verbose",1)) - def design_semigreedy(self, iters=100, tries=20, num_models=1, + def design_semigreedy(self, iters=100, tries=20, num_recycles=None, num_models=1, use_plddt=True, save_best=True, verbose=1): '''semigreedy search''' - self.set_opt(hard=True, dropout=False, crop=False, + self.set_opt(hard=True, dropout=False, use_crop=False, num_models=num_models, sample_models=False) if self._k == 0: - self.run(backprop=False) + self.run(num_recycles=num_recycles, backprop=False) def mut(seq, plddt=None): '''mutate random position''' @@ -438,7 +389,7 @@ def get_seq(): buff = [] for _ in range(tries): self.set_seq(seq=mut(seq, plddt), set_state=False) - self.run(backprop=False) + self.run(num_recycles=num_recycles, backprop=False) buff.append({"aux":self.aux, "seq":self._params["seq"]}) # accept best diff --git a/colabdesign/af/inputs.py b/colabdesign/af/inputs.py index 0292991e..39a07096 100644 --- a/colabdesign/af/inputs.py +++ b/colabdesign/af/inputs.py @@ -5,7 +5,7 @@ from colabdesign.shared.utils import copy_dict from colabdesign.shared.model import soft_seq from colabdesign.af.alphafold.common import residue_constants -from colabdesign.af.alphafold.model import model +from colabdesign.af.alphafold.model import model, config ############################################################################ # AF_INPUTS - functions for modifying inputs before passing to alphafold @@ -14,14 +14,16 @@ class _af_inputs: def _get_seq(self, inputs, params, opt, aux, key): '''get sequence features''' seq = soft_seq(params["seq"], opt, key) - if "pos" in opt and "fix_seq" in opt: - seq_ref = jax.nn.one_hot(self._wt_aatype,20) - p = opt["pos"] - if self.protocol == "partial": - fix_seq = lambda x:jnp.where(opt["fix_seq"],x.at[...,p,:].set(seq_ref),x) + if "fix_pos" in opt: + if "pos" in self.opt: + seq_ref = jax.nn.one_hot(self._wt_aatype_sub,20) + p = opt["pos"][opt["fix_pos"]] + fix_seq = lambda x:x.at[...,p,:].set(seq_ref) else: - fix_seq = lambda x:jnp.where(opt["fix_seq"],x.at[...,p,:].set(seq_ref[...,p,:]),x) - seq = jax.tree_map(fix_seq,seq) + seq_ref = jax.nn.one_hot(self._wt_aatype,20) + p = opt["fix_pos"] + fix_seq = lambda x:x.at[...,p,:].set(seq_ref[...,p,:]) + seq = jax.tree_map(fix_seq, seq) aux.update({"seq":seq, "seq_pseudo":seq["pseudo"]}) # protocol specific modifications to seq features @@ -38,66 +40,68 @@ def _get_seq(self, inputs, params, opt, aux, key): def _update_template(self, inputs, opt, key): ''''dynamically update template features''' + + o = opt["template"] - # aatype = is used to define template's CB coordinates (CA in case of glycine) - # template_aatype = is used as template's sequence + # enable templates + inputs["template_mask"] = inputs["template_mask"].at[:].set(1) batch = inputs["batch"] - if self.protocol in ["partial","fixbb","binder"]: - L = batch["aatype"].shape[0] - if self.protocol in ["partial","fixbb"]: - rt = opt["rm_template_seq"] - aatype = jnp.where(rt,0,batch["aatype"]) - template_aatype = jnp.where(rt,opt["template"]["aatype"],batch["aatype"]) + if self.protocol in ["partial","fixbb","binder"]: + + L = batch["aatype"].shape[0] + # decide which position to remove sequence and/or sidechains + rm = jnp.logical_or(o["rm_seq"],o["rm_sc"]) + rm_seq = jnp.full(L,o["rm_seq"]) + rm_sc = jnp.full(L,rm) if self.protocol == "binder": - if self._args["redesign"]: - rt = opt["rm_template_seq"] - aatype = jnp.where(rt,batch["aatype"].at[self._target_len:].set(0),batch["aatype"]) - template_aatype = jnp.where(rt,batch["aatype"].at[self._target_len:].set(opt["template"]["aatype"]),batch["aatype"]) - else: - aatype = template_aatype = batch["aatype"] - + rm_seq = rm_seq.at[:self._target_len].set(False) + rm_sc = rm_sc.at[:self._target_len].set(False) + + # aatype = is used to define template's CB coordinates (CA in case of glycine) + # template_aatype = is used as template's sequence + aatype = jnp.where(rm_seq,0,batch["aatype"]) + template_aatype = jnp.where(rm_seq,21,batch["aatype"]) + # get pseudo-carbon-beta coordinates (carbon-alpha for glycine) - pb, pb_mask = model.modules.pseudo_beta_fn(aatype, - batch["all_atom_positions"], - batch["all_atom_mask"]) + cb, cb_mask = model.modules.pseudo_beta_fn(aatype, batch["all_atom_positions"], batch["all_atom_mask"]) # define template features template_feats = {"template_aatype": template_aatype, "template_all_atom_positions": batch["all_atom_positions"], - "template_all_atom_masks": batch["all_atom_mask"], - "template_pseudo_beta": pb, - "template_pseudo_beta_mask": pb_mask} + "template_all_atom_mask": batch["all_atom_mask"], + "template_pseudo_beta": cb, + "template_pseudo_beta_mask": cb_mask} - # protocol specific template injection - for k,v in template_feats.items(): - if self.protocol == "binder": - n = self._target_len - inputs[k] = inputs[k].at[:,0,:n].set(v[:n]) - inputs[k] = inputs[k].at[:,-1,n:].set(v[n:]) - - if self.protocol == "fixbb": - inputs[k] = inputs[k].at[:,0].set(v) + # inject template features + if self.protocol == "partial": + pos = opt["pos"] + if self._args["repeat"] or self._args["homooligomer"]: + C,L = self._args["copies"], self._len + pos = (jnp.repeat(pos,C).reshape(-1,C) + jnp.arange(C) * L).T.flatten() + for k,v in template_feats.items(): if self.protocol == "partial": - inputs[k] = inputs[k].at[:,0,opt["pos"]].set(v) + inputs[k] = inputs[k].at[0,pos].set(v) + else: + inputs[k] = inputs[k].at[0].set(v) - if k == "template_all_atom_masks": - rt = jnp.logical_or(opt["rm_template_seq"],opt["rm_template_sc"]) - if self.protocol == "binder": - inputs[k] = jnp.where(rt,inputs[k].at[:,-1,n:,5:].set(0),inputs[k]) + # remove sidechains (mask anything beyond CB) + if k == "template_all_atom_mask": + if self.protocol == "partial": + inputs[k] = inputs[k].at[:,pos,5:].set(jnp.where(rm_sc[:,None],0,inputs[k][:,pos,5:])) else: - inputs[k] = jnp.where(rt,inputs[k].at[:,0,:,5:].set(0),inputs[k]) + inputs[k] = inputs[k].at[:,:,5:].set(jnp.where(rm_sc[:,None],0,inputs[k][:,:,5:])) # dropout template input features - L = inputs["template_aatype"].shape[2] + L = inputs["template_aatype"].shape[1] n = self._target_len if self.protocol == "binder" else 0 - pos_mask = jax.random.bernoulli(key, 1-opt["template"]["dropout"],(L,)) - inputs["template_all_atom_masks"] = inputs["template_all_atom_masks"].at[:,:,n:].multiply(pos_mask[n:,None]) - inputs["template_pseudo_beta_mask"] = inputs["template_pseudo_beta_mask"].at[:,:,n:].multiply(pos_mask[n:]) + pos_mask = jax.random.bernoulli(key, 1-o["dropout"],(L,)) + inputs["template_all_atom_mask"] = inputs["template_all_atom_mask"].at[:,n:].multiply(pos_mask[n:,None]) + inputs["template_pseudo_beta_mask"] = inputs["template_pseudo_beta_mask"].at[:,n:].multiply(pos_mask[n:]) -def update_seq(seq, inputs, seq_1hot=None, seq_pssm=None, msa_input=None): +def update_seq(seq, inputs, seq_1hot=None, seq_pssm=None, mlm=None): '''update the sequence features''' if seq_1hot is None: seq_1hot = seq @@ -107,12 +111,13 @@ def update_seq(seq, inputs, seq_1hot=None, seq_pssm=None, msa_input=None): seq_pssm = jnp.pad(seq_pssm,[[0,0],[0,0],[0,22-seq_pssm.shape[-1]]]) msa_feat = jnp.zeros_like(inputs["msa_feat"]).at[...,0:22].set(seq_1hot).at[...,25:47].set(seq_pssm) - if seq.ndim == 3: - target_feat = jnp.zeros_like(inputs["target_feat"]).at[...,1:21].set(seq[0,...,:20]) - else: - target_feat = jnp.zeros_like(inputs["target_feat"]).at[...,1:21].set(seq[...,:20]) + + if mlm is not None: + X = jax.nn.one_hot(22,23) + X = jnp.zeros(msa_feat.shape[-1]).at[...,:23].set(X).at[...,25:48].set(X) + msa_feat = jnp.where(mlm[None,:,None],X,msa_feat) - inputs.update({"target_feat":target_feat,"msa_feat":msa_feat}) + inputs.update({"msa_feat":msa_feat}) def update_aatype(aatype, inputs): if jnp.issubdtype(aatype.dtype, jnp.integer): @@ -147,28 +152,4 @@ def expand_copies(x, copies, block_diag=True): y = (seq + gap_seq).swapaxes(0,1).reshape(-1,L,22) return jnp.concatenate([x[:1],y],0) else: - return x - -def crop_feat(feat, pos, cfg, add_batch=True): - ''' - crop features to specified [pos]itions - ''' - if feat is None: return None - - def find(x,k): - i = [] - for j,y in enumerate(x): - if y == k: i.append(j) - return i - - shapes = cfg.data.eval.feat - NUM_RES = "num residues placeholder" - idx = {k:find(v,NUM_RES) for k,v in shapes.items()} - new_feat = copy_dict(feat) - for k in new_feat.keys(): - if k == "batch": - new_feat[k] = crop_feat(feat[k], pos, cfg, add_batch=False) - if k in idx: - for i in idx[k]: new_feat[k] = jnp.take(new_feat[k], pos, i + add_batch) - - return new_feat \ No newline at end of file + return x \ No newline at end of file diff --git a/colabdesign/af/loss.py b/colabdesign/af/loss.py index 7f98e789..f95b232d 100644 --- a/colabdesign/af/loss.py +++ b/colabdesign/af/loss.py @@ -2,61 +2,73 @@ import jax.numpy as jnp import numpy as np -from colabdesign.shared.utils import Key +from colabdesign.shared.utils import Key, copy_dict from colabdesign.shared.protein import jnp_rmsd_w, _np_kabsch, _np_rmsd, _np_get_6D_loss from colabdesign.af.alphafold.model import model, folding, all_atom -from colabdesign.af.alphafold.common import confidence_jax +from colabdesign.af.alphafold.common import confidence_jax, residue_constants #################################################### # AF_LOSS - setup loss function #################################################### + class _af_loss: # protocol specific loss functions - def _loss_hallucination(self, inputs, outputs, opt, aux): - plddt_prob = jax.nn.softmax(outputs["predicted_lddt"]["logits"]) - plddt_loss = (plddt_prob * jnp.arange(plddt_prob.shape[-1])[::-1]).mean(-1) - aux["losses"]["plddt"] = plddt_loss.mean() - self._get_pairwise_loss(inputs, outputs, opt, aux) - def _loss_fixbb(self, inputs, outputs, opt, aux): '''get losses''' - plddt_prob = jax.nn.softmax(outputs["predicted_lddt"]["logits"]) - plddt_loss = (plddt_prob * jnp.arange(plddt_prob.shape[-1])[::-1]).mean(-1) - self._get_pairwise_loss(inputs, outputs, opt, aux) - - copies = self._args["copies"] - if self._args["repeat"] or not self._args["homooligomer"]: copies = 1 - + copies = self._args["copies"] if self._args["homooligomer"] else 1 # rmsd loss aln = get_rmsd_loss(inputs, outputs, copies=copies) - rmsd, aux["atom_positions"] = aln["rmsd"], aln["align"](aux["atom_positions"]) - - # dgram loss - aatype = inputs["aatype"][0] - dgram_cce = get_dgram_loss(inputs, outputs, copies=copies, aatype=aatype) + aux["atom_positions"] = aln["align"](aux["atom_positions"]) - aux["losses"].update({"fape": get_fape_loss(inputs, outputs, model_config=self._cfg), - "rmsd": rmsd, "dgram_cce": dgram_cce, "plddt":plddt_loss.mean()}) + # supervised losses + aux["losses"].update({ + "fape": get_fape_loss(inputs, outputs, copies=copies, clamp=opt["fape_cutoff"]), + "dgram_cce": get_dgram_loss(inputs, outputs, copies=copies, aatype=inputs["aatype"]), + "rmsd": aln["rmsd"], + }) + + # unsupervised losses + self._loss_unsupervised(inputs, outputs, opt, aux) def _loss_binder(self, inputs, outputs, opt, aux): '''get losses''' - plddt_prob = jax.nn.softmax(outputs["predicted_lddt"]["logits"]) - plddt_loss = (plddt_prob * jnp.arange(plddt_prob.shape[-1])[::-1]).mean(-1) - aux["losses"]["plddt"] = plddt_loss[...,self._target_len:].mean() - self._get_pairwise_loss(inputs, outputs, opt, aux, interface=True) - + zeros = jnp.zeros(sum(self._lengths)) + binder_id = zeros.at[self._target_len:].set(1) + if "hotspot" in opt: + target_id = zeros.at[opt["hotspot"]].set(1) + else: + target_id = zeros.at[:self._target_len].set(1) + + # unsupervised losses + aux["losses"].update({ + "plddt": get_plddt_loss(outputs, mask_1d=binder_id), # plddt over binder + "exp_res": get_exp_res_loss(outputs, mask_1d=binder_id), + "pae": get_pae_loss(outputs, mask_1d=binder_id), # pae over binder + interface + "con": get_con_loss(inputs, outputs, opt["con"], mask_1d=binder_id, mask_1b=binder_id), + # interface + "i_con": get_con_loss(inputs, outputs, opt["i_con"], mask_1d=binder_id, mask_1b=target_id), + "i_pae": get_pae_loss(outputs, mask_1d=binder_id, mask_1b=target_id), + }) + + # supervised losses if self._args["redesign"]: + aln = get_rmsd_loss(inputs, outputs, L=self._target_len, include_L=False) align_fn = aln["align"] - - fape = get_fape_loss(inputs, outputs, model_config=self._cfg) # compute cce of binder + interface - aatype = inputs["aatype"][0] - cce = get_dgram_loss(inputs, outputs, aatype=aatype, return_cce=True) + aatype = inputs["aatype"] + cce = get_dgram_loss(inputs, outputs, aatype=aatype, return_mtx=True) + + # compute fape + fape = get_fape_loss(inputs, outputs, clamp=opt["fape_cutoff"], return_mtx=True) + + aux["losses"].update({ + "rmsd": aln["rmsd"], + "dgram_cce": cce[self._target_len:,:].mean(), + "fape": fape[self._target_len:,:].mean() + }) - aux["losses"].update({"rmsd":aln["rmsd"], "fape":fape, "dgram_cce":cce[self._target_len:,:].mean()}) - else: align_fn = get_rmsd_loss(inputs, outputs, L=self._target_len)["align"] @@ -64,157 +76,100 @@ def _loss_binder(self, inputs, outputs, opt, aux): def _loss_partial(self, inputs, outputs, opt, aux): '''get losses''' - batch = inputs["batch"] - plddt_prob = jax.nn.softmax(outputs["predicted_lddt"]["logits"]) - plddt_loss = (plddt_prob * jnp.arange(plddt_prob.shape[-1])[::-1]).mean(-1) - aux["losses"]["plddt"] = plddt_loss.mean() - self._get_pairwise_loss(inputs, outputs, opt, aux) - - def sub(x, p, axis=0): - fn = lambda y:jnp.take(y,p,axis) - # fn = lambda y:jnp.tensordot(p,y,(-1,axis)).swapaxes(axis,0) - return jax.tree_map(fn, x) - pos = opt["pos"] - aatype = inputs["aatype"][0] - _config = self._cfg.model.heads.structure_module - - # dgram - dgram = sub(sub(outputs["distogram"]["logits"],pos),pos,1) - if aatype is not None: aatype = sub(aatype,pos,0) - aux["losses"]["dgram_cce"] = get_dgram_loss(inputs, pred=dgram, copies=1, aatype=aatype) - - # rmsd - true = batch["all_atom_positions"] - pred = sub(outputs["structure_module"]["final_atom_positions"],pos) - aln = _get_rmsd_loss(true[:,1], pred[:,1]) - aux["losses"]["rmsd"] = aln["rmsd"] - - # fape - fape_loss = {"loss":0.0} - struct = outputs["structure_module"] - traj = {"traj":sub(struct["traj"],pos,-2)} - folding.backbone_loss(fape_loss, batch, traj, _config) - aux["losses"]["fape"] = fape_loss["loss"] + if self._args["repeat"] or self._args["homooligomer"]: + C,L = self._args["copies"], self._len + pos = (jnp.repeat(pos,C).reshape(-1,C) + jnp.arange(C) * L).T.flatten() + + def sub(x, axis=0): return jnp.take(x,pos,axis) + + copies = self._args["copies"] if self._args["homooligomer"] else 1 + aatype = sub(inputs["aatype"]) + dgram = {"logits":sub(sub(outputs["distogram"]["logits"]),1), + "bin_edges":outputs["distogram"]["bin_edges"]} + atoms = sub(outputs["structure_module"]["final_atom_positions"]) + + I = {"aatype": aatype, "batch": inputs["batch"]} + O = {"distogram": dgram, "structure_module": {"final_atom_positions": atoms}} + aln = get_rmsd_loss(I, O, copies=copies) + + # supervised losses + aux["losses"].update({ + "dgram_cce": get_dgram_loss(I, O, copies=copies, aatype=I["aatype"]), + "fape": get_fape_loss(I, O, copies=copies, clamp=opt["fape_cutoff"]), + "rmsd": aln["rmsd"], + }) + + # unsupervised losses + self._loss_unsupervised(inputs, outputs, opt, aux) # sidechain specific losses - if self._args["use_sidechains"]: - # sc_fape - pred_pos = sub(struct["final_atom14_positions"],pos) - sc_struct = {**folding.compute_renamed_ground_truth(self._sc["batch"], pred_pos), - "sidechains":{k: sub(struct["sidechains"][k],pos,1) for k in ["frames","atom_pos"]}} - - aux["losses"]["sc_fape"] = folding.sidechain_loss(batch, sc_struct, _config)["loss"] + if self._args["use_sidechains"] and copies == 1: + + struct = outputs["structure_module"] + pred_pos = sub(struct["final_atom14_positions"]) + true_pos = all_atom.atom37_to_atom14(inputs["batch"]["all_atom_positions"], self._sc["batch"]) # sc_rmsd - true_pos = all_atom.atom37_to_atom14(batch["all_atom_positions"], self._sc["batch"]) - aln = get_sc_rmsd(true_pos, pred_pos, self._sc["pos"]) + aln = _get_sc_rmsd_loss(true_pos, pred_pos, self._sc["pos"]) aux["losses"]["sc_rmsd"] = aln["rmsd"] + + # sc_fape + if not self._args["use_multimer"]: + sc_struct = {**folding.compute_renamed_ground_truth(self._sc["batch"], pred_pos), + "sidechains":{k: sub(struct["sidechains"][k],1) for k in ["frames","atom_pos"]}} + batch = {**inputs["batch"], + **all_atom.atom37_to_frames(**inputs["batch"])} + aux["losses"]["sc_fape"] = folding.sidechain_loss(batch, sc_struct, + self._cfg.model.heads.structure_module)["loss"] + + else: + # TODO + print("ERROR: 'sc_fape' not currently supported for 'multimer' mode") + aux["losses"]["sc_fape"] = 0.0 # align final atoms aux["atom_positions"] = aln["align"](aux["atom_positions"]) - def _get_pairwise_loss(self, inputs, outputs, opt, aux, interface=False): - '''get pairwise loss features''' - - # decide on what offset to use - if "offset" in inputs: - offset = inputs["offset"][0] - else: - idx = inputs["residue_index"][0] - offset = idx[:,None] - idx[None,:] - - # pae loss - pae_prob = jax.nn.softmax(outputs["predicted_aligned_error"]["logits"]) - pae = (pae_prob * jnp.arange(pae_prob.shape[-1])).mean(-1) + def _loss_hallucination(self, inputs, outputs, opt, aux): + # unsupervised losses + self._loss_unsupervised(inputs, outputs, opt, aux) + + def _loss_unsupervised(self, inputs, outputs, opt, aux): + + # define masks + mask_1d = jnp.ones_like(inputs["asym_id"]) + if "pos" in opt: + C,L = self._args["copies"], self._len + pos = opt["pos"] + if C > 1: pos = (jnp.repeat(pos,C).reshape(-1,C) + jnp.arange(C) * L).T.flatten() + mask_1d = mask_1d.at[pos].set(0) - # define distogram - dgram = outputs["distogram"]["logits"] - dgram_bins = jnp.append(0,outputs["distogram"]["bin_edges"]) - if not interface: - aux["losses"].update({"con":get_con_loss(dgram, dgram_bins, offset=offset, **opt["con"]).mean(), - "helix":get_helix_loss(dgram, dgram_bins, offset=offset, **opt["con"]), - "pae":pae.mean()}) - else: - # split pae/con into inter/intra - if self.protocol == "binder": - (L,H) = (self._target_len, opt.get("pos",None)) - else: - (L,H) = (self._len, None) - - def split_feats(v): - '''split pairwise features into intra and inter features''' - if v is None: - return None,None - - (aa,bb) = (v[:L,:L],v[L:,L:]) - if H is None: - (ab,ba) = (v[:L,L:],v[L:,:L]) - else: - (ab,ba) = (v[H, L:],v[L:, H]) - - abba = (ab + ba.swapaxes(0,1)) / 2 - if self.protocol == "binder": - return bb,abba.swapaxes(0,1) - else: - return aa,abba - - x_offset, ix_offset = split_feats(jnp.abs(offset)) - for k,v in zip(["pae","con"], [pae,dgram]): - x, ix = split_feats(v) - if k == "con": - aux["losses"]["helix"] = get_helix_loss(x, dgram_bins, x_offset) - x = get_con_loss(x, dgram_bins, offset=x_offset, **opt["con"]) - ix = get_con_loss(ix, dgram_bins, offset=ix_offset, **opt["i_con"]) - - aux["losses"].update({k:x.mean(),f"i_{k}":ix.mean()}) + mask_2d = inputs["asym_id"][:,None] == inputs["asym_id"][None,:] + masks = {"mask_1d":mask_1d, + "mask_2d":mask_2d} + + # define losses + losses = { + "exp_res": get_exp_res_loss(outputs, mask_1d=mask_1d), + "plddt": get_plddt_loss(outputs, mask_1d=mask_1d), + "pae": get_pae_loss(outputs, **masks), + "con": get_con_loss(inputs, outputs, opt["con"], **masks) + } + + # define losses at interface + if self._args["copies"] > 1 and not self._args["repeat"]: + masks = {"mask_1d": mask_1d if self._args["homoligomer"] else jnp.ones_like(mask_1d), + "mask_2d": mask_2d == False} + losses.update({ + "i_pae": get_pae_loss(outputs, **masks), + "i_con": get_con_loss(inputs, outputs, opt["i_con"], **masks), + }) + + aux["losses"].update(losses) ##################################################################################### -##################################################################################### -def get_pw_con_loss(dgram, dgram_bins, cutoff=None, binary=True): - '''dgram to contacts''' - if cutoff is None: cutoff = dgram_bins[-1] - bins = dgram_bins < cutoff - px = jax.nn.softmax(dgram) - px_ = jax.nn.softmax(dgram - 1e7 * (1-bins)) - # binary/cateogorical cross-entropy - con_loss_cat_ent = -(px_ * jax.nn.log_softmax(dgram)).sum(-1) - con_loss_bin_ent = -jnp.log((bins * px + 1e-8).sum(-1)) - return jnp.where(binary, con_loss_bin_ent, con_loss_cat_ent) - -def get_con_loss(dgram, dgram_bins, cutoff=None, binary=True, - num=1, seqsep=0, offset=None): - '''convert distogram into contact loss''' - x = get_pw_con_loss(dgram, dgram_bins, cutoff, binary) - a,b = x.shape - if offset is None: - mask = jnp.abs(jnp.arange(a)[:,None] - jnp.arange(b)[None,:]) >= seqsep - else: - mask = jnp.abs(offset) >= seqsep - x = jnp.sort(jnp.where(mask,x,jnp.nan)) - k_mask = (jnp.arange(b) < num) * (jnp.isnan(x) == False) - return jnp.where(k_mask,x,0.0).sum(-1) / (k_mask.sum(-1) + 1e-8) - -def get_helix_loss(dgram, dgram_bins, offset=None, **kwargs): - '''helix bias loss''' - x = get_pw_con_loss(dgram, dgram_bins, cutoff=6.0, binary=True) - if offset is None: - return jnp.diagonal(x,3).mean() - else: - mask = offset == 3 - return jnp.where(mask,x,0.0).sum() / (mask.sum() + 1e-8) - -def get_contact_map(outputs, dist=8.0): - '''get contact map from distogram''' - dist_logits = outputs["distogram"]["logits"] - dist_bins = jax.numpy.append(0,outputs["distogram"]["bin_edges"]) - dist_mtx = dist_bins[dist_logits.argmax(-1)] - return (jax.nn.softmax(dist_logits) * (dist_bins < dist)).sum(-1) - -#################### -# confidence metrics -#################### def get_plddt(outputs): logits = outputs["predicted_lddt"]["logits"] num_bins = logits.shape[-1] @@ -231,52 +186,190 @@ def get_pae(outputs): bin_centers = jnp.append(bin_centers,bin_centers[-1]+step) return (prob*bin_centers).sum(-1) -def get_ptm(outputs): +def get_ptm(inputs, outputs, interface=False): pae = outputs["predicted_aligned_error"] - return confidence_jax.predicted_tm_score_jax(**pae) + if "asym_id" not in pae: + pae["asym_id"] = inputs["asym_id"] + return confidence_jax.predicted_tm_score_jax(**pae, interface=interface) + +def get_contact_map(outputs, dist=8.0): + '''get contact map from distogram''' + dist_logits = outputs["distogram"]["logits"] + dist_bins = jax.numpy.append(0,outputs["distogram"]["bin_edges"]) + dist_mtx = dist_bins[dist_logits.argmax(-1)] + return (jax.nn.softmax(dist_logits) * (dist_bins < dist)).sum(-1) + +#################### +# confidence metrics +#################### +def mask_loss(x, mask=None, mask_grad=False): + if mask is None: + return x.mean() + else: + x_masked = (x * mask).sum() / (1e-8 + mask.sum()) + if mask_grad: + return jax.lax.stop_gradient(x.mean() - x_masked) + x_masked + else: + return x_masked + +def get_exp_res_loss(outputs, mask_1d=None): + p = jax.nn.sigmoid(outputs["experimentally_resolved"]["logits"]) + p = 1 - p[...,residue_constants.atom_order["CA"]] + return mask_loss(p, mask_1d) + +def get_plddt_loss(outputs, mask_1d=None): + p = jax.nn.softmax(outputs["predicted_lddt"]["logits"]) + p = (p * jnp.arange(p.shape[-1])[::-1]).mean(-1) + return mask_loss(p, mask_1d) + +def get_pae_loss(outputs, mask_1d=None, mask_1b=None, mask_2d=None): + p = jax.nn.softmax(outputs["predicted_aligned_error"]["logits"]) + p = (p * jnp.arange(p.shape[-1])).mean(-1) + p = (p + p.T)/2 + L = p.shape[0] + if mask_1d is None: mask_1d = jnp.ones(L) + if mask_1b is None: mask_1b = jnp.ones(L) + if mask_2d is None: mask_2d = jnp.ones((L,L)) + mask_2d = mask_2d * mask_1d[:,None] * mask_1b[None,:] + return mask_loss(p, mask_2d) + +def get_con_loss(inputs, outputs, opt, + mask_1d=None, mask_1b=None, mask_2d=None): + + # get top k + def min_k(x, k=1, mask=None): + y = jnp.sort(x if mask is None else jnp.where(mask,x,jnp.nan)) + k_mask = jnp.logical_and(jnp.arange(y.shape[-1]) < k, jnp.isnan(y) == False) + return jnp.where(k_mask,y,0).sum(-1) / (k_mask.sum(-1) + 1e-8) + + # decide on what offset to use + if "offset" in inputs: + offset = inputs["offset"] + else: + idx = inputs["residue_index"].flatten() + offset = idx[:,None] - idx[None,:] + + # define distogram + dgram = outputs["distogram"]["logits"] + dgram_bins = jnp.append(0,outputs["distogram"]["bin_edges"]) + + p = _get_con_loss(dgram, dgram_bins, cutoff=opt["cutoff"], binary=opt["binary"]) + if "seqsep" in opt: + m = jnp.abs(offset) >= opt["seqsep"] + else: + m = jnp.ones_like(offset) + + # mask results + if mask_1d is None: mask_1d = jnp.ones(m.shape[0]) + if mask_1b is None: mask_1b = jnp.ones(m.shape[0]) + + if mask_2d is None: + m = jnp.logical_and(m, mask_1b) + else: + m = jnp.logical_and(m, mask_2d) + + p = min_k(p, opt["num"], m) + return min_k(p, opt["num_pos"], mask_1d) + +def _get_con_loss(dgram, dgram_bins, cutoff=None, binary=True): + '''dgram to contacts''' + if cutoff is None: cutoff = dgram_bins[-1] + bins = dgram_bins < cutoff + px = jax.nn.softmax(dgram) + px_ = jax.nn.softmax(dgram - 1e7 * (1-bins)) + # binary/cateogorical cross-entropy + con_loss_cat_ent = -(px_ * jax.nn.log_softmax(dgram)).sum(-1) + con_loss_bin_ent = -jnp.log((bins * px + 1e-8).sum(-1)) + return jnp.where(binary, con_loss_bin_ent, con_loss_cat_ent) + +def _get_helix_loss(dgram, dgram_bins, offset=None, **kwargs): + '''helix bias loss''' + x = _get_con_loss(dgram, dgram_bins, cutoff=6.0, binary=True) + if offset is None: + return jnp.diagonal(x,3).mean() + else: + mask = offset == 3 + return jnp.where(mask,x,0.0).sum() / (mask.sum() + 1e-8) #################### # loss functions #################### -def get_dgram_loss(inputs, outputs=None, copies=1, aatype=None, pred=None, return_cce=False): +def get_dgram_loss(inputs, outputs, copies=1, aatype=None, return_mtx=False): batch = inputs["batch"] # gather features if aatype is None: aatype = batch["aatype"] - if pred is None: pred = outputs["distogram"]["logits"] + pred = outputs["distogram"]["logits"] # get true features x, weights = model.modules.pseudo_beta_fn(aatype=aatype, all_atom_positions=batch["all_atom_positions"], - all_atom_masks=batch["all_atom_mask"]) + all_atom_mask=batch["all_atom_mask"]) dm = jnp.square(x[:,None]-x[None,:]).sum(-1,keepdims=True) bin_edges = jnp.linspace(2.3125, 21.6875, pred.shape[-1] - 1) true = jax.nn.one_hot((dm > jnp.square(bin_edges)).sum(-1), pred.shape[-1]) - return _get_dgram_loss(true, pred, weights, copies, return_cce=return_cce) - -def _get_dgram_loss(true, pred, weights=None, copies=1, return_cce=False): + def loss_fn(t,p,m): + cce = -(t*jax.nn.log_softmax(p)).sum(-1) + return cce, (cce*m).sum((-1,-2))/(m.sum((-1,-2))+1e-8) + return _get_pw_loss(true, pred, loss_fn, weights=weights, copies=copies, return_mtx=return_mtx) + +def get_fape_loss(inputs, outputs, copies=1, clamp=10.0, return_mtx=False): + + def robust_norm(x, axis=-1, keepdims=False, eps=1e-8): + return jnp.sqrt(jnp.square(x).sum(axis=axis, keepdims=keepdims) + eps) + + def get_R(N, CA, C): + (v1,v2) = (C-CA, N-CA) + e1 = v1 / robust_norm(v1, axis=-1, keepdims=True) + c = jnp.einsum('li, li -> l', e1, v2)[:,None] + e2 = v2 - c * e1 + e2 = e2 / robust_norm(e2, axis=-1, keepdims=True) + e3 = jnp.cross(e1, e2, axis=-1) + return jnp.concatenate([e1[:,:,None], e2[:,:,None], e3[:,:,None]], axis=-1) + + def get_ij(R,T): + return jnp.einsum('rji,rsj->rsi',R,T[None,:]-T[:,None]) + + def loss_fn(t,p,m): + fape = robust_norm(t-p) + fape = jnp.clip(fape, 0, clamp) / 10.0 + return fape, (fape*m).sum((-1,-2))/(m.sum((-1,-2)) + 1e-8) + + true = inputs["batch"]["all_atom_positions"] + pred = outputs["structure_module"]["final_atom_positions"] + + N,CA,C = (residue_constants.atom_order[k] for k in ["N","CA","C"]) + + true_mask = inputs["batch"]["all_atom_mask"] + weights = true_mask[:,N] * true_mask[:,CA] * true_mask[:,C] + + true = get_ij(get_R(true[:,N],true[:,CA],true[:,C]),true[:,CA]) + pred = get_ij(get_R(pred[:,N],pred[:,CA],pred[:,C]),pred[:,CA]) + + return _get_pw_loss(true, pred, loss_fn, weights=weights, copies=copies, return_mtx=return_mtx) + +def _get_pw_loss(true, pred, loss_fn, weights=None, copies=1, return_mtx=False): length = true.shape[0] - if weights is None: weights = jnp.ones(length) + + if weights is None: + weights = jnp.ones(length) + F = {"t":true, "p":pred, "m":weights[:,None] * weights[None,:]} - def cce_fn(t,p,m): - cce = -(t*jax.nn.log_softmax(p)).sum(-1) - return cce, (cce*m).sum((-1,-2))/(m.sum((-1,-2))+1e-8) - if copies > 1: (L,C) = (length//copies, copies-1) # intra (L,L,F) intra = jax.tree_map(lambda x:x[:L,:L], F) - cce, cce_loss = cce_fn(**intra) + mtx, loss = loss_fn(**intra) # inter (C*L,L,F) inter = jax.tree_map(lambda x:x[L:,:L], F) if C == 0: - i_cce, i_cce_loss = cce_fn(**inter) + i_mtx, i_loss = loss_fn(**inter) else: # (C,L,L,F) @@ -286,20 +379,20 @@ def cce_fn(t,p,m): "m":inter["m"][:,None,:,:,0]} # (C,1,L,L) # (C,C,L,L,F) → (C,C,L,L) → (C,C) → (C) → () - i_cce, i_cce_loss = cce_fn(**inter) - i_cce_loss = sum([i_cce_loss.min(i).sum() for i in [0,1]]) / 2 + i_mtx, i_loss = loss_fn(**inter) + i_loss = sum([i_loss.min(i).sum() for i in [0,1]]) / 2 - total_loss = (cce_loss + i_cce_loss) / copies - return (cce, i_cce) if return_cce else total_loss + total_loss = (loss + i_loss) / copies + return (mtx, i_mtx) if return_mtx else total_loss else: - cce, cce_loss = cce_fn(**F) - return cce if return_cce else cce_loss - + mtx, loss = loss_fn(**F) + return mtx if return_mtx else loss + def get_rmsd_loss(inputs, outputs, L=None, include_L=True, copies=1): batch = inputs["batch"] - true = batch["all_atom_positions"][:,1,:] - pred = outputs["structure_module"]["final_atom_positions"][:,1,:] + true = batch["all_atom_positions"][:,1] + pred = outputs["structure_module"]["final_atom_positions"][:,1] weights = batch["all_atom_mask"][:,1] return _get_rmsd_loss(true, pred, weights=weights, L=L, include_L=include_L, copies=copies) @@ -355,7 +448,7 @@ def _get_rmsd_loss(true, pred, weights=None, L=None, include_L=True, copies=1): return {"rmsd":rmsd, "align":align_fn} -def get_sc_rmsd(true, pred, sc): +def _get_sc_rmsd_loss(true, pred, sc): '''get sidechain rmsd + alignment function''' # select atoms @@ -387,21 +480,22 @@ def get_sc_rmsd(true, pred, sc): rmsd = jnp.sqrt(msd + 1e-8) return {"rmsd":rmsd, "align":align_fn} -#-------------------------------------- -# TODO (make copies friendly) -#-------------------------------------- -def get_fape_loss(inputs, outputs, model_config, use_clamped_fape=False): - batch = inputs["batch"] - sub_batch = jax.tree_map(lambda x: x, batch) - sub_batch["use_clamped_fape"] = use_clamped_fape - loss = {"loss":0.0} - _config = model_config.model.heads.structure_module - folding.backbone_loss(loss, sub_batch, outputs["structure_module"], _config) - return loss["loss"] - -def get_6D_loss(inputs, outputs, **kwargs): - batch = inputs["batch"] - true = batch["all_atom_positions"] - pred = outputs["structure_module"]["final_atom_positions"] - mask = batch["all_atom_mask"] - return _np_get_6D_loss(true, pred, mask, **kwargs) \ No newline at end of file +def get_seq_ent_loss(inputs, outputs, opt): + x = inputs["seq"]["logits"] / opt["temp"] + ent = -(jax.nn.softmax(x) * jax.nn.log_softmax(x)).sum(-1) + mask = jnp.ones(ent.shape[-1]) + if "fix_pos" in opt: + if "pos" in opt: + p = opt["pos"][opt["fix_pos"]] + else: + p = opt["fix_pos"] + mask = mask.at[p].set(0) + ent = (ent * mask).sum() / (mask.sum() + 1e-8) + return {"seq_ent":ent.mean()} + +def get_mlm_loss(outputs, mask, truth=None): + x = outputs["masked_msa"]["logits"] + if truth is None: truth = jax.nn.softmax(x[...,:20]) + ent = -(truth[...,:20] * jax.nn.log_softmax(x)[...,:20]).sum(-1) + ent = (ent * mask).sum(-1) / (mask.sum() + 1e-8) + return {"mlm":ent.mean()} \ No newline at end of file diff --git a/colabdesign/af/model.py b/colabdesign/af/model.py index fd46276c..c843cb4e 100644 --- a/colabdesign/af/model.py +++ b/colabdesign/af/model.py @@ -9,26 +9,30 @@ from colabdesign.shared.utils import Key from colabdesign.af.prep import _af_prep -from colabdesign.af.loss import _af_loss, get_plddt, get_pae, get_contact_map, get_ptm +from colabdesign.af.loss import _af_loss, get_plddt, get_pae, get_contact_map, get_ptm, get_seq_ent_loss, get_mlm_loss from colabdesign.af.utils import _af_utils from colabdesign.af.design import _af_design -from colabdesign.af.inputs import _af_inputs, update_seq, update_aatype, crop_feat +from colabdesign.af.inputs import _af_inputs, update_seq, update_aatype +from colabdesign.af.crop import _af_crop, crop_feat ################################################################ # MK_DESIGN_MODEL - initialize model, and put it all together ################################################################ -class mk_af_model(design_model, _af_inputs, _af_loss, _af_prep, _af_design, _af_utils): +class mk_af_model(design_model, _af_inputs, _af_loss, _af_prep, _af_design, _af_utils, _af_crop): def __init__(self, protocol="fixbb", num_seq=1, num_models=1, sample_models=True, - recycle_mode="average", num_recycles=0, + recycle_mode="last", num_recycles=0, use_templates=False, best_metric="loss", - crop_len=None, crop_mode="slide", - debug=False, use_alphafold=True, use_openfold=False, - loss_callback=None, data_dir="."): + model_names=None, + use_openfold=False, use_alphafold=True, + use_multimer=False, + use_mlm=False, + use_crop=False, crop_len=None, crop_mode="slide", + debug=False, loss_callback=None, data_dir="."): assert protocol in ["fixbb","hallucination","binder","partial"] - assert recycle_mode in ["average","add_prev","backprop","last","sample"] + assert recycle_mode in ["average","first","last","sample","add_prev","backprop"] assert crop_mode in ["slide","roll","pair","dist"] # decide if templates should be used @@ -37,23 +41,25 @@ def __init__(self, protocol="fixbb", num_seq=1, self.protocol = protocol self._loss_callback = loss_callback self._num = num_seq - self._args = {"use_templates":use_templates, - "recycle_mode":recycle_mode, + self._args = {"use_templates":use_templates, "use_multimer":use_multimer, + "recycle_mode":recycle_mode, "use_mlm": use_mlm, "debug":debug, "repeat":False, "homooligomer":False, "copies":1, "best_metric":best_metric, - 'use_alphafold':use_alphafold, 'use_openfold':use_openfold, - "crop":False, "crop_len":crop_len,"crop_mode":crop_mode, + "use_crop":use_crop, "crop_len":crop_len, "crop_mode":crop_mode, "models":None} - self.opt = {"dropout":True, "lr":1.0, "use_pssm":False, + self.opt = {"dropout":True, "lr":1.0, "use_pssm":False, "mlm_dropout":0.05, "num_recycles":num_recycles, "num_models":num_models, "sample_models":sample_models, "temp":1.0, "soft":0.0, "hard":0.0, "bias":0.0, "alpha":2.0, - "con": {"num":2, "cutoff":14.0, "binary":False, "seqsep":9}, - "i_con": {"num":1, "cutoff":20.0, "binary":False}, - "template": {"aatype":21, "dropout":0.0}, - "weights": {"helix":0.0, "plddt":0.01, "pae":0.01}, - "cmap_cutoff": 10.0} + "con": {"num":2, "cutoff":14.0, "binary":False, "seqsep":9, "num_pos":float("inf")}, + "i_con": {"num":1, "cutoff":21.6875, "binary":False, "num_pos":float("inf")}, + "template": {"dropout":0.0, "rm_ic":False, "rm_seq":True, "rm_sc":True}, + "weights": {"seq_ent":0.0, "plddt":0.0, "pae":0.0, "exp_res":0.0}, + "cmap_cutoff": 10.0, "fape_cutoff":10.0} + + if self._args["use_mlm"]: + self.opt["weights"]["mlm"] = 0.1 self._params = {} self._inputs = {} @@ -61,43 +67,40 @@ def __init__(self, protocol="fixbb", num_seq=1, ############################# # configure AlphaFold ############################# - cfg = config.model_config("model_1_ptm" if use_templates else "model_3_ptm") - cfg.model.global_config.use_remat = True - # number of sequences - if use_templates: - cfg.data.eval.max_templates = 1 - cfg.data.eval.max_msa_clusters = num_seq + 1 + if use_multimer: + cfg = config.model_config("model_1_multimer") else: - cfg.data.eval.max_templates = 0 - cfg.data.eval.max_msa_clusters = num_seq - cfg.data.common.max_extra_msa = 1 - cfg.data.eval.masked_msa_replace_fraction = 0 - - # number of recycles - if recycle_mode == "average": num_recycles = 0 - cfg.data.common.num_recycle = 0 # for feature processing - cfg.model.num_recycle = num_recycles # for model configuration + cfg = config.model_config("model_1_ptm" if use_templates else "model_3_ptm") + if recycle_mode in ["average","first","last","sample"]: num_recycles = 0 + cfg.model.num_recycle = num_recycles + cfg.model.global_config.use_remat = True # setup model self._cfg = cfg # load model_params - model_names = [] - if use_templates: - model_names += [f"model_{k}_ptm" for k in [1,2]] - model_names += [f"openfold_model_ptm_{k}" for k in [1,2]] - else: - model_names += [f"model_{k}_ptm" for k in [1,2,3,4,5]] - model_names += [f"openfold_model_ptm_{k}" for k in [1,2]] + ["openfold_model_no_templ_ptm_1"] + if model_names is None: + model_names = [] + if use_multimer: + model_names += [f"model_{k}_multimer_v2" for k in [1,2,3,4,5]] + else: + if use_templates: + if use_alphafold: model_names += [f"model_{k}_ptm" for k in [1,2]] + if use_openfold: model_names += [f"openfold_model_ptm_{k}" for k in [1,2]] + else: + if use_alphafold: model_names += [f"model_{k}_ptm" for k in [1,2,3,4,5]] + if use_openfold: model_names += [f"openfold_model_ptm_{k}" for k in [1,2]] + ["openfold_model_no_templ_ptm_1"] self._model_params, self._model_names = [],[] for model_name in model_names: params = data.get_model_haiku_params(model_name=model_name, data_dir=data_dir) if params is not None: - if not use_templates: + if not use_multimer and not use_templates: params = {k:v for k,v in params.items() if "template" not in k} self._model_params.append(params) self._model_names.append(model_name) + else: + print(f"WARNING: '{model_name}' not found") ##################################### # set protocol specific functions @@ -108,7 +111,10 @@ def __init__(self, protocol="fixbb", num_seq=1, def _get_model(self, cfg, callback=None): - runner = model.RunModel(cfg, is_training=True, recycle_mode=self._args["recycle_mode"]) + a = self._args + runner = model.RunModel(cfg, is_training=True, + recycle_mode=a["recycle_mode"], + use_multimer=a["use_multimer"]) # setup function to get gradients def _model(params, model_params, inputs, key, opt): @@ -120,41 +126,37 @@ def _model(params, model_params, inputs, key, opt): # INPUTS ####################################################################### + L = inputs["aatype"].shape[0] + # get sequence seq = self._get_seq(inputs, params, opt, aux, key()) # update sequence features pssm = jnp.where(opt["use_pssm"], seq["pssm"], seq["pseudo"]) - update_seq(seq["pseudo"], inputs, seq_pssm=pssm) + if a["use_mlm"]: + mlm = jax.random.bernoulli(key(), opt["mlm_dropout"], (L,)) + update_seq(seq["pseudo"], inputs, seq_pssm=pssm, mlm=mlm) + else: + update_seq(seq["pseudo"], inputs, seq_pssm=pssm) # update amino acid sidechain identity - B,L = inputs["aatype"].shape[:2] aatype = jax.nn.one_hot(seq["pseudo"][0].argmax(-1),21) - update_aatype(jnp.broadcast_to(aatype,(B,L,21)), inputs) + update_aatype(jnp.broadcast_to(aatype,(L,21)), inputs) # update template features - if self._args["use_templates"]: + if a["use_templates"]: self._update_template(inputs, opt, key()) + inputs["mask_template_interchain"] = opt["template"]["rm_ic"] # set dropout - inputs["dropout_scale"] = jnp.array([opt["dropout"]]).astype(float) + inputs["dropout_scale"] = jnp.array(opt["dropout"], dtype=float) - # decide number of recycles to do - if self._args["recycle_mode"] in ["last","sample"]: - inputs["num_iter_recycling"] = jnp.array([opt["num_recycles"]]) - - # crop inputs - if opt["crop_pos"].shape[0] < L: - inputs = crop_feat(inputs, opt["crop_pos"], self._cfg) - - if "batch" in inputs: - # need frames for fape - batch = inputs.pop("batch") - batch.update(all_atom.atom37_to_frames(**batch)) - else: - batch = None - - inputs["batch"] = batch + # experimental - crop inputs + if a["use_crop"] and opt["crop_pos"].shape[0] < L: + inputs = crop_feat(inputs, opt["crop_pos"]) + + if "batch" not in inputs: + inputs["batch"] = None ####################################################################### # OUTPUTS @@ -165,34 +167,45 @@ def _model(params, model_params, inputs, key, opt): # add aux outputs aux.update({"atom_positions":outputs["structure_module"]["final_atom_positions"], "atom_mask":outputs["structure_module"]["final_atom_mask"], - "residue_index":inputs["residue_index"][0], "aatype":inputs["aatype"][0], - "plddt":get_plddt(outputs),"pae":get_pae(outputs), "ptm":get_ptm(outputs), - "cmap":get_contact_map(outputs, opt["cmap_cutoff"])}) - - # experimental - # crop outputs (TODO) - if opt["crop_pos"].shape[0] < L: + "residue_index":inputs["residue_index"], + "aatype":inputs["aatype"], + "plddt": get_plddt(outputs), + "pae": get_pae(outputs), + "ptm": get_ptm(inputs, outputs), + "i_ptm": get_ptm(inputs, outputs, interface=True), + "cmap": get_contact_map(outputs, opt["cmap_cutoff"]), + "prev": outputs["prev"]}) + + # experimental - uncrop outputs + if a["use_crop"] and opt["crop_pos"].shape[0] < L: p = opt["crop_pos"] aux["cmap"] = jnp.zeros((L,L)).at[p[:,None],p[None,:]].set(aux["cmap"]) aux["pae"] = jnp.full((L,L),jnp.nan).at[p[:,None],p[None,:]].set(aux["pae"]) - - if self._args["recycle_mode"] == "average": aux["prev"] = outputs["prev"] ####################################################################### # LOSS ####################################################################### - aux["losses"] = {} + + # add protocol specific losses self._get_loss(inputs=inputs, outputs=outputs, opt=opt, aux=aux) + # add user defined losses inputs["seq"] = aux["seq"] if self._loss_callback is not None: loss_fns = self._loss_callback if isinstance(self._loss_callback,list) else [self._loss_callback] for loss_fn in loss_fns: aux["losses"].update(loss_fn(inputs, outputs, opt)) - if self._args["debug"]: + if a["debug"]: aux["debug"] = {"inputs":inputs, "outputs":outputs, "opt":opt} + + # sequence entropy loss + aux["losses"].update(get_seq_ent_loss(inputs, outputs, opt)) + + # experimental masked-language-modeling + if a["use_mlm"]: + aux["losses"].update(get_mlm_loss(outputs, mask=mlm, truth=seq["pssm"])) # weighted loss w = opt["weights"] diff --git a/colabdesign/af/prep.py b/colabdesign/af/prep.py index 4d81b469..1f9da429 100644 --- a/colabdesign/af/prep.py +++ b/colabdesign/af/prep.py @@ -9,6 +9,8 @@ from colabdesign.af.alphafold.data import pipeline, prep_inputs from colabdesign.af.alphafold.common import protein, residue_constants from colabdesign.af.alphafold.model.tf import shape_placeholders +from colabdesign.af.alphafold.model import config + from colabdesign.shared.protein import _np_get_cb, pdb_to_string from colabdesign.shared.prep import prep_pos @@ -35,182 +37,109 @@ def _prep_model(self, **kwargs): self._opt = copy_dict(self.opt) self.restart(**kwargs) - def _prep_features(self, length, num_seq=None, num_templates=1, template_features=None): + def _prep_features(self, num_res, num_seq=None, num_templates=1): '''process features''' if num_seq is None: num_seq = self._num - return prep_input_features(L=length, N=num_seq, T=num_templates, - use_templates=self._args["use_templates"]) - - # prep functions specific to protocol - def _prep_binder(self, pdb_filename, chain="A", - binder_len=50, binder_chain=None, - use_binder_template=False, split_templates=False, - hotspot=None, rm_template_seq=True, rm_template_sc=True, **kwargs): - ''' - prep inputs for binder design - --------------------------------------------------- - -binder_len = length of binder to hallucinate (option ignored if binder_chain is defined) - -binder_chain = chain of binder to redesign - -use_binder_template = use binder coordinates as template input - -split_templates = use target and binder coordinates as seperate template inputs - -hotspot = define position/hotspots on target - -rm_template_seq = for binder redesign protocol, remove sequence info from binder template - --------------------------------------------------- - ''' - - redesign = binder_chain is not None - - self.opt.update({"rm_template_seq":rm_template_seq,"rm_template_sc":rm_template_sc}) - self._args.update({"redesign":redesign}) - - self.opt["template"]["dropout"] = 0.0 if use_binder_template else 1.0 - num_templates = 1 - - # get pdb info - chains = f"{chain},{binder_chain}" if redesign else chain - pdb = prep_pdb(pdb_filename, chain=chains) - - if redesign: - target_len = sum([(pdb["idx"]["chain"] == c).sum() for c in chain.split(",")]) - binder_len = sum([(pdb["idx"]["chain"] == c).sum() for c in binder_chain.split(",")]) - if split_templates: num_templates = 2 - # get input features - self._inputs = self._prep_features(target_len + binder_len, num_templates=num_templates) - - else: - target_len = pdb["residue_index"].shape[0] - self._inputs = self._prep_features(target_len) - - self._inputs["residue_index"][...,:] = pdb["residue_index"] - - # gather hotspot info - hotspot = kwargs.pop("pos", hotspot) - if hotspot is not None: - self.opt["pos"] = prep_pos(hotspot, **pdb["idx"])["pos"] - - # add batch - self._inputs["batch"] = pdb["batch"] - - if redesign: - self._wt_aatype = self._inputs["batch"]["aatype"][target_len:] - self.opt["weights"].update({"dgram_cce":1.0, "fape":0.0, "rmsd":0.0, - "con":0.0, "i_pae":0.01, "i_con":0.0}) - else: # binder hallucination - # pad inputs - total_len = target_len + binder_len - self._inputs = make_fixed_size(self._inputs, self._cfg, total_len) - - # offset residue index for binder - self._inputs["residue_index"] = self._inputs["residue_index"].copy() - self._inputs["residue_index"][:,target_len:] = pdb["residue_index"][-1] + np.arange(binder_len) + 50 - for k in ["seq_mask","msa_mask"]: self._inputs[k] = np.ones_like(self._inputs[k]) - self.opt["weights"].update({"con":0.5, "i_pae":0.01, "i_con":0.5}) - - self._target_len = target_len - self._binder_len = self._len = binder_len - self._lengths = [self._target_len, self._binder_len] + return prep_input_features(L=num_res, N=num_seq, T=num_templates) - self._prep_model(**kwargs) - - def _prep_fixbb(self, pdb_filename, chain=None, copies=1, homooligomer=False, - repeat=False, block_diag=True, rm_template_seq=True, rm_template_sc=True, - pos=None, fix_seq=True, **kwargs): + def _prep_fixbb(self, pdb_filename, chain=None, + copies=1, repeat=False, homooligomer=False, + rm_template_seq=True, rm_template_sc=True, rm_template_ic=False, + fix_pos=None, **kwargs): ''' prep inputs for fixed backbone design --------------------------------------------------- if copies > 1: - -homooligomer=True - input pdb chains are parsed as homo-olgiomeric units - -block_diag=True - each copy is it's own sequence in the MSA - -repeat=True - tie the repeating sequence within single chain - -rm_template_seq - if template is defined, remove information about template sequence - if fix_seq: - -pos="1,2-10" - specify which positions to keep fixed in the sequence + -homooligomer=True - input pdb chains are parsed as homo-oligomeric units + -repeat=True - tie the repeating sequence within single chain + -rm_template_seq - if template is defined, remove information about template sequence + -fix_pos="1,2-10" - specify which positions to keep fixed in the sequence note: supervised loss is applied to all positions, use "partial" protocol to apply supervised loss to only subset of positions --------------------------------------------------- - ''' - self.opt.update({"rm_template_seq":rm_template_seq,"rm_template_sc":rm_template_sc}) - # block_diag the msa features - if block_diag and not repeat and copies > 1: - max_msa_clusters = 1 + self._num * copies - self._cfg.data.eval.max_msa_clusters = max_msa_clusters - else: - max_msa_clusters = self._num - block_diag = False + ''' + self.opt["template"].update({"rm_seq":rm_template_seq,"rm_sc":rm_template_sc, "rm_ic":rm_template_ic}) - pdb = prep_pdb(pdb_filename, chain=chain) - if chain is not None and homooligomer and copies == 1: - copies = len(chain.split(",")) + # prep features + pdb = prep_pdb(pdb_filename, chain=chain, + lengths=kwargs.pop("pdb_lengths",None), + offsets=kwargs.pop("pdb_offsets",None)) self._len = pdb["residue_index"].shape[0] - self._inputs = self._prep_features(self._len, num_seq=max_msa_clusters) - self._inputs["batch"] = pdb["batch"] - - self._args.update({"repeat":repeat, - "block_diag":block_diag, - "homooligomer":homooligomer, - "copies":copies}) + self._lengths = [self._len] - # set weights - self.opt["weights"].update({"dgram_cce":1.0, "rmsd":0.0, "con":0.0, "fape":0.0}) + # feat dims + num_seq = self._num + res_idx = pdb["residue_index"] + + # get [pos]itions of interests + if fix_pos is not None: + self._pos_info = prep_pos(fix_pos, **pdb["idx"]) + self.opt["fix_pos"] = self._pos_info["pos"] - # update residue index from pdb + # repeat/homo-oligomeric support + if chain is not None and copies == 1: copies = len(chain.split(",")) if copies > 1: - if repeat: + + if repeat or homooligomer: self._len = self._len // copies - block_diag = False + if "fix_pos" in self.opt: + self.opt["fix_pos"] = self.opt["fix_pos"][self.opt["fix_pos"] < self._len] + + if repeat: self._lengths = [self._len * copies] + block_diag = False + else: - if homooligomer: - self._len = self._len // copies - self._inputs["residue_index"] = repeat_idx(pdb["residue_index"][:self._len], copies)[None] - else: - self._inputs = make_fixed_size(self._inputs, self._cfg, self._len * copies) - self._inputs["residue_index"] = repeat_idx(pdb["residue_index"], copies)[None] - for k in ["seq_mask","msa_mask"]: self._inputs[k] = np.ones_like(self._inputs[k]) self._lengths = [self._len] * copies + block_diag = not self._args["use_multimer"] + + res_idx = repeat_idx(res_idx[:self._len], copies) + num_seq = (self._num * copies + 1) if block_diag else self._num + self.opt["weights"].update({"i_pae":0.0, "i_con":0.0}) + + self._args.update({"copies":copies, "repeat":repeat, "homooligomer":homooligomer, "block_diag":block_diag}) + homooligomer = not repeat else: - self._inputs["residue_index"] = pdb["residue_index"][None] - self._lengths = [self._len] + self._lengths = pdb["lengths"] - # fix certain positions - self.opt["fix_seq"] = fix_seq - if pos is not None: - self._pos_info = prep_pos(pos, **pdb["idx"]) - self.opt["pos"] = self._pos_info["pos"] + # configure input features + self._inputs = self._prep_features(num_res=sum(self._lengths), num_seq=num_seq) + self._inputs["residue_index"] = res_idx + self._inputs["batch"] = make_fixed_size(pdb["batch"], num_res=sum(self._lengths)) + self._inputs.update(get_multi_id(self._lengths, homooligomer=homooligomer)) + # configure options/weights + self.opt["weights"].update({"dgram_cce":1.0, "rmsd":0.0, "fape":0.0, "con":0.0}) self._wt_aatype = self._inputs["batch"]["aatype"][:self._len] + self._prep_model(**kwargs) - # undocumented: for dist cropping (for Shihao) - cb_atoms = pdb["cb_feat"]["atoms"] - cb_atoms[pdb["cb_feat"]["mask"] == 0,:] = np.nan - self._dist = np.sqrt(np.square(cb_atoms[:,None] - cb_atoms[None,:]).sum(-1)) + # undocumented: dist cropping (for Shihao) + if self._args["use_crop"]: + cb_atoms = pdb["cb_feat"]["atoms"] + cb_atoms[pdb["cb_feat"]["mask"] == 0,:] = np.nan + self._dist = np.sqrt(np.square(cb_atoms[:,None] - cb_atoms[None,:]).sum(-1)) - def _prep_hallucination(self, length=100, copies=1, - repeat=False, block_diag=True, **kwargs): + def _prep_hallucination(self, length=100, copies=1, repeat=False, **kwargs): ''' prep inputs for hallucination --------------------------------------------------- if copies > 1: - -homooligomer=True - input pdb chains are parsed as homo-olgiomeric units - -block_diag=True - each copy is it's own sequence in the MSA -repeat=True - tie the repeating sequence within single chain --------------------------------------------------- ''' - # set [arg]uments - if block_diag and not repeat and copies > 1: - max_msa_clusters = 1 + self._num * copies - self._cfg.data.eval.max_msa_clusters = max_msa_clusters + # define num copies (for repeats/ homo-oligomers) + if not repeat and copies > 1 and not self._args["use_multimer"]: + (num_seq, block_diag) = (self._num * copies + 1, True) else: - max_msa_clusters = self._num - block_diag = False - self._args.update({"block_diag":block_diag, "repeat":repeat, "copies":copies}) + (num_seq, block_diag) = (self._num, False) + + self._args.update({"repeat":repeat,"block_diag":block_diag,"copies":copies}) # prep features self._len = length - self._inputs = self._prep_features(length * copies, num_seq=max_msa_clusters) # set weights self.opt["weights"].update({"con":1.0}) @@ -220,65 +149,179 @@ def _prep_hallucination(self, length=100, copies=1, self._lengths = [self._len * copies] else: offset = 50 - self.opt["weights"].update({"i_pae":0.01, "i_con":0.1}) self._lengths = [self._len] * copies - self._inputs["residue_index"] = repeat_idx(np.arange(length), copies, offset=offset)[None] + self.opt["weights"].update({"i_pae":0.0, "i_con":1.0}) + res_idx = repeat_idx(np.arange(length), copies, offset=offset) else: self._lengths = [self._len] + res_idx = np.arange(length) + + # configure input features + self._inputs = self._prep_features(num_res=sum(self._lengths), num_seq=num_seq) + self._inputs["residue_index"] = res_idx + self._inputs.update(get_multi_id(self._lengths, homooligomer=True)) + + self._prep_model(**kwargs) + + def _prep_binder(self, pdb_filename, chain="A", + binder_len=50, binder_chain=None, + use_binder_template=False, + rm_template_ic=False,rm_template_seq=True, rm_template_sc=True, + hotspot=None, **kwargs): + ''' + prep inputs for binder design + --------------------------------------------------- + -binder_len = length of binder to hallucinate (option ignored if binder_chain is defined) + -binder_chain = chain of binder to redesign + -use_binder_template = use binder coordinates as template input + -rm_template_ic = use target and binder coordinates as seperate template inputs + -hotspot = define position/hotspots on target + -rm_template_seq = for binder redesign protocol, remove sequence info from binder template + --------------------------------------------------- + ''' + + redesign = binder_chain is not None + + self.opt["template"].update({"rm_seq":rm_template_seq, "rm_sc":rm_template_sc, "rm_ic":rm_template_ic, + "dropout":(0.0 if use_binder_template else 1.0)}) + self._args.update({"redesign":redesign}) + + # get pdb info + chains = f"{chain},{binder_chain}" if redesign else chain + pdb = prep_pdb(pdb_filename, chain=chains, ignore_missing=True) + res_idx = pdb["residue_index"] + + if redesign: + self._target_len = sum([(pdb["idx"]["chain"] == c).sum() for c in chain.split(",")]) + self._binder_len = self._len = sum([(pdb["idx"]["chain"] == c).sum() for c in binder_chain.split(",")]) + else: + self._target_len = pdb["residue_index"].shape[0] + self._binder_len = self._len = binder_len + res_idx = np.append(res_idx, res_idx[-1] + np.arange(binder_len) + 50) + self._lengths = [self._target_len, self._binder_len] + + # gather hotspot info + if hotspot is not None: + self.opt["hotspot"] = prep_pos(hotspot, **pdb["idx"])["pos"] + + if redesign: + # binder redesign + self._wt_aatype = pdb["batch"]["aatype"][target_len:] + self.opt["weights"].update({"dgram_cce":1.0, "rmsd":0.0, "fape":0.0, + "con":0.0, "i_con":0.0, "i_pae":0.0}) + else: + # binder hallucination + pdb["batch"] = make_fixed_size(pdb["batch"], num_res=sum(self._lengths)) + self.opt["weights"].update({"plddt":0.1, "con":0.0, "i_con":1.0, "i_pae":0.0}) + + # configure input features + self._inputs = self._prep_features(num_res=sum(self._lengths), num_seq=1) + self._inputs["residue_index"] = res_idx + self._inputs["batch"] = pdb["batch"] + self._inputs.update(get_multi_id(self._lengths)) + self._prep_model(**kwargs) def _prep_partial(self, pdb_filename, chain=None, length=None, - pos=None, fix_seq=True, use_sidechains=False, atoms_to_exclude=None, - rm_template_seq=False, rm_template_sc=False, **kwargs): + copies=1, repeat=False, homooligomer=False, + pos=None, fix_pos=None, use_sidechains=False, atoms_to_exclude=None, + rm_template_seq=False, rm_template_sc=False, rm_template_ic=False, **kwargs): ''' prep input for partial hallucination --------------------------------------------------- -length=100 - total length of protein (if different from input PDB) -pos="1,2-10" - specify which positions to apply supervised loss to - -fix_seq=True - keep sequence fixed in the specified positions -use_sidechains=True - add a sidechain supervised loss to the specified positions -atoms_to_exclude=["N","C","O"] (for sc_rmsd loss, specify which atoms to exclude) -rm_template_seq - if template is defined, remove information about template sequence --------------------------------------------------- ''' - self.opt.update({"rm_template_seq":rm_template_seq,"rm_template_sc":rm_template_sc}) + self.opt["template"].update({"rm_seq":rm_template_seq,"rm_sc":rm_template_sc,"rm_ic":rm_template_ic}) # prep features - pdb = prep_pdb(pdb_filename, chain=chain) - - self._len = pdb["residue_index"].shape[0] if length is None else length + pdb = prep_pdb(pdb_filename, chain=chain, + lengths=kwargs.pop("pdb_lengths",None), + offsets=kwargs.pop("pdb_offsets",None)) + + pdb["len"] = sum(pdb["lengths"]) + + self._len = pdb["len"] if length is None else length self._lengths = [self._len] - self._inputs = self._prep_features(self._len) - self._inputs["batch"] = pdb["batch"] - # undocumented: experimental repeat support - if kwargs.pop("repeat",False): - copies = kwargs.pop("copies",1) - if copies > 1: + # feat dims + num_seq = self._num + res_idx = np.arange(self._len) + + # get [pos]itions of interests + if pos is None: + self.opt["pos"] = pdb["pos"] = np.arange(pdb["len"]) + self._pos_info = {"length":np.array([pdb["len"]]), "pos":pdb["pos"]} + else: + self._pos_info = prep_pos(pos, **pdb["idx"]) + self.opt["pos"] = pdb["pos"] = self._pos_info["pos"] + + # repeat/homo-oligomeric support + if chain is not None and copies == 1: copies = len(chain.split(",")) + if copies > 1: + + if repeat or homooligomer: self._len = self._len // copies + pdb["len"] = pdb["len"] // copies + self.opt["pos"] = pdb["pos"][pdb["pos"] < pdb["len"]] + + # repeat positions across copies + pdb["pos"] = repeat_pos(self.opt["pos"], copies, pdb["len"]) + + if repeat: self._lengths = [self._len * copies] - self._args.update({"copies":copies, "repeat":True, "block_diag":False}) + block_diag = False - # configure options/weights - self.opt["pos"] = np.arange(pdb["residue_index"].shape[0]) - self.opt["weights"].update({"dgram_cce":1.0,"con":1.0, "fape":0.0, "rmsd":0.0}) - self.opt["fix_seq"] = fix_seq + else: + self._lengths = [self._len] * copies + block_diag = not self._args["use_multimer"] - # get [pos]itions of interests - if pos is not None: - self._pos_info = prep_pos(pos, **pdb["idx"]) - self.opt["pos"] = self._pos_info["pos"] - self._inputs["batch"] = jax.tree_map(lambda x:x[self.opt["pos"]], pdb["batch"]) - self._wt_aatype = self._inputs["batch"]["aatype"] + num_seq = (self._num * copies + 1) if block_diag else self._num + res_idx = repeat_idx(np.arange(self._len), copies) + + self.opt["weights"].update({"i_pae":0.0, "i_con":1.0}) + + self._args.update({"copies":copies, "repeat":repeat, "homooligomer":homooligomer, "block_diag":block_diag}) + homooligomer = not repeat + + # configure input features + self._inputs = self._prep_features(num_res=sum(self._lengths), num_seq=num_seq) + self._inputs["residue_index"] = res_idx + self._inputs["batch"] = jax.tree_map(lambda x:x[pdb["pos"]], pdb["batch"]) + self._inputs.update(get_multi_id(self._lengths, homooligomer=homooligomer)) + + # configure options/weights + self.opt["weights"].update({"dgram_cce":1.0, "rmsd":0.0, "fape":0.0, "con":1.0}) + self._wt_aatype = pdb["batch"]["aatype"][self.opt["pos"]] # configure sidechains - self._args["use_sidechains"] = kwargs.pop("sidechain", use_sidechains) - if self._args["use_sidechains"]: + self._args["use_sidechains"] = use_sidechains + if use_sidechains: self._sc = {"batch":prep_inputs.make_atom14_positions(self._inputs["batch"]), "pos":get_sc_pos(self._wt_aatype, atoms_to_exclude)} self.opt["weights"].update({"sc_rmsd":0.1, "sc_fape":0.1}) - self.opt["fix_seq"] = True + self.opt["fix_pos"] = np.arange(self.opt["pos"].shape[0]) + self._wt_aatype_sub = self._wt_aatype + + elif fix_pos is not None: + sub_fix_pos = [] + sub_i = [] + pos = self.opt["pos"].tolist() + for i in prep_pos(fix_pos, **pdb["idx"])["pos"]: + if i in pos: + sub_i.append(i) + sub_fix_pos.append(pos.index(i)) + self.opt["fix_pos"] = np.array(sub_fix_pos) + self._wt_aatype_sub = pdb["batch"]["aatype"][sub_i] + + elif kwargs.pop("fix_seq",False): + self.opt["fix_pos"] = np.arange(self.opt["pos"].shape[0]) + self._wt_aatype_sub = self._wt_aatype self._prep_model(**kwargs) @@ -289,12 +332,17 @@ def repeat_idx(idx, copies=1, offset=50): idx_offset = np.repeat(np.cumsum([0]+[idx[-1]+offset]*(copies-1)),len(idx)) return np.tile(idx,copies) + idx_offset -def prep_pdb(pdb_filename, chain=None, for_alphafold=True): +def repeat_pos(pos, copies, length): + return (np.repeat(pos,copies).reshape(-1,copies) + np.arange(copies) * length).T.flatten() + +def prep_pdb(pdb_filename, chain=None, + offsets=None, lengths=None, + ignore_missing=False): '''extract features from pdb''' def add_cb(batch): '''add missing CB atoms based on N,CA,C''' - p,m = batch["all_atom_positions"],batch["all_atom_mask"] + p,m = batch["all_atom_positions"], batch["all_atom_mask"] atom_idx = residue_constants.atom_order atoms = {k:p[...,atom_idx[k],:] for k in ["N","CA","C"]} cb = atom_idx["CB"] @@ -308,64 +356,70 @@ def add_cb(batch): chains = [None] if chain is None else chain.split(",") o,last = [],0 residue_idx, chain_idx = [],[] - for chain in chains: + full_lengths = [] + + for n,chain in enumerate(chains): protein_obj = protein.from_pdb_string(pdb_to_string(pdb_filename), chain_id=chain) batch = {'aatype': protein_obj.aatype, 'all_atom_positions': protein_obj.atom_positions, - 'all_atom_mask': protein_obj.atom_mask} + 'all_atom_mask': protein_obj.atom_mask, + 'residue_index': protein_obj.residue_index} cb_feat = add_cb(batch) # add in missing cb (in the case of glycine) + + if ignore_missing: + r = batch["all_atom_mask"][:,0] == 1 + batch = jax.tree_map(lambda x:x[r], batch) + residue_index = batch["residue_index"] + last - has_ca = batch["all_atom_mask"][:,0] == 1 - batch = jax.tree_map(lambda x:x[has_ca], batch) - seq = "".join([order_aa[a] for a in batch["aatype"]]) - residue_index = protein_obj.residue_index[has_ca] + last - last = residue_index[-1] + 50 + else: + # pad values + offset = 0 if offsets is None else (offsets[n] if isinstance(offsets,list) else offsets) + r = offset + (protein_obj.residue_index - protein_obj.residue_index.min()) + length = (r.max()+1) if lengths is None else (lengths[n] if isinstance(lengths,list) else lengths) + def scatter(x, value=0): + shape = (length,) + x.shape[1:] + y = np.full(shape, value, dtype=x.dtype) + y[r] = x + return y + + batch = {"aatype":scatter(batch["aatype"],-1), + "all_atom_positions":scatter(batch["all_atom_positions"]), + "all_atom_mask":scatter(batch["all_atom_mask"]), + "residue_index":scatter(batch["residue_index"],-1)} + + residue_index = np.arange(length) + last - if for_alphafold: - template_aatype = residue_constants.sequence_to_onehot(seq, residue_constants.HHBLITS_AA_TO_ID) - template_features = {"template_aatype":template_aatype, - "template_all_atom_masks":batch["all_atom_mask"], - "template_all_atom_positions":batch["all_atom_positions"]} - o.append({"batch":batch, - "template_features":template_features, - "residue_index": residue_index, - "cb_feat":cb_feat}) - else: - o.append({"batch":batch, - "residue_index": residue_index, - "cb_feat":cb_feat}) + last = residue_index[-1] + 50 + o.append({"batch":batch, + "residue_index": residue_index, + "cb_feat":cb_feat}) - residue_idx.append(protein_obj.residue_index[has_ca]) + residue_idx.append(batch.pop("residue_index")) chain_idx.append([chain] * len(residue_idx[-1])) + full_lengths.append(len(residue_index)) # concatenate chains o = jax.tree_util.tree_map(lambda *x:np.concatenate(x,0),*o) - if for_alphafold: - o["template_features"] = jax.tree_map(lambda x:x[None],o["template_features"]) - o["template_features"]["template_domain_names"] = np.asarray(["None"]) - # save original residue and chain index o["idx"] = {"residue":np.concatenate(residue_idx), "chain":np.concatenate(chain_idx)} + o["lengths"] = full_lengths return o -def make_fixed_size(feat, cfg, length, batch_axis=True): +def make_fixed_size(feat, num_res, num_seq=1, num_templates=1): '''pad input features''' - if batch_axis: - shape_schema = {k:[None]+v for k,v in dict(cfg.data.eval.feat).items()} - else: - shape_schema = {k:v for k,v in dict(cfg.data.eval.feat).items()} - num_msa_seq = cfg.data.eval.max_msa_clusters - cfg.data.eval.max_templates + shape_schema = {k:v for k,v in config.CONFIG.data.eval.feat.items()} + pad_size_map = { - shape_placeholders.NUM_RES: length, - shape_placeholders.NUM_MSA_SEQ: num_msa_seq, - shape_placeholders.NUM_EXTRA_SEQ: cfg.data.common.max_extra_msa, - shape_placeholders.NUM_TEMPLATES: cfg.data.eval.max_templates - } + shape_placeholders.NUM_RES: num_res, + shape_placeholders.NUM_MSA_SEQ: num_seq, + shape_placeholders.NUM_EXTRA_SEQ: 1, + shape_placeholders.NUM_TEMPLATES: num_templates + } for k,v in feat.items(): if k == "batch": - feat[k] = make_fixed_size(v, cfg, length, batch_axis=False) + feat[k] = make_fixed_size(v, num_res) else: shape = list(v.shape) schema = shape_schema[k] @@ -415,14 +469,19 @@ def get_sc_pos(aa_ident, atoms_to_exclude=None): return {"pos":pos, "pos_alt":pos_alt, "non_amb":non_amb, "weight":w, "weight_non_amb":w_na[:,None]} -def prep_input_features(L, N=1, T=1, use_templates=False, eN=1): +def prep_input_features(L, N=1, T=1, eN=1): ''' given [L]ength, [N]umber of sequences and number of [T]emplates return dictionary of blank features ''' inputs = {'aatype': np.zeros(L,int), - 'target_feat': np.zeros((L,22)), 'msa_feat': np.zeros((N,L,49)), + # 23 = one_hot -> (20, UNK, GAP, MASK) + # 1 = has deletion + # 1 = deletion_value + # 23 = profile + # 1 = deletion_mean_value + 'seq_mask': np.ones(L), 'msa_mask': np.ones((N,L)), 'msa_row_mask': np.ones(N), @@ -435,14 +494,27 @@ def prep_input_features(L, N=1, T=1, use_templates=False, eN=1): 'extra_has_deletion': np.zeros((eN,L)), 'extra_msa': np.zeros((eN,L),int), 'extra_msa_mask': np.zeros((eN,L)), - 'extra_msa_row_mask': np.zeros(eN)} - - if use_templates: - inputs.update({'template_aatype': np.zeros((T,L),int), - 'template_all_atom_masks': np.zeros((T,L,37)), - 'template_all_atom_positions': np.zeros((T,L,37,3)), - 'template_mask': np.ones(T), - 'template_pseudo_beta': np.zeros((T,L,3)), - 'template_pseudo_beta_mask': np.zeros((T,L))}) - - return jax.tree_map(lambda x:x[None], inputs) \ No newline at end of file + 'extra_msa_row_mask': np.zeros(eN), + + # for template inputs + 'template_aatype': np.zeros((T,L),int), + 'template_all_atom_mask': np.zeros((T,L,37)), + 'template_all_atom_positions': np.zeros((T,L,37,3)), + 'template_mask': np.zeros(T), + 'template_pseudo_beta': np.zeros((T,L,3)), + 'template_pseudo_beta_mask': np.zeros((T,L)), + + # for alphafold-multimer + 'asym_id': np.zeros(L), + 'sym_id': np.zeros(L), + 'entity_id': np.zeros(L), + 'all_atom_positions': np.zeros((N,37,3))} + return inputs + +def get_multi_id(lengths, homooligomer=False): + '''set info for alphafold-multimer''' + i = np.concatenate([[n]*l for n,l in enumerate(lengths)]) + if homooligomer: + return {"asym_id":i, "sym_id":i, "entity_id":np.zeros_like(i)} + else: + return {"asym_id":i, "sym_id":i, "entity_id":i} \ No newline at end of file diff --git a/colabdesign/af/utils.py b/colabdesign/af/utils.py index 6d7b6324..657c7d4c 100644 --- a/colabdesign/af/utils.py +++ b/colabdesign/af/utils.py @@ -39,15 +39,13 @@ def set_args(self, **kwargs): ''' set [arg]uments ''' - for k in ["best_metric","crop","crop_mode","crop_len", - "use_openfold","use_alphafold","models"]: + for k in ["best_metric","use_crop","crop_mode","crop_len","models"]: if k in kwargs: self._args[k] = kwargs.pop(k) - if k == "crop" and not self._args[k]: - self._args["crop_len"] = None if "recycle_mode" in kwargs: - if kwargs["recycle_mode"] in ["sample","last"] and self._args["recycle_mode"] in ["sample","last"]: + ok_recycle_mode_swap = ["average","sample","first","last"] + if kwargs["recycle_mode"] in ok_recycle_mode_swap and self._args["recycle_mode"] in ok_recycle_mode_swap: self._args["recycle_mode"] = kwargs.pop("recycle_mode") else: print(f"ERROR: use {self.__class__.__name__}(recycle_mode=...) to set the recycle_mode") @@ -56,7 +54,6 @@ def set_args(self, **kwargs): if len(ks) > 0: print(f"ERROR: the following args were not set: {ks}") - def get_loss(self, x="loss"): '''output the loss (for entire trajectory)''' return np.array([float(loss[x]) for loss in self._traj["log"]]) @@ -109,11 +106,10 @@ def animate(self, s=0, e=None, dpi=100, get_best=True): if self.protocol == "hallucination": return make_animation(**sub_traj, pos_ref=pos_ref, length=self._lengths, dpi=dpi) else: - return make_animation(**sub_traj, pos_ref=pos_ref, length=self._lengths, align_xyz=False, dpi=dpi) + return make_animation(**sub_traj, pos_ref=pos_ref, length=self._lengths, align_xyz=False, dpi=dpi) def plot_pdb(self, show_sidechains=False, show_mainchains=False, - color="pLDDT", color_HP=False, size=(800,480), - animate=False, get_best=True): + color="pLDDT", color_HP=False, size=(800,480), animate=False, get_best=True): ''' use py3Dmol to plot pdb coordinates - color=["pLDDT","chain","rainbow"] @@ -165,4 +161,19 @@ def plot_traj(self, dpi=100): ax2.legend(loc='center left') else: print("TODO") - plt.show() \ No newline at end of file + plt.show() + + def clear_best(self): + self._best = {} + + def save_current_pdb(self, filename=None): + '''save pdb coordinates (if filename provided, otherwise return as string)''' + self.save_pdb(filename=filename, get_best=False) + + def plot_current_pdb(self, show_sidechains=False, show_mainchains=False, + color="pLDDT", color_HP=False, size=(800,480), animate=False): + '''use py3Dmol to plot pdb coordinates + - color=["pLDDT","chain","rainbow"] + ''' + self.plot_pdb(show_sidechains=show_sidechains, show_mainchains=show_mainchains, color=color, + color_HP=color_HP, size=size, animate=animate, get_best=False) \ No newline at end of file diff --git a/colabdesign/shared/model.py b/colabdesign/shared/model.py index 49b95c18..3358f021 100644 --- a/colabdesign/shared/model.py +++ b/colabdesign/shared/model.py @@ -139,14 +139,16 @@ def rewire(self, order=None, offset=0, loops=0): ''' self.opt["pos"] = rewire(length=self._pos_info["length"], order=order, offset=offset, loops=loops) - if hasattr(self,"_opt"): - self._opt["pos"] = self.opt["pos"] + + # make default + if hasattr(self,"_opt"): self._opt["pos"] = self.opt["pos"] def soft_seq(x, opt, key=None): seq = {"input":x} # shuffle msa (randomly pick which sequence is query) if x.ndim == 3 and x.shape[0] > 1 and key is not None: - n = jax.random.randint(key,[],0,x.shape[0]) + key, sub_key = jax.random.split(key) + n = jax.random.randint(sub_key,[],0,x.shape[0]) seq["input"] = seq["input"].at[0].set(seq["input"][n]).at[n].set(seq["input"][0]) # straight-through/reparameterization @@ -158,5 +160,9 @@ def soft_seq(x, opt, key=None): # create pseudo sequence seq["pseudo"] = opt["soft"] * seq["soft"] + (1-opt["soft"]) * seq["input"] - seq["pseudo"] = opt["hard"] * seq["hard"] + (1-opt["hard"]) * seq["pseudo"] + + # key, sub_key = jax.random.split(key) + # hard_mask = jax.random.bernoulli(sub_key, opt["hard"], seq["hard"].shape[:-1] + (1,)) + hard_mask = opt["hard"] + seq["pseudo"] = hard_mask * seq["hard"] + (1-hard_mask) * seq["pseudo"] return seq \ No newline at end of file diff --git a/colabdesign/shared/utils.py b/colabdesign/shared/utils.py index 17eefd2e..b3ebca17 100644 --- a/colabdesign/shared/utils.py +++ b/colabdesign/shared/utils.py @@ -10,27 +10,28 @@ def clear_mem(): def update_dict(D, *args, **kwargs): '''robust function for updating dictionary''' - def set_dict(d, x): + def set_dict(d, x, override=False): for k,v in x.items(): if v is not None: if k in d: if isinstance(v, dict): - set_dict(d[k], x[k]) + set_dict(d[k], x[k], override=override) + elif override or d[k] is None: + d[k] = v elif isinstance(d[k],(np.ndarray,jnp.ndarray)): d[k] = np.asarray(v) - elif d[k] is None: - d[k] = v elif isinstance(d[k], dict): d[k] = jax.tree_map(lambda x: type(x)(v), d[k]) else: d[k] = type(d[k])(v) else: print(f"ERROR: '{k}' not found in {list(d.keys())}") + override = kwargs.pop("override", False) while len(args) > 0 and isinstance(args[0],str): D,args = D[args[0]],args[1:] for a in args: - if isinstance(a, dict): set_dict(D, a) - set_dict(D, kwargs) + if isinstance(a, dict): set_dict(D, a, override=override) + set_dict(D, kwargs, override=override) def copy_dict(x): '''deepcopy dictionary''' diff --git a/colabdesign/tr/joint_model.py b/colabdesign/tr/joint_model.py index b8cfc801..592feafa 100644 --- a/colabdesign/tr/joint_model.py +++ b/colabdesign/tr/joint_model.py @@ -22,17 +22,17 @@ def _prep_inputs(pdb_filename, chain, binder_len=50, binder_chain=None, **kwargs self.tr = mk_tr_model(protocol=protocol) if protocol == "fixbb": - def _prep_inputs(pdb_filename, chain, pos=None, fix_seq=False, **kwargs): - flags = dict(pdb_filename=pdb_filename, chain=chain, pos=pos, fix_seq=fix_seq) + def _prep_inputs(pdb_filename, chain, fix_pos=None, **kwargs): + flags = dict(pdb_filename=pdb_filename, chain=chain, fix_pos=fix_pos) self.af.prep_inputs(**flags, **kwargs) self.tr.prep_inputs(**flags, chain=chain) if protocol == "partial": def _prep_inputs(pdb_filename, chain, pos=None, length=None, - fix_seq=True, use_sidechains=False, atoms_to_exclude=None, **kwargs): + fix_pos=None, use_sidechains=False, atoms_to_exclude=None, **kwargs): if use_sidechains: fix_seq = True flags = dict(pdb_filename=pdb_filename, chain=chain, - length=length, pos=pos, fix_seq=fix_seq) + length=length, pos=pos, fix_pos=fix_pos) af_a2e = kwargs.pop("af_atoms_to_exclude",atoms_to_exclude) tr_a2e = kwargs.pop("tr_atoms_to_exclude",atoms_to_exclude) self.af.prep_inputs(**flags, use_sidechains=use_sidechains, atoms_to_exclude=af_a2e, **kwargs) diff --git a/colabdesign/tr/model.py b/colabdesign/tr/model.py index 5acd8087..81fd5cd2 100644 --- a/colabdesign/tr/model.py +++ b/colabdesign/tr/model.py @@ -63,7 +63,7 @@ def _get_loss(inputs, outputs, opt): # cce loss if self.protocol in ["fixbb","partial"]: - if self.protocol in ["partial"] and "pos" in opt: + if "pos" in opt: pos = opt["pos"] log_p = jax.tree_map(lambda x:x[:,pos][pos,:], log_p) @@ -84,14 +84,17 @@ def _get_loss(inputs, outputs, opt): def _model(params, model_params, opt, inputs, key): seq = soft_seq(params["seq"], opt) - if "pos" in opt: - seq_ref = jax.nn.one_hot(inputs["batch"]["aatype"],20) - p = opt["pos"] - if self.protocol == "partial": - fix_seq = lambda x:jnp.where(opt["fix_seq"],x.at[...,p,:].set(seq_ref),x) + if "fix_pos" in opt: + if "pos" in self.opt: + seq_ref = jax.nn.one_hot(inputs["batch"]["aatype_sub"],20) + p = opt["pos"][opt["fix_pos"]] + fix_seq = lambda x:x.at[...,p,:].set(seq_ref) else: - fix_seq = lambda x:jnp.where(opt["fix_seq"],x.at[...,p,:].set(seq_ref[...,p,:]),x) - seq = jax.tree_map(fix_seq,seq) + seq_ref = jax.nn.one_hot(inputs["batch"]["aatype"],20) + p = opt["fix_pos"] + fix_seq = lambda x:x.at[...,p,:].set(seq_ref[...,p,:]) + seq = jax.tree_map(fix_seq, seq) + inputs.update({"seq":seq["pseudo"][0], "prf":jnp.where(opt["use_pssm"],seq["pssm"],seq["pseudo"])[0]}) rate = jnp.where(opt["dropout"],0.15,0.0) @@ -104,23 +107,33 @@ def _model(params, model_params, opt, inputs, key): "fn":jax.jit(_model)} def prep_inputs(self, pdb_filename=None, chain=None, length=None, - pos=None, fix_seq=True, atoms_to_exclude=None, **kwargs): + pos=None, fix_pos=None, atoms_to_exclude=None, **kwargs): ''' prep inputs for TrDesign ''' if self.protocol in ["fixbb", "partial"]: # parse PDB file and return features compatible with TrRosetta - pdb = prep_pdb(pdb_filename, chain, for_alphafold=False) + pdb = prep_pdb(pdb_filename, chain) self._inputs["batch"] = pdb["batch"] - if pos is not None: + if fix_pos is not None: + self.opt["fix_pos"] = prep_pos(fix_pos, **pdb["idx"])["pos"] + + if self.protocol == "partial" and pos is not None: self._pos_info = prep_pos(pos, **pdb["idx"]) p = self._pos_info["pos"] - if self.protocol == "partial": - self._inputs["batch"] = jax.tree_map(lambda x:x[p], self._inputs["batch"]) - + aatype = self._inputs["batch"]["aatype"] + self._inputs["batch"] = jax.tree_map(lambda x:x[p], self._inputs["batch"]) self.opt["pos"] = p - self.opt["fix_seq"] = fix_seq + if "fix_pos" in self.opt: + sub_i,sub_p = [],[] + p = p.tolist() + for i in self.opt["fix_pos"].tolist(): + if i in p: + sub_i.append(i) + sub_p.append(p.index(i)) + self.opt["fix_pos"] = np.array(sub_p) + self._inputs["batch"]["aatype_sub"] = aatype[sub_i] self._inputs["6D"] = _np_get_6D_binned(self._inputs["batch"]["all_atom_positions"], self._inputs["batch"]["all_atom_mask"]) @@ -236,9 +249,9 @@ def step(self, models=None, backprop=True, g = self.aux["grad"]["seq"] gn = jnp.linalg.norm(g,axis=(-1,-2),keepdims=True) - if "pos" in self.opt and self.opt.get("fix_seq",False): + if "fix_pos" in self.opt: # note: gradients only exist in unconstrained positions - eff_len = self._len - self.opt["pos"].shape[0] + eff_len = self._len - self.opt["fix_pos"].shape[0] else: eff_len = self._len self.aux["grad"]["seq"] *= jnp.sqrt(eff_len)/(gn+1e-7) diff --git a/setup.py b/setup.py index c95d7206..a0863749 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup( name='colabdesign', - version='1.0.5', + version='1.0.6', packages=find_packages(include=['colabdesign*']), install_requires=['py3Dmol','absl-py','biopython', 'chex','dm-haiku','dm-tree', diff --git a/tr/design.ipynb b/tr/design.ipynb index 21e08ddf..344f15ba 100644 --- a/tr/design.ipynb +++ b/tr/design.ipynb @@ -214,7 +214,7 @@ "if [ ! -d params/af ]; then\n", " # download alphafold weights\n", " mkdir -p params/af/params\n", - " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2021-07-14.tar | tar x -C params/af/params\n", + " curl -fsSL https://storage.googleapis.com/alphafold/alphafold_params_2022-03-02.tar | tar x -C params/af/params\n", "fi" ], "metadata": {