Download model/PXDesignBench/pxdbench/run.py from OneScience-Group/PXDesign: direct link, hf CLI and curl.
- Browser
- Download file 11.7 kB
-
https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/PXDesignBench/pxdbench/run.py
- Command line
-
hf download hf://OneScience-Group/PXDesign/model/PXDesignBench/pxdbench/run.py
-
curl -L -o run.py https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/PXDesignBench/pxdbench/run.py
11.7 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 json | |
| import logging | |
| import os | |
| from typing import Union | |
| import torch | |
| import torch.distributed as dist | |
| from protenix.config import parse_configs, parse_sys_args | |
| from protenix.config.extend_types import ListValue, RequiredValue | |
| from protenix.utils.distributed import DIST_WRAPPER | |
| from protenix.utils.logger import get_logger | |
| from pxdbench.pxd_configs.eval import eval_configs | |
| from pxdbench.tasks import get_task_class | |
| from pxdbench.utils import convert_cif_to_pdb | |
| logger = get_logger(__name__) | |
| def run_task(input_data: dict, configs, device_id: int = 0, seed: int = None): | |
| """ | |
| Args: | |
| input_data (dict): A dictionary containing the following keys: | |
| task (str): The task to run. Example: "binder". | |
| pdb_dir (str): The directory containing the PDB files. | |
| pdb_names (list): The names of the PDB files. | |
| cond_chains (list): The chains to condition on. | |
| binder_chains (list): The chains to bind. | |
| out_dir (str): The root directory. Outputs will be saved here. | |
| configs (dict): A dictionary containing the configuration for the task. | |
| device_id (int): Device ID to run the task on. | |
| seed (int): Random seed to use. | |
| Returns: | |
| dict: A dictionary containing the results of the task. | |
| """ | |
| if device_id >= 0 and not torch.cuda.is_available(): | |
| raise ValueError("device_id must be -1 (CPU) or a valid GPU ID") | |
| task = input_data["task"] | |
| task_cls = get_task_class(task) | |
| task_cfg = configs.get(task) | |
| task = task_cls(input_data, task_cfg, device_id, seed) | |
| return task.run() | |
| def split_string(s): | |
| return s.split(",") | |
| def get_file_name_list(file_name_list: Union[str, list]): | |
| """ | |
| Get file names from a string or a file. | |
| Args: | |
| file_name_list (str or list): A string or a list containing file names. | |
| If a string, it should be a comma-separated list of file names. | |
| If a file, it should contain one file name per line. | |
| Returns: | |
| list: A list of file names. | |
| """ | |
| if isinstance(file_name_list, list): | |
| return file_name_list | |
| if os.path.exists(file_name_list): | |
| file_names = [] | |
| with open(file_name_list, "r") as f: | |
| for line in f.readlines(): | |
| line = line.strip() | |
| if line: | |
| file_names.append(line) | |
| return file_names | |
| else: | |
| file_name_list = file_name_list.split(",") | |
| return file_name_list | |
| def find_files_with_ext(folder_path, ext="cif"): | |
| pdb_files = [] | |
| for filename in os.listdir(folder_path): | |
| if filename.endswith(f".{ext}"): | |
| pdb_files.append(filename[: -(len(ext) + 1)]) | |
| return sorted(pdb_files) | |
| def get_chains_from_pdb(pdb_path): | |
| chains = set() | |
| with open(pdb_path, "r") as f: | |
| for line in f: | |
| if line.startswith("ATOM") or line.startswith("HETATM"): | |
| chain_id = line[21].strip() | |
| if chain_id: | |
| chains.add(chain_id) | |
| return chains | |
| def prepare_tasks_from_mmcif( | |
| data_dir: str, | |
| file_name_list: list, | |
| binder_chains: list[str], | |
| cond_chains: list[str], | |
| dump_dir: str, | |
| ): | |
| """ | |
| Prepare tasks from mmCIF files. | |
| Args: | |
| data_dir (str): The directory containing the mmCIF files. | |
| file_name_list (str or list): A string or a list containing file names. | |
| If a string, it should be a comma-separated list of file names. | |
| If a file, it should contain one file name per line. | |
| binder_chains (list): The binder chain IDs. | |
| cond_chains (list): The chains to condition on. Could be auto infered by binder_chains. | |
| root_dir (str): The root directory. Outputs will be saved here. | |
| Returns: | |
| dict: A dictionary containing the results of the task. | |
| """ | |
| n_cond_chains, n_binder_chains = None, None | |
| valid_file_name_list = [] | |
| for file_name in file_name_list: | |
| mmcif_path = os.path.join(data_dir, f"{file_name}.cif") | |
| if not os.path.exists(mmcif_path): | |
| logger.warning(f"Could not find {mmcif_path}, skip!") | |
| continue | |
| else: | |
| valid_file_name_list.append(file_name) | |
| pdb_path = os.path.join(data_dir, "converted_pdbs", f"{file_name}.pdb") | |
| os.makedirs(os.path.dirname(pdb_path), exist_ok=True) | |
| new_cond_chains, new_binder_chains = convert_cif_to_pdb( | |
| cif_path=mmcif_path, | |
| out_pdb_path=pdb_path, | |
| binder_chains=binder_chains, | |
| ) | |
| if n_cond_chains is None: | |
| n_cond_chains = list(new_cond_chains) | |
| else: | |
| assert set(n_cond_chains) == set(new_cond_chains) | |
| if n_binder_chains is None: | |
| n_binder_chains = list(new_binder_chains) | |
| else: | |
| assert set(n_binder_chains) == set(new_binder_chains) | |
| logger.info( | |
| f"Found {len(valid_file_name_list)} valid files, cond chains: {n_cond_chains}, binder chains: {n_binder_chains}" | |
| ) | |
| input_data = { | |
| "task": "binder", | |
| "name": os.path.basename(data_dir), | |
| "pdb_dir": os.path.join(data_dir, "converted_pdbs"), | |
| "pdb_names": valid_file_name_list, | |
| "cond_chains": n_cond_chains, | |
| "binder_chains": n_binder_chains, | |
| "out_dir": dump_dir, | |
| } | |
| return input_data | |
| def prepare_tasks_from_pdb( | |
| data_dir: str, | |
| file_name_list: list, | |
| binder_chains: list[str], | |
| cond_chains: list[str], | |
| dump_dir: str, | |
| ): | |
| n_cond_chains = None | |
| valid_file_name_list = [] | |
| for file_name in file_name_list: | |
| pdb_path = os.path.join(data_dir, f"{file_name}.pdb") | |
| if not os.path.exists(pdb_path): | |
| logger.warning(f"Could not find {pdb_path}, skip!") | |
| continue | |
| else: | |
| valid_file_name_list.append(file_name) | |
| if cond_chains == [""]: | |
| chains = get_chains_from_pdb(pdb_path) | |
| new_cond_chains = chains - set(binder_chains) | |
| else: | |
| new_cond_chains = cond_chains | |
| if n_cond_chains is None: | |
| n_cond_chains = list(new_cond_chains) | |
| else: | |
| assert set(n_cond_chains) == set(new_cond_chains) | |
| return { | |
| "task": "binder", | |
| "pdb_dir": data_dir, | |
| "name": os.path.basename(data_dir), | |
| "pdb_names": valid_file_name_list, | |
| "cond_chains": n_cond_chains, | |
| "binder_chains": binder_chains, | |
| "out_dir": dump_dir, | |
| } | |
| def prepare_tasks_from_json(json_path): | |
| """ | |
| Prepare tasks from a JSON file. | |
| Args: | |
| json_path (str): Path to the JSON file. | |
| Returns: | |
| list: List of tasks. | |
| """ | |
| with open(json_path, "r") as f: | |
| tasks = json.load(f) | |
| if isinstance(tasks, dict): | |
| tasks = [tasks] | |
| return tasks | |
| class EvalRunner(object): | |
| def __init__(self, configs): | |
| self.configs = configs | |
| self.init_env() | |
| def init_env(self) -> None: | |
| self.print( | |
| f"Distributed environment: world size: {DIST_WRAPPER.world_size}, " | |
| + f"global rank: {DIST_WRAPPER.rank}, local rank: {DIST_WRAPPER.local_rank}" | |
| ) | |
| self.use_cuda = torch.cuda.device_count() > 0 | |
| if self.use_cuda: | |
| self.device = torch.device("cuda:{}".format(DIST_WRAPPER.local_rank)) | |
| os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" | |
| all_gpu_ids = ",".join(str(x) for x in range(torch.cuda.device_count())) | |
| devices = os.getenv("CUDA_VISIBLE_DEVICES", all_gpu_ids) | |
| logging.info( | |
| f"LOCAL_RANK: {DIST_WRAPPER.local_rank} - CUDA_VISIBLE_DEVICES: [{devices}]" | |
| ) | |
| torch.cuda.set_device(self.device) | |
| else: | |
| self.device = torch.device("cpu") | |
| if DIST_WRAPPER.world_size > 1: | |
| dist.init_process_group(backend="nccl") | |
| logging.info("Finished init ENV.") | |
| def print(self, msg: str): | |
| if DIST_WRAPPER.rank == 0: | |
| logger.info(msg) | |
| def run(self, input_data_list): | |
| for input_data in input_data_list: | |
| run_task( | |
| input_data, | |
| self.configs, | |
| device_id=DIST_WRAPPER.local_rank, | |
| seed=self.configs.seed, | |
| ) | |
| logging.info("Eval done!") | |
| def main(): | |
| # Configs | |
| config_dict = { | |
| "file_name_list": "", | |
| "json_path": "", | |
| "data_dir": "", | |
| "dump_dir": RequiredValue(str), | |
| "cond_chains": ListValue([""]), | |
| "binder_chains": ListValue([""]), # required | |
| "is_mmcif": False, | |
| "orig_seqs_json": "", | |
| "seed": 2025, | |
| **eval_configs, | |
| } | |
| configs = parse_configs(config_dict, arg_str=parse_sys_args()) | |
| # Prepare tasks | |
| if configs.json_path: | |
| logger.info("Prepare tasks from json file.") | |
| input_data_list = prepare_tasks_from_json(configs.json_path) | |
| if DIST_WRAPPER.world_size > 1: | |
| input_data_list = input_data_list[ | |
| DIST_WRAPPER.rank :: DIST_WRAPPER.world_size | |
| ] | |
| for input_data in input_data_list: | |
| input_data["out_dir"] += f"_rank{DIST_WRAPPER.rank}" | |
| else: | |
| if not configs.file_name_list: | |
| if configs.is_mmcif: | |
| fn_list = find_files_with_ext(configs.data_dir, "cif") | |
| else: | |
| fn_list = find_files_with_ext(configs.data_dir, "pdb") | |
| else: | |
| fn_list = get_file_name_list(configs.file_name_list) | |
| if DIST_WRAPPER.world_size > 1: | |
| fn_list = fn_list[DIST_WRAPPER.rank :: DIST_WRAPPER.world_size] | |
| if configs.is_mmcif: | |
| logger.info("Prepare tasks from mmCIF files.") | |
| input_data = prepare_tasks_from_mmcif( | |
| data_dir=configs.data_dir, | |
| file_name_list=fn_list, | |
| binder_chains=configs.binder_chains, | |
| cond_chains=configs.cond_chains, | |
| dump_dir=configs.dump_dir, | |
| ) | |
| else: | |
| logger.info("Prepare tasks from PDB files.") | |
| input_data = prepare_tasks_from_pdb( | |
| data_dir=configs.data_dir, | |
| file_name_list=fn_list, | |
| binder_chains=configs.binder_chains, | |
| cond_chains=configs.cond_chains, | |
| dump_dir=configs.dump_dir, | |
| ) | |
| if len(configs.orig_seqs_json) > 0: | |
| input_data["orig_seqs_json"] = configs.orig_seqs_json | |
| if DIST_WRAPPER.world_size > 1: | |
| input_data["out_dir"] += f"_rank{DIST_WRAPPER.rank}" | |
| input_data_list = [input_data] | |
| # Run task | |
| runner = EvalRunner(configs) | |
| runner.run(input_data_list) | |
| if __name__ == "__main__": | |
| main() | |