diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5029267..205ce89 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -70,3 +70,8 @@ jobs: run: | python -m pytest --nbval notebooks/RFDiffusion.ipynb tests/rfdiffusion_output.py + - name: Run PLM-Interact + run: | + python -m pytest --nbval tests/PLM_prepare.py notebooks/PLM-interact.ipynb tests/PLM_output.py + + diff --git a/docs/images/tutorial/bwVisu_PLM_files.png b/docs/images/tutorial/bwVisu_PLM_files.png new file mode 100644 index 0000000..9961ce1 Binary files /dev/null and b/docs/images/tutorial/bwVisu_PLM_files.png differ diff --git a/docs/images/tutorial/bwVisu_PLM_input.png b/docs/images/tutorial/bwVisu_PLM_input.png new file mode 100644 index 0000000..603bbd0 Binary files /dev/null and b/docs/images/tutorial/bwVisu_PLM_input.png differ diff --git a/docs/images/tutorial/bwVisu_PLM_more_output.png b/docs/images/tutorial/bwVisu_PLM_more_output.png new file mode 100644 index 0000000..4e91413 Binary files /dev/null and b/docs/images/tutorial/bwVisu_PLM_more_output.png differ diff --git a/docs/images/tutorial/bwVisu_PLM_output.png b/docs/images/tutorial/bwVisu_PLM_output.png new file mode 100644 index 0000000..9da4552 Binary files /dev/null and b/docs/images/tutorial/bwVisu_PLM_output.png differ diff --git a/docs/tutorials/tutorial_PLM_bwVisu.md b/docs/tutorials/tutorial_PLM_bwVisu.md new file mode 100644 index 0000000..3f73c35 --- /dev/null +++ b/docs/tutorials/tutorial_PLM_bwVisu.md @@ -0,0 +1,88 @@ +# PLM-Interact on bwVisu + +Welcome to the PLM-interact Tutorial for bwVisu! + +PLM-interact is an open-source protein language model that predicts whether two proteins can interact from their sequences. This tutorial will guide you through running PLM-interact on bwVisu. Please follow these steps carefully. Any feedback on the tutorial is welcome! Feel free to [contact us](../contact.md)! + +### Step 1: Get access to bwVisu + +To start, get access to bwVisu via bwForCluster Helix or SDS. For more information, visit + +https://www.urz.uni-heidelberg.de/en/service-catalogue/software-and-applications/bwvisu + +For technical questions regarding the high performance cluster, see https://bw-support.scc.kit.edu. Feel free to [contact us](../contact.md) for support. + +### Step 2: Connect to bwVisu and Start Jupyter + +Go to https://bwvisu.bwservices.uni-heidelberg.de/ and log in with your credentials and one-time password. + +Choose Jupyter and start a new session. Now you can select the resources you need. + +For the inference model in PLM-interact we need a GPU. A list of available GPUs and their specifications is available at https://wiki.bwhpc.de/e/Helix/Hardware#Compute_Nodes, or in the table below. + +![Screenshot](../images/tutorial/Helix_GPU.png){:.invertable} + + +The GPU is selected by "GPU Type". The memory of each GPU Type is specified in GPU Memory per GPU (GB). For this example we select one of the A40 GPUs. Larger jobs (= longer sequences, more chains) require more memory. To access these, it is suggested to run the job directly on the Helix cluster. Feel free to contact us, if you need assistance! + + + +![Screenshot](../images/tutorial/bwVisu_GPU.png){:.invertable} + + +Click on "Launch". This will bring you to a new screen showing your interactive sessions. Wait for your session to be ready, then click on "Connect to Jupyter". This brings you into a JupyterLab environment. + +### Step 3: Set a Working Directory and Upload Files + +Now we need to define a working directory. These will contain all files necessary for the tutorial. A new directory can be created using folder icon on the top left of the file browser: + +![Screenshot](../images/tutorial/bwVisu_newDir.png){: .invertable style="height:111px;width:444px"} + + +#### Input Sequences in `.csv` Format +PLM-interact reads the input sequence pairs from a `.csv` file. More information can be found here. We also provide an example in our github. + +| query | text | +| -------- | ------- | +| {sequence1} | {sequence2} | + +If you copy these from `.fasta` files, make sure that there are no spaces within the sequences. + +Upload the PLM-interact notebook from our github and the `.csv` file by clicking on the upload button: + +![Screenshot](../images/tutorial/bwVisu_upload.png){: .invertable style="height:111px;width:444px"} + +After the upload, you can see the notebooks in the file browser on the left. + +![Screenshot](../images/tutorial/bwVisu_PLM_files.png){: .invertable style="width:377px"} + +### Step 4: Open the Notebook and Start the Calculation + +Open `PLM_interact.ipynb` and execute the cells in the notebook to start your Boltzgen run! + + +#### Verify Input + +Before starting your Boltz prediction you should see the following files in your working directory: + +![Screenshot](../images/tutorial/bwVisu_PLM_input.png){:.invertable style="width:377px"} + +#### Verify Output + +In the output directory, there will be a `.csv` file with the output scores. For more information on the scoring, see the PLM-interact publication. + +![Screenshot](../images/tutorial/bwVisu_PLM_output.png){:.invertable style="width:377px"} + +Note that the entries in the output `.csv` file are in the same order as the input sequence pairs. In the tutorial `.ipynb` we include a simple cell to merge the input sequences and output scores, if you prefer this: + +![Screenshot](../images/tutorial/bwVisu_PLM_more_output.png){:.invertable style="width:377px"} + +### References + +https://www.nature.com/articles/s41467-025-64512-w + +https://github.com/liudan111/PLM-interact + +https://huggingface.co/danliu1226/PLM-interact-650M-humanV11 + +https://huggingface.co/facebook/esm2_t33_650M_UR50D diff --git a/mkdocs.yml b/mkdocs.yml index 1b68a08..539da4d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -59,6 +59,7 @@ nav: - tutorials/tutorial_Boltz_bwVisu.md - tutorials/tutorial_Boltzgen_bwVisu.md - tutorials/tutorial_RFDiffusion_bwVisu.md + - tutorials/tutorial_PLM_bwVisu.md - Contact: contact.md extra_css: diff --git a/notebooks/PLM-interact.ipynb b/notebooks/PLM-interact.ipynb new file mode 100644 index 0000000..4fd4a54 --- /dev/null +++ b/notebooks/PLM-interact.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "5b554a86-ff7e-453a-9e79-877f88bb3290", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "markdown", + "id": "1e510275-e8e5-400b-bde1-c6b67007177f", + "metadata": {}, + "source": [ + "First we need to define where PLM_interact finds our input data and where the output files are written to. You can see these files in the file browser on the left. If you change these names, remember to change them in the analysis notebook as well.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e09c3bd-e8ad-42c0-ada0-798eefbed072", + "metadata": {}, + "outputs": [], + "source": [ + "WORKING_DIR = Path.cwd() / \"PLM_interact\"\n", + "OUTPUT_DIR = WORKING_DIR / \"output\"" + ] + }, + { + "cell_type": "markdown", + "id": "7b0c1ba2-55c2-44dc-86e6-1e950be264a8", + "metadata": {}, + "source": [ + "Next we need to upload our sequence pairs. A description of the `.csv` format can be found [here](https://github.com/liudan111/PLM-interact/tree/main#1-ppi-inference-with-multi-gpus). You also need to tell PLM-interact the length of the longest sequence." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4e4a1ed-4ac8-46dd-8566-59eb6ac9312c", + "metadata": {}, + "outputs": [], + "source": [ + "list_of_sequence_pairs = WORKING_DIR / \"test.csv\"\n", + "max_length = 1520" + ] + }, + { + "cell_type": "markdown", + "id": "fe247cff-b189-4aa4-8270-00bed455be0f", + "metadata": {}, + "source": [ + "Now we can prepare a bash file that calls PLM-interact." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0af52ad0-7482-434d-88c1-d479cf1d68e9", + "metadata": {}, + "outputs": [], + "source": [ + "RUN_FILE = WORKING_DIR / \"run.sh\"\n", + "\n", + "run_file = f\"\"\"\n", + "#!/bin/bash\n", + "\n", + "module load devel/miniforge/24.9.2\n", + "conda activate /mnt/sds-hd/sd25g005/PLMinteract\n", + "\n", + "! torchrun --nproc_per_node=1 -m PLMinteract inference_PPI \\\\\n", + "--seed 2 \\\\\n", + "--batch_size_val 1 \\\\\n", + "--test_filepath {list_of_sequence_pairs} \\\\\n", + "--resume_from_checkpoint /mnt/sds-hd/sd25g005/PLMinteract/download_huggingface_folder/PLM-interact-650M-humanV11/pytorch_model.bin \\\\\n", + "--output_filepath {OUTPUT_DIR}/ \\\\\n", + "--offline_model_path /mnt/sds-hd/sd25g005/PLMinteract/download_huggingface_folder/offline/ \\\\\n", + "--model_name esm2_t33_650M_UR50D \\\\\n", + "--embedding_size 1280 --max_length {max_length}\n", + "\"\"\"\n", + "\n", + "with open(RUN_FILE, \"w\") as file:\n", + " file.write(run_file)" + ] + }, + { + "cell_type": "markdown", + "id": "ce271a31-601b-44da-897a-9ba5f2b2e17e", + "metadata": {}, + "source": [ + "Execute the cell below to start the PLM-interact:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4a42c64e-0880-4a05-8d19-1d1bc59074bd", + "metadata": { + "scrolled": true, + "tags": [ + "nbval-skip" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running file run.sh\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Be aware, overflowing tokens are not returned for the setting you have chosen, i.e. sequence pairs with the 'longest_first' truncation strategy. So the returned list will always be empty even if some tokens have been removed.\n" + ] + }, + { + "data": { + "text/plain": [ + "256" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "os.system(f'echo \"Running file {RUN_FILE}\"')\n", + "os.system(f\"bash {RUN_FILE}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2e0f40d5-349e-4289-bf73-45fc3b2ee1d1", + "metadata": {}, + "source": [ + "If you want to have your input sequences and the output scores in one `.csv` file, execute the following cells:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b0e04a4c-3288-4458-a433-cd3d24ef1f14", + "metadata": { + "tags": [ + "nbval-skip" + ] + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "input_csv = pd.read_csv(list_of_sequence_pairs)\n", + "output_csv = pd.read_csv(OUTPUT_DIR / \"pred_scores.csv\", header=None)\n", + "\n", + "results_csv = pd.merge(input_csv, output_csv, left_index=True, right_index=True)\n", + "results_csv.to_csv(OUTPUT_DIR / \"pred_scores_w_sequences.csv\", index=False)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/references/PLM/run.sh b/references/PLM/run.sh new file mode 100644 index 0000000..d419597 --- /dev/null +++ b/references/PLM/run.sh @@ -0,0 +1,15 @@ + +#!/bin/bash + +module load devel/miniforge/24.9.2 +conda activate /mnt/sds-hd/sd25g005/PLMinteract + +! torchrun --nproc_per_node=1 -m PLMinteract inference_PPI \ +--seed 2 \ +--batch_size_val 1 \ +--test_filepath PLM_interact/test.csv \ +--resume_from_checkpoint /mnt/sds-hd/sd25g005/PLMinteract/download_huggingface_folder/PLM-interact-650M-humanV11/pytorch_model.bin \ +--output_filepath PLM_interact/output/ \ +--offline_model_path /mnt/sds-hd/sd25g005/PLMinteract/download_huggingface_folder/offline/ \ +--model_name esm2_t33_650M_UR50D \ +--embedding_size 1280 --max_length 1520 diff --git a/references/PLM/test.csv b/references/PLM/test.csv new file mode 100644 index 0000000..272d98a --- /dev/null +++ b/references/PLM/test.csv @@ -0,0 +1,3 @@ +query,text +MSAPTPLAEASQIPTIPALSPLTAKQSKGNFFSSNPISSFVVDTYKQLHSHRQSLELVNPGTVENLNKEVSRDVFLSQYFFTGLRADLNKAFSMNPAFQTSHTFSIGSQALPKYAFSALFANDNLFAQGNIDNDLSVSGRLNYGWDKKNISKVNLQISDGQPTMCQLEQDYQASDFSVNVKTLNPSFSEKGEFTGVAVASFLQSVTPQLALGLETLYSRTDGSAPGDAGVSYLTRYVSKKQDWIFSGQLQANGALIASLWRKVAQNVEAGIETTLQAGMVPITDPLMGTPIGIQPTVEGSTTIGAKYEYRQSVYRGTLDSNGKVACFLERKVLPTLSVLFCGEIDHFKNDTKIGCGLQFETAGNQELLMLQQGLDADGNPLQALPQL,MSFLPSFILSDESKERISKILTLTHNVAHYGWIPFVLYLGWAHTSNRPNFLNLLSPLPSV +MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGPGAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN,MSFLPSFILSDESKERISKILTLTHNVAHYGWIPFVLYLGWAHTSNRPNFLNLLSPLPSV diff --git a/tests/PLM_output.py b/tests/PLM_output.py new file mode 100644 index 0000000..c0dff81 --- /dev/null +++ b/tests/PLM_output.py @@ -0,0 +1,24 @@ +from pathlib import Path +from tests.utils import normalize_text_paths + + +REPO_ROOT = Path(__file__).resolve().parent.parent +OUTPUT_DIR = REPO_ROOT / "notebooks" / "PLM_interact" + +output_sh = OUTPUT_DIR / "run.sh" +reference_sh = REPO_ROOT / "references" / "PLM" / "run.sh" + +MARKER = "PLM_interact" + + +def test_run_sh_exists(): + assert output_sh.exists(), f"{output_sh} was not created by the notebook" + + +def test_run_sh_content(): + produced_text = normalize_text_paths(output_sh.read_text(), [MARKER]) + expected_text = normalize_text_paths(reference_sh.read_text(), [MARKER]) + + assert produced_text == expected_text, ( + f"Produced {output_sh} differs from reference" + ) diff --git a/tests/PLM_prepare.py b/tests/PLM_prepare.py new file mode 100644 index 0000000..2d3613f --- /dev/null +++ b/tests/PLM_prepare.py @@ -0,0 +1,15 @@ +from pathlib import Path +from tests.utils import prepare_file + +REPO_ROOT = Path(__file__).resolve().parent.parent + + +SRC_csv = REPO_ROOT / "references" / "PLM" / "test.csv" +DST_csv = REPO_ROOT / "notebooks" / "PLM_interact" / "test.csv" + + +def test_prepare_csv(): + """Ensure reference PLM input files are copied for prediction notebook.""" + prepare_file(SRC_csv, DST_csv) + + assert DST_csv.exists() diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py index 166f4b0..449f6ad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,6 +12,7 @@ "BOLTZGEN_WORKING_DIR": NOTEBOOKS_DIR / "protein_design_w_Boltzgen", "RFDIFFUSION_WORKING_DIR": NOTEBOOKS_DIR / "protein_design_w_RFDiffusion", "BINDCRAFT_WORKING_DIR": NOTEBOOKS_DIR / "protein_design_w_Bindcraft", + "PLM_INTERACT_WORKING_DIR": NOTEBOOKS_DIR / "PLM_interact", }