Download model/PXDesignBench/pxdbench/tasks/monomer.py from OneScience-Group/PXDesign: direct link, hf CLI and curl.
- Browser
- Download file 6.58 kB
-
https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/PXDesignBench/pxdbench/tasks/monomer.py
- Command line
-
hf download hf://OneScience-Group/PXDesign/model/PXDesignBench/pxdbench/tasks/monomer.py
-
curl -L -o monomer.py https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/PXDesignBench/pxdbench/tasks/monomer.py
6.58 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 os | |
| import numpy as np | |
| import pandas as pd | |
| from tqdm.auto import tqdm | |
| from pxdbench.metrics import consistency | |
| from pxdbench.tasks.base import BaseTask | |
| from pxdbench.tools import esmfold | |
| from pxdbench.tools.protmpnn.vanilla_mpnn_predictor import VanillaMPNNPredictor | |
| from pxdbench.utils import save_eval_results | |
| from .registry import register_task | |
| class MonomerTask(BaseTask): | |
| def __init__(self, input_data, cfg, device_id: int, seed: int): | |
| """ | |
| Initialize a MonomerTask instance. | |
| Args: | |
| input_data (dict): Task input parameters with PDB directory and names. | |
| cfg (dict): Configuration dictionary with task settings. | |
| device_id (int): GPU device ID (-1 for CPU). | |
| seed (int): Random seed for reproducibility. | |
| """ | |
| self.task_type = "monomer" | |
| self.task_name = input_data.get("name", "monomer") | |
| self.eval_diversity = cfg.get("eval_diversity", False) | |
| super().__init__(input_data, cfg, device_id, seed) | |
| def get_target_fn(self, item): | |
| return item["name"] + f"_seq{item['seq_idx']}.pdb" | |
| def prepare_consistency_inputs(self, results, folding_dir): | |
| inputs = {} | |
| for item in results: | |
| name = f"{item['name']}_seq{item['seq_idx']}" | |
| inputs[name] = { | |
| "source_file": os.path.join(self.pdb_dir, item["name"] + ".pdb"), | |
| "target_file": os.path.join(folding_dir, self.get_target_fn(item)), | |
| } | |
| return inputs | |
| def design_sequence(self, verbose=True): | |
| """ | |
| Design monomer sequences using Vanilla MPNN. | |
| Initializes a VanillaMPNNPredictor and uses it to generate sequences for monomer proteins. | |
| Args: | |
| verbose (bool, optional): Whether to print detailed progress. Defaults to True. | |
| Returns: | |
| list[dict]: List of design results with "name", "seq_idx", and "sequence" keys. | |
| """ | |
| mpnn_predictor = VanillaMPNNPredictor( | |
| self.cfg.tools.mpnn, | |
| device_id=self.device_id, | |
| verbose=verbose, | |
| seed=self.seed, | |
| ) | |
| results = mpnn_predictor.design_monomer( | |
| self.pdb_dir, self.pdb_names, self.num_seqs | |
| ) | |
| return results | |
| def run(self): | |
| """ | |
| Execute the complete monomer design evaluation workflow. | |
| Workflow steps: | |
| 1. Design sequences via design_sequence() | |
| 2. Predict structures using ESMFold and evaluate self consistency | |
| 3. Calculate secondary structure metrics | |
| 4. Compute diversity and success rates based on scRMSD thresholds | |
| 5. Save sample-level results to CSV and summary metrics to JSON | |
| Returns: | |
| dict: Task metadata and output file paths. | |
| """ | |
| results = self.design_sequence() | |
| esmfold_model = esmfold.ESMFold(self.get_device()) | |
| print("Load esmfold done!") | |
| folding_dir = os.path.join(self.out_dir, "esmfold") | |
| os.makedirs(folding_dir, exist_ok=True) | |
| for item in tqdm(results, desc="ESMFold eval"): | |
| pdb_str, plddt = esmfold_model.predict([item["sequence"]]) | |
| assert len(pdb_str) == 1 and len(plddt) == 1 | |
| with open(os.path.join(folding_dir, self.get_target_fn(item)), "w") as f: | |
| f.write(pdb_str[0]) | |
| item["plddt"] = plddt[0] | |
| inputs = self.prepare_consistency_inputs(results, folding_dir) | |
| outputs = consistency.self_consistency(inputs) | |
| for item in results: | |
| consistency_key = f"{item['name']}_seq{item['seq_idx']}" | |
| item.update(outputs[consistency_key]) | |
| self.cal_secondary(results, chain_id="A") | |
| overall = {} | |
| for threshold in [2, 5]: | |
| success_names = [] | |
| for item in results: | |
| if item["scRMSD"] < threshold: | |
| success_names.append(item["name"]) | |
| div = self.cal_diversity(set(success_names)) | |
| overall[f"scRMSD_lt{threshold}"] = len(success_names) / len(results) | |
| overall[f"scRMSD_lt{threshold}_str"] = len(set(success_names)) / len( | |
| self.pdb_names | |
| ) | |
| overall[f"div_scRMSD_lt{threshold}"] = div | |
| # scTM and scRMSD: max/min(all seq in a same design) -> avg over all designs | |
| overall_consistency = {} | |
| for item in results: | |
| key = item["name"] | |
| if key not in overall_consistency: | |
| overall_consistency[key] = {"scTM": 0.00001, "scRMSD": 10000.0} | |
| cur = overall_consistency[key] | |
| overall_consistency[key]["scTM"] = max(cur["scTM"], item["scTM"]) | |
| overall_consistency[key]["scRMSD"] = min(cur["scRMSD"], item["scRMSD"]) | |
| avg_tm = np.mean([v["scTM"] for v in overall_consistency.values()]) | |
| avg_rmsd = np.mean([v["scRMSD"] for v in overall_consistency.values()]) | |
| overall.update({"scTM": avg_tm, "scRMSD": avg_rmsd}) | |
| sample_df = pd.DataFrame(results) | |
| sample_df = sample_df.sort_values(by=["name", "seq_idx"]) | |
| summary_dict = {"task": self.task_type, "name": self.task_name} | |
| summary_dict.update( | |
| self.summary_from_df( | |
| sample_df, | |
| other_metrics=overall, | |
| ) | |
| ) | |
| sample_save_path, summary_save_path = save_eval_results( | |
| sample_df, summary_dict, self.out_dir, self.sample_fn, self.summary_fn | |
| ) | |
| print( | |
| f"Eval done! Results are saved in {sample_save_path} and {summary_save_path}" | |
| ) | |
| return { | |
| "task": self.task_type, | |
| "name": self.task_name, | |
| "sample_save_path": sample_save_path, | |
| "summary_save_path": summary_save_path, | |
| } | |