import gradio as gr import warnings import os import subprocess from pathlib import Path import shutil import spaces from space_utils.download_weights import download_ligandmpnn_weights import random import time from space_utils.pipelines import * from space_utils.handle_events import * from space_utils.handle_files import * download_ligandmpnn_weights() with gr.Blocks(title="") as demo: gr.Markdown("# LigandMPNN for Inverse Folding 🔙 ") with gr.Row(): gr.Markdown(""" Inverse Protein Folding is the step following protein backbone generation in protein design pipelines. It involves designing amino acid sequences that will fold into a given protein backbone structure. ProteinMPNN tackles this problem by using a message-passing neural network architecture to design sequences conditioned on backbone structure. LigandMPNN extends this method to include ligand information, thus conditioning the designed sequence on the atomic context as well. It shows increased recovery of native sequences in pockets interacting with small molecules, nucleotides and metals. This space allows you to run inverse folding jobs using Hugging Face's hardware and download the results! It is based on LigandMPNN's original Github repository. Image and Model Source: Dauparas, J., Lee, G.R., Pecoraro, R. et al. Atomic context-conditioned protein sequence design using LigandMPNN. Nat Methods 22, 717–723 (2025). https://doi.org/10.1038/s41592-025-02626-1 """) gr.Image("assets/ligand_mpnn.png", width=600) gr.Markdown("## How to Use this Space") gr.Markdown(""" Refer to the original repo for a detailed description of the available command line arguments. While essential parameters such as number of designs to generate, temperature, and which chains to design can be easily controlled through the UI, more advanced parameters can still be specified under `Advanced Options`. Please note that this space hard-codes the version of LigandMPNN weights so that trying to change the checkpoint using `--checkpoint_ligand_mpnn` will cause errors. Batch generation allows to design sequences for multiple PDB files at once. Note that CLI options `--fixed_residues_multi`, `--redesigned_residues_multi` and the like that allow fine-grained pdb-specific control over design parameters are not yet implemented. However, one can still specify which residues to fix for all PDBs in the batch at once using `--fixed_residues`. This space pairs well with the RFD3 backbone generation space as the PDB files in its output can be directly uploaded for batch generation. """) with gr.Tabs(): with gr.TabItem("Batch generation"): with gr.Row(): with gr.Column(): num_designs_per_batch_multi = gr.Number( value=2, label="Number of Designs per Batch", precision=0, minimum=1, maximum=16 ) num_batches_multi = gr.Number( value=5, label="Number of Batches", precision=0, minimum=1, maximum=10 ) temperature_multi = gr.Number( label="Temperature", value=0.3, minimum=0, maximum=1.0 ) max_duration = gr.Number( label="Max Duration (seconds)", value=300, minimum=1, maximum=3600, ) chains_to_design_multi = gr.Textbox( label="Chains to Design (comma-separated)", placeholder="e.g., 'A,B' to redesign chains A and B. all chains if left blank" ) with gr.Accordion(label="Advanced Options", open=False): extra_args_multi = gr.Textbox( label="Additional CLI Arguments", placeholder="e.g., --fixed_residues 'C1 C2 C3 C4 C5 C6 C7 C8 C9 C10' --parse_these_chains_only 'A,B'", lines=3, info="Add extra LigandMPNN CLI arguments here (optional)" ) with gr.Column(): pdb_folder = gr.File(label="Upload all PDB files", file_count="multiple", file_types=[".pdb"]) run_btn_multi = gr.Button("Run Generation", variant="primary") runtextbox_multi = gr.Textbox(label="Run status", value="Waiting for generation run...") output_file_multi = gr.File(label="Download LigandMPNN results as zip", visible=True) select_fasta_to_show = gr.Dropdown(label="Select PDB file to visualize fasta results from", choices=[], interactive=True) fastatextbox_multi = gr.Textbox(label="Visualize Fasta results", value="Waiting for generation run...") gen_directory_multi = gr.State(value=None) run_btn_multi.click( run_generation_folder, inputs=[pdb_folder, num_batches_multi, num_designs_per_batch_multi, chains_to_design_multi, temperature_multi, extra_args_multi, max_duration], outputs=[runtextbox_multi, gen_directory_multi, output_file_multi] ).then( display_fasta, inputs=gen_directory_multi, outputs=fastatextbox_multi ).then( lambda gen_dir: gr.update(choices=os.listdir(os.path.join(gen_dir, "seqs"))) if gen_dir else gr.update(), inputs=gen_directory_multi, outputs=select_fasta_to_show ) select_fasta_to_show.change(display_specific_fasta, inputs=[gen_directory_multi, select_fasta_to_show], outputs=fastatextbox_multi) #with gr.TabItem("Single PDB generation"): # with gr.Row(): # with gr.Column(): # num_designs_per_batch = gr.Number( # value=2, # label="Number of Designs per Batch", # precision=0, # minimum=1, # maximum=16 # ) # num_batches= gr.Number( # value=5, # label="Number of Batches", # precision=0, # minimum=1, # maximum=10 # ) # temperature = gr.Number( # label="Temperature", # value=0.3, # minimum=0, # maximum=1.0 # ) # # chains_to_design = gr.Textbox( # label="Chains to Design (comma-separated)", # placeholder="e.g., 'A,B' to redesign chains A and B. all chains if left blank" # ) # # with gr.Accordion(label="Advanced Options", open=False): # extra_args = gr.Textbox( # label="Additional CLI Arguments", # placeholder="e.g., --fixed_residues 'C1 C2 C3 C4 C5 C6 C7 C8 C9 C10' --parse_these_chains_only 'A,B'", # lines=3, # info="Add extra LigandMPNN CLI arguments here (optional)" # ) # # with gr.Column(): # pdb_file = gr.File(label="Upload PDB File", file_types=[".pdb"]) # # run_btn = gr.Button("Run Generation", variant="primary") # runtextbox = gr.Textbox(label="Run status", value="Waiting for generation run...") # # # output_file = gr.File(label="Download LigandMPNN results as zip", visible=True) # fastatextbox = gr.Textbox(label="Visualize Fasta results", value="Waiting for generation run...") # # gen_directory = gr.State(value=None) # # # def generate(pdb_file, num_batches, num_designs_per_batch, chains_to_design, temperature, extra_args): # if pdb_file is None: # return gr.update(), gr.update(value=None) # else: # return run_generation_single_pdb(pdb_file.name, num_batches, num_designs_per_batch, chains_to_design, temperature, extra_args) # # # run_btn.click(give_run_status_single, inputs=[pdb_file, num_batches, num_designs_per_batch], outputs=runtextbox).then( # generate, inputs=[pdb_file, num_batches, num_designs_per_batch, chains_to_design, temperature, extra_args], outputs=[runtextbox, gen_directory] # ).then( # display_fasta, inputs=gen_directory, outputs=fastatextbox # ).then( # download_results_as_zip, inputs=gen_directory, outputs=output_file # ) if __name__ == "__main__": demo.launch()