Download model/PXDesignBench/pxdbench/tools/protmpnn/main_mpnn.py from OneScience-Group/PXDesign: direct link, hf CLI and curl.
- Browser
- Download file 9.61 kB
-
https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/PXDesignBench/pxdbench/tools/protmpnn/main_mpnn.py
- Command line
-
hf download hf://OneScience-Group/PXDesign/model/PXDesignBench/pxdbench/tools/protmpnn/main_mpnn.py
-
curl -L -o main_mpnn.py https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/PXDesignBench/pxdbench/tools/protmpnn/main_mpnn.py
9.61 kB
| # Copyright 2025 ByteDance and/or its affiliates. | |
| # | |
| # 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. | |
| import argparse | |
| import json | |
| import os | |
| from colabdesign.mpnn import clear_mem, mk_mpnn_model | |
| from ml_collections import ConfigDict | |
| from pxdbench.tools.biopython_utils import get_interface_residue_id, hotspot_residues | |
| from pxdbench.utils import extract_chain_sequence, seed_everything | |
| def get_pdb_basename(pdb_path: str): | |
| assert pdb_path.endswith(".pdb") | |
| basename = os.path.basename(pdb_path) | |
| return basename[:-4] | |
| def design_monomer( | |
| pdb_dir: str, | |
| pdb_names: list[str], | |
| num_samples: int, | |
| mpnn_cfg: ConfigDict, | |
| if_print=True, | |
| ): | |
| """ | |
| Design sequences for monomer proteins using ProteinMPNN. | |
| Args: | |
| pdb_dir (str): Directory containing input PDB files. | |
| pdb_names (list[str]): List of PDB base names (without '.pdb') to process. | |
| num_samples (int): Number of sequences to generate per PDB. | |
| mpnn_cfg (ConfigDict): ProteinMPNN configuration (temperature, weights, etc.). | |
| if_print (bool, optional): Whether to print progress. Defaults to True. | |
| Returns: | |
| list[dict]: List of design results with keys 'name' (PDB name), 'seq_idx' (sequence index), | |
| and 'sequence' (designed amino acid sequence). | |
| """ | |
| clear_mem() | |
| mpnn_model = mk_mpnn_model( | |
| backbone_noise=0.0, | |
| model_name="v_48_020", | |
| weights=mpnn_cfg.weights, | |
| ) | |
| final_result = [] | |
| for name in pdb_names: | |
| pdb_path = os.path.join(pdb_dir, name + ".pdb") | |
| # Prepare MPNN input | |
| mpnn_model.prep_inputs( | |
| pdb_filename=pdb_path, | |
| chain="A", | |
| ) | |
| if if_print: | |
| print(f"{pdb_path} is done") | |
| temperature = mpnn_cfg.temperature | |
| if isinstance(temperature, str): | |
| if temperature == "auto": | |
| temperature = 0.0001 if num_samples > 1 else 0.1 | |
| print( | |
| f"Use temperature {temperature} for num_samples being {num_samples}" | |
| ) | |
| else: | |
| temperature = float(temperature) | |
| else: | |
| raise ValueError(f"Unknown temperature {temperature}") | |
| # Run MPNN sampling | |
| mpnn_sequences = mpnn_model.sample( | |
| temperature=temperature, | |
| num=num_samples, | |
| batch=1, | |
| ) | |
| # Collect sequences | |
| for i, seq in enumerate(mpnn_sequences["seq"]): | |
| final_result.append( | |
| {"name": name, "seq_idx": i, "sequence": seq.split("/")[-1]} | |
| ) | |
| if if_print: | |
| print("finished all Sequence Design") | |
| return final_result | |
| def design_binder( | |
| pdb_dir: str, | |
| pdb_names: list[str], | |
| num_samples: int, | |
| binder_chains: list[str], | |
| cond_chains: list[str], | |
| mpnn_cfg: ConfigDict, | |
| if_print=True, | |
| ): | |
| """ | |
| Design sequences for binder proteins using ProteinMPNN. | |
| Args: | |
| pdb_dir (str): Directory containing input PDB files. | |
| pdb_names (list[str]): List of PDB base names (without '.pdb') to process. | |
| num_samples (int): Number of sequences to generate per PDB. | |
| binder_chains (list[str]): List of binder chain IDs. | |
| cond_chains (list[str]): List of conditional chain IDs. | |
| mpnn_cfg (ConfigDict): ProteinMPNN configuration (temperature, weights, etc.). | |
| if_print (bool, optional): Whether to print progress. Defaults to True. | |
| Returns: | |
| list[dict]: List of design results with keys 'name' (PDB name), 'seq_idx' (sequence index), | |
| and 'sequence' (designed amino acid sequence). | |
| """ | |
| clear_mem() | |
| mpnn_model = mk_mpnn_model( | |
| backbone_noise=0.0, | |
| model_name="v_48_020", | |
| weights=mpnn_cfg.weights, | |
| ) | |
| final_result = [] | |
| for name in pdb_names: | |
| pdb_path = os.path.join(pdb_dir, name + ".pdb") | |
| # Prepare MPNN input | |
| if len(binder_chains) > 1: | |
| raise ValueError(f"Only support one-chain binders, but got {binder_chains}") | |
| if mpnn_cfg.fix_interface: | |
| interacting_residues = hotspot_residues( | |
| pdb_path=pdb_path, binder_chain=binder_chains[0] | |
| ) # Hardcode, only take the first binder chains | |
| if len(interacting_residues) > 0: | |
| fix_pos = get_interface_residue_id( | |
| interacting_residues=interacting_residues, | |
| binder_chain=binder_chains[0], | |
| ) | |
| else: | |
| fix_pos = ",".join(cond_chains) | |
| else: | |
| fix_pos = ",".join(cond_chains) | |
| mpnn_model.prep_inputs( | |
| pdb_filename=pdb_path, | |
| chain=",".join(cond_chains + binder_chains), | |
| fix_pos=fix_pos, | |
| rm_aa=mpnn_cfg.rm_aa, | |
| ) | |
| if if_print: | |
| print(f"{pdb_path} is done") | |
| temperature = mpnn_cfg.temperature | |
| if isinstance(temperature, str): | |
| if temperature == "auto": | |
| temperature = 0.0001 if num_samples > 1 else 0.1 | |
| print( | |
| f"Use temperature {temperature} for num_samples being {num_samples}" | |
| ) | |
| else: | |
| temperature = float(temperature) | |
| else: | |
| raise ValueError(f"Unknown temperature {temperature}") | |
| # Run MPNN sampling | |
| mpnn_sequences = mpnn_model.sample( | |
| temperature=temperature, | |
| num=num_samples, | |
| batch=1, | |
| ) | |
| # Collect sequences | |
| for i, seq in enumerate(mpnn_sequences["seq"]): | |
| final_result.append( | |
| {"name": name, "seq_idx": i, "sequence": seq.split("/")[-1]} | |
| ) | |
| if if_print: | |
| print("finished all Sequence Design") | |
| return final_result | |
| def get_gt_sequence(pdb_dir: str, pdb_names: list[str], binder_chain="B"): | |
| """ | |
| Get ground truth sequences for binder proteins from PDB files. | |
| Args: | |
| pdb_dir (str): Directory containing input PDB files. | |
| pdb_names (list[str]): List of PDB base names (without '.pdb') to process. | |
| binder_chain (str, optional): Chain ID of the binder protein. Defaults to "B". | |
| Returns: | |
| list[dict]: List of design results with keys 'name' (PDB name), 'seq_idx' (sequence index), | |
| and 'sequence' (ground truth amino acid sequence). | |
| """ | |
| final_result = [] | |
| for name in pdb_names: | |
| sequence = [] | |
| result = {} | |
| pdb_path = os.path.join(pdb_dir, name + ".pdb") | |
| seq = extract_chain_sequence(pdb_path, chain_id=binder_chain) | |
| sequence.append(seq) | |
| result["name"] = name | |
| result["sequences"] = sequence | |
| final_result.append({"name": name, "seq_idx": 0, "sequence": sequence[0]}) | |
| return final_result | |
| def main(): | |
| parser = argparse.ArgumentParser(description="ProteinMPNN Sequence Design") | |
| parser.add_argument("--input", type=str, required=True, help="Input JSON file") | |
| parser.add_argument("--output", type=str, required=True, help="Output JSON file") | |
| parser.add_argument("--seed", type=int, default=None) | |
| args = parser.parse_args() | |
| with open(args.input, "r") as f: | |
| input_data = json.load(f) | |
| if args.seed is not None: | |
| seed_everything(args.seed, deterministic=False) | |
| design_type = input_data["design_type"] | |
| try: | |
| if design_type == "monomer": | |
| result = design_monomer( | |
| pdb_dir=input_data["pdb_dir"], | |
| pdb_names=input_data["pdb_names"], | |
| num_samples=input_data["num_samples"], | |
| mpnn_cfg=ConfigDict(input_data["mpnn_cfg"]), | |
| if_print=True, | |
| ) | |
| elif design_type == "binder": | |
| result = design_binder( | |
| pdb_dir=input_data["pdb_dir"], | |
| pdb_names=input_data["pdb_names"], | |
| num_samples=input_data["num_samples"], | |
| binder_chains=input_data["binder_chains"], | |
| cond_chains=input_data["cond_chains"], | |
| mpnn_cfg=ConfigDict(input_data["mpnn_cfg"]), | |
| if_print=True, | |
| ) | |
| elif design_type == "gt": | |
| result = get_gt_sequence( | |
| pdb_dir=input_data["pdb_dir"], | |
| pdb_names=input_data["pdb_names"], | |
| binder_chain=input_data.get("binder_chain", "B"), | |
| ) | |
| else: | |
| raise ValueError(f"Unknown design type: {design_type}") | |
| with open(args.output, "w") as f: | |
| json.dump(result, f) | |
| print(f"Successfully completed {design_type} design") | |
| except Exception as e: | |
| print(f"Error: {str(e)}") | |
| import traceback | |
| traceback.print_exc() | |
| exit(1) | |
| if __name__ == "__main__": | |
| main() | |