commit 4c2fdc395f45674759ca0e07fec3db1721641a1c Author: RYDE-WORK Date: Sun Jan 18 21:50:21 2026 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7dadb71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,191 @@ +# Data +/data/ + +# Mac OS-specific storage files +.DS_Store + +# vim +*.swp +*.swo + +## https://github.com/github/gitignore/blob/e8554d85bf62e38d6db966a50d2064ac025fd82a/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# MkDocs documentation +docs/site/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +#uv.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# pixi.lock should be committed to version control for reproducibility +# .pixi/ contains the environments and should not be committed +.pixi/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc \ No newline at end of file diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..a1ab80e --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,114 @@ +## Model Architecture + +1. Input(8 tokens): +``` +# chem token([B, 600]) +SMILES(string) + mpnn_encoder -> chem token + +# morgan token([B, 1024]), maccs token([B, 167]), rdkit token([B, 210]) +SMILES(string) + rdkit_encoder-> chem token, morgan token, maccs token, rdkit token + +# comp token([B, 5]) +Cationic_Lipid_to_mRNA_weight_ratio(float) +Cationic_Lipid_Mol_Ratio(float) +Phospholipid_Mol_Ratio(float) +Cholesterol_Mol_Ratio(float) +PEG_Lipid_Mol_Ratio(float) + +# phys token([B, 12]) +Purity_Pure(one-hot for Purity) +Purity_Crude(one-hot for Purity) +Mix_type_Microfluidic(one-hot for Mix_type) +Mix_type_Microfluidic(one-hot for Mix_type) +Cargo_type_mRNA(one-hot for Cargo_type) +Cargo_type_pDNA(one-hot for Cargo_type) +Cargo_type_siRNA(one-hot for Cargo_type) +Target_or_delivered_gene_FFL(one-hot for Target_or_delivered_gene) +Target_or_delivered_gene_Peptide_barcode(one-hot for Target_or_delivered_gene) +Target_or_delivered_gene_hEPO(one-hot for Target_or_delivered_gene) +Target_or_delivered_gene_FVII(one-hot for Target_or_delivered_gene) +Target_or_delivered_gene_GFP(one-hot for Target_or_delivered_gene) + +# help token([B, 4]) +Helper_lipid_ID_DOPE(one-hot for Helper_lipid_ID) +Helper_lipid_ID_DOTAP(one-hot for Helper_lipid_ID) +Helper_lipid_ID_DSPC(one-hot for Helper_lipid_ID) +Helper_lipid_ID_MDOA(one-hot for Helper_lipid_ID) + +# exp token([B, 32]) +Model_type_A549(one-hot for Model_type) +Model_type_BDMC(one-hot for Model_type) +Model_type_BMDM(one-hot for Model_type) +Model_type_HBEC_ALI(one-hot for Model_type) +Model_type_HEK293T(one-hot for Model_type) +Model_type_HeLa(one-hot for Model_type) +Model_type_IGROV1(one-hot for Model_type) +Model_type_Mouse(one-hot for Model_type) +Model_type_RAW264p7(one-hot for Model_type) +Delivery_target_dendritic_cell(one-hot for Delivery_target) +Delivery_target_generic_cell(one-hot for Delivery_target) +Delivery_target_liver(one-hot for Delivery_target) +Delivery_target_lung(one-hot for Delivery_target) +Delivery_target_lung_epithelium(one-hot for Delivery_target) +Delivery_target_macrophage(one-hot for Delivery_target) +Delivery_target_muscle(one-hot for Delivery_target) +Delivery_target_spleen(one-hot for Delivery_target) +Delivery_target_body(one-hot for Delivery_target) +Route_of_administration_in_vitro(one-hot for Route_of_administration) +Route_of_administration_intravenous(one-hot for Route_of_administration) +Route_of_administration_intramuscular(one-hot for Route_of_administration) +Route_of_administration_intratracheal(one-hot for Route_of_administration) +Sample_organization_type_individual(one-hot for Sample_organization_type) +Sample_organization_type_barcoded(one-hot for Sample_organization_type) +Value_name_log_luminescence(one-hot for Value_name) +Value_name_luminescence(one-hot for Value_name) +Value_name_FFL_silencing(one-hot for Value_name) +Value_name_Peptide_abundance(one-hot for Value_name) +Value_name_hEPO(one-hot for Value_name) +Value_name_FVII_silencing(one-hot for Value_name) +Value_name_GFP_delivery(one-hot for Value_name) +Value_name_Discretized_luminescence(one-hot for Value_name) +``` + +2. token projector + +3. Channel split: to A[chem, Morgan, Maccs, rdkit] and B[comp, pays, help, exp] + +4. Bi-directional cross attention + +5. Token re-compose: back to [chem, Morgan, maccs, rdkit, comp, physical, help, exp] + +6. Fusion layer(depends on user choice) + +7. heads: +``` +# regression head +size(float, training data already logged) + +# classification head +toxic(boolean, 0/1) + +# regression head +quantified_delivery(float, training data already z-scored) + +# classification head +PDI_0_0to0_2(one-hot classification for PDI) +PDI_0_2to0_3(one-hot classification for PDI) +PDI_0_3to0_4(one-hot classification for PDI) +PDI_0_4to0_5(one-hot classification for PDI) + +# classification head +Encapsulation_Efficiency_EE<50(one-hot classification for Encapsulation_Efficiency) +Encapsulation_Efficiency_50<=EE<80(one-hot classification for Encapsulation_Efficiency) +Encapsulation_Efficiency_80>> Pixi environment will be created when running 'make requirements'" + + @echo ">>> Activate with:\npixi shell" + + + + +################################################################################# +# PROJECT RULES # +################################################################################# + + +## Clean raw data (raw -> interim) +.PHONY: clean_data +clean_data: requirements + $(PYTHON_INTERPRETER) scripts/data_cleaning.py + +## Process dataset (interim -> processed) +.PHONY: data +data: requirements + $(PYTHON_INTERPRETER) scripts/process_data.py + +## Train model +.PHONY: train +train: requirements + $(PYTHON_INTERPRETER) -m lnp_ml.modeling.train + +## Train with hyperparameter tuning +.PHONY: tune +tune: requirements + $(PYTHON_INTERPRETER) -m lnp_ml.modeling.train --tune + +## Run predictions +.PHONY: predict +predict: requirements + $(PYTHON_INTERPRETER) -m lnp_ml.modeling.predict + +## Test model on test set (with detailed metrics) +.PHONY: test +test: requirements + $(PYTHON_INTERPRETER) -m lnp_ml.modeling.predict test + + +################################################################################# +# Self Documenting Commands # +################################################################################# + +.DEFAULT_GOAL := help + +define PRINT_HELP_PYSCRIPT +import re, sys; \ +lines = '\n'.join([line for line in sys.stdin]); \ +matches = re.findall(r'\n## (.*)\n[\s\S]+?\n([a-zA-Z_-]+):', lines); \ +print('Available rules:\n'); \ +print('\n'.join(['{:25}{}'.format(*reversed(match)) for match in matches])) +endef +export PRINT_HELP_PYSCRIPT + +help: + @$(PYTHON_INTERPRETER) -c "${PRINT_HELP_PYSCRIPT}" < $(MAKEFILE_LIST) diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ae6413 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# lnp-ml + + + + + +A short description of the project. + +## Project Organization + +``` +├── LICENSE <- Open-source license if one is chosen +├── Makefile <- Makefile with convenience commands like `make data` or `make train` +├── README.md <- The top-level README for developers using this project. +├── data +│ ├── external <- Data from third party sources. +│ ├── interim <- Intermediate data that has been transformed. +│ ├── processed <- The final, canonical data sets for modeling. +│ └── raw <- The original, immutable data dump. +│ +├── docs <- A default mkdocs project; see www.mkdocs.org for details +│ +├── models <- Trained and serialized models, model predictions, or model summaries +│ +├── notebooks <- Jupyter notebooks. Naming convention is a number (for ordering), +│ the creator's initials, and a short `-` delimited description, e.g. +│ `1.0-jqp-initial-data-exploration`. +│ +├── pyproject.toml <- Project configuration file with package metadata for +│ lnp_ml and configuration for tools like black +│ +├── references <- Data dictionaries, manuals, and all other explanatory materials. +│ +├── reports <- Generated analysis as HTML, PDF, LaTeX, etc. +│ └── figures <- Generated graphics and figures to be used in reporting +│ +├── requirements.txt <- The requirements file for reproducing the analysis environment, e.g. +│ generated with `pip freeze > requirements.txt` +│ +├── setup.cfg <- Configuration file for flake8 +│ +└── lnp_ml <- Source code for use in this project. + │ + ├── __init__.py <- Makes lnp_ml a Python module + │ + ├── config.py <- Store useful variables and configuration + │ + ├── dataset.py <- Scripts to download or generate data + │ + ├── features.py <- Code to create features for modeling + │ + ├── modeling + │ ├── __init__.py + │ ├── predict.py <- Code to run model inference with trained models + │ └── train.py <- Code to train models + │ + └── plots.py <- Code to create visualizations +``` + +-------- + diff --git a/cal_features.py b/cal_features.py new file mode 100644 index 0000000..124c451 --- /dev/null +++ b/cal_features.py @@ -0,0 +1,35 @@ +import logging +import numpy as np +import pandas as pd +import multiprocessing as mp +from typing import List +from tqdm import tqdm +from rdkit import Chem +from rdkit.Chem import ( + Mol, + AllChem, + MACCSkeys, + Descriptors +) + +mp.set_start_method('fork') # Screw MacOS +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + + +def get_morgan(mol: Mol, radius: int = 2, nBits: int = 1024) -> List[int]: + return AllChem.GetMorganFingerprintAsBitVect( + mol, + radius=radius, + nBits=nBits, + useChirality=False + ).ToList() + + +def get_maccs(mol: Mol) -> List[int]: + return MACCSkeys.GenMACCSKeys(mol).ToList() + + +def get_rdkit_descriptors(mol: Mol) -> List[float]: + desc_dict = Descriptors.CalcMolDescriptors(mol) + return list(desc_dict.values()) \ No newline at end of file diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lnp_ml/__init__.py b/lnp_ml/__init__.py new file mode 100644 index 0000000..349576f --- /dev/null +++ b/lnp_ml/__init__.py @@ -0,0 +1,4 @@ +# Lazy imports to avoid loading heavy dependencies (chemprop, rdkit) on every import +# Use explicit imports when needed: +# from lnp_ml.featurization import RDKitFeaturizer +# from lnp_ml.modeling import LNPModel diff --git a/lnp_ml/config.py b/lnp_ml/config.py new file mode 100644 index 0000000..8d77c22 --- /dev/null +++ b/lnp_ml/config.py @@ -0,0 +1,32 @@ +from pathlib import Path + +from dotenv import load_dotenv +from loguru import logger + +# Load environment variables from .env file if it exists +load_dotenv() + +# Paths +PROJ_ROOT = Path(__file__).resolve().parents[1] +logger.info(f"PROJ_ROOT path is: {PROJ_ROOT}") + +DATA_DIR = PROJ_ROOT / "data" +RAW_DATA_DIR = DATA_DIR / "raw" +INTERIM_DATA_DIR = DATA_DIR / "interim" +PROCESSED_DATA_DIR = DATA_DIR / "processed" +EXTERNAL_DATA_DIR = DATA_DIR / "external" + +MODELS_DIR = PROJ_ROOT / "models" + +REPORTS_DIR = PROJ_ROOT / "reports" +FIGURES_DIR = REPORTS_DIR / "figures" + +# If tqdm is installed, configure loguru with tqdm.write +# https://github.com/Delgan/loguru/issues/135 +try: + from tqdm import tqdm + + logger.remove(0) + logger.add(lambda msg: tqdm.write(msg, end=""), colorize=True) +except ModuleNotFoundError: + pass diff --git a/lnp_ml/dataset.py b/lnp_ml/dataset.py new file mode 100644 index 0000000..bb09e81 --- /dev/null +++ b/lnp_ml/dataset.py @@ -0,0 +1,348 @@ +"""数据集处理模块""" + +from pathlib import Path +from typing import Dict, List, Tuple, Optional +from dataclasses import dataclass + +import numpy as np +import pandas as pd +import torch +from torch.utils.data import Dataset + + +# ============ 列名配置 ============ + +# SMILES 列 +SMILES_COL = "smiles" + +# comp token: 配方比例 [5] +COMP_COLS = [ + "Cationic_Lipid_to_mRNA_weight_ratio", + "Cationic_Lipid_Mol_Ratio", + "Phospholipid_Mol_Ratio", + "Cholesterol_Mol_Ratio", + "PEG_Lipid_Mol_Ratio", +] + +# phys token: 物理/实验参数 one-hot [12] +# 需要从原始列生成 one-hot +PHYS_ONEHOT_SPECS = { + "Purity": ["Pure", "Crude"], + "Mix_type": ["Microfluidic", "Pipetting"], + "Cargo_type": ["mRNA", "pDNA", "siRNA"], + "Target_or_delivered_gene": ["FFL", "Peptide_barcode", "hEPO", "FVII", "GFP"], +} + +# help token: Helper lipid one-hot [4] +HELP_COLS = [ + "Helper_lipid_ID_DOPE", + "Helper_lipid_ID_DOTAP", + "Helper_lipid_ID_DSPC", + "Helper_lipid_ID_MDOA", +] + +# exp token: 实验条件 one-hot [32] +EXP_ONEHOT_SPECS = { + "Model_type": ["A549", "BDMC", "BMDM", "HBEC_ALI", "HEK293T", "HeLa", "IGROV1", "Mouse", "RAW264p7"], + "Delivery_target": ["body", "dendritic_cell", "generic_cell", "liver", "lung", "lung_epithelium", "macrophage", "muscle", "spleen"], + "Route_of_administration": ["in_vitro", "intramuscular", "intratracheal", "intravenous"], + "Batch_or_individual_or_barcoded": ["Barcoded", "Individual"], + "Value_name": ["log_luminescence", "luminescence", "FFL_silencing", "Peptide_abundance", "hEPO", "FVII_silencing", "GFP_delivery", "Discretized_luminescence"], +} + +# Target 列 +TARGET_REGRESSION = ["size", "quantified_delivery"] +TARGET_CLASSIFICATION_PDI = ["PDI_0_0to0_2", "PDI_0_2to0_3", "PDI_0_3to0_4", "PDI_0_4to0_5"] +TARGET_CLASSIFICATION_EE = ["Encapsulation_Efficiency_EE<50", "Encapsulation_Efficiency_50<=EE<80", "Encapsulation_Efficiency_80 List[str]: + """生成 one-hot 列名""" + return [f"{prefix}_{v}" for v in values] + + +def process_dataframe(df: pd.DataFrame) -> pd.DataFrame: + """ + 处理原始 DataFrame,生成模型所需的所有列。 + + Args: + df: 原始 DataFrame + + Returns: + 处理后的 DataFrame,包含所有需要的列 + """ + df = df.copy() + + # 1. 处理 phys token 的 one-hot 列(如果不存在则生成) + for col, values in PHYS_ONEHOT_SPECS.items(): + for v in values: + onehot_col = f"{col}_{v}" + if onehot_col not in df.columns: + if col in df.columns: + df[onehot_col] = (df[col] == v).astype(float) + else: + df[onehot_col] = 0.0 + + # 2. 处理 exp token 的 one-hot 列(如果不存在则生成) + for col, values in EXP_ONEHOT_SPECS.items(): + for v in values: + onehot_col = f"{col}_{v}" + if onehot_col not in df.columns: + if col in df.columns: + df[onehot_col] = (df[col] == v).astype(float) + else: + df[onehot_col] = 0.0 + + # 3. 确保 comp 列存在且为 float + for col in COMP_COLS: + if col in df.columns: + df[col] = pd.to_numeric(df[col], errors="coerce").fillna(0.0) + else: + df[col] = 0.0 + + # 4. 确保 help 列存在 + for col in HELP_COLS: + if col not in df.columns: + df[col] = 0.0 + else: + df[col] = df[col].fillna(0.0).astype(float) + + # 5. 处理 target 列 + # size: 已经 log 过,填充缺失值 + if "size" in df.columns: + df["size"] = pd.to_numeric(df["size"], errors="coerce") + + # quantified_delivery: 已经 z-score 过 + if "quantified_delivery" in df.columns: + df["quantified_delivery"] = pd.to_numeric(df["quantified_delivery"], errors="coerce") + + # toxic: 0/1 + if TARGET_TOXIC in df.columns: + df[TARGET_TOXIC] = pd.to_numeric(df[TARGET_TOXIC], errors="coerce").fillna(-1).astype(int) + + # PDI 和 EE 的 one-hot 分类 + for col in TARGET_CLASSIFICATION_PDI + TARGET_CLASSIFICATION_EE: + if col in df.columns: + df[col] = df[col].fillna(0).astype(float) + + # Biodistribution + for col in TARGET_BIODIST: + if col in df.columns: + df[col] = pd.to_numeric(df[col], errors="coerce").fillna(0.0) + + return df + + +def get_phys_cols() -> List[str]: + """获取 phys token 的所有列名""" + cols = [] + for col, values in PHYS_ONEHOT_SPECS.items(): + cols.extend(get_onehot_cols(col, values)) + return cols + + +def get_exp_cols() -> List[str]: + """获取 exp token 的所有列名""" + cols = [] + for col, values in EXP_ONEHOT_SPECS.items(): + cols.extend(get_onehot_cols(col, values)) + return cols + + +@dataclass +class LNPDatasetConfig: + """数据集配置""" + comp_cols: List[str] = None + phys_cols: List[str] = None + help_cols: List[str] = None + exp_cols: List[str] = None + + def __post_init__(self): + self.comp_cols = self.comp_cols or COMP_COLS + self.phys_cols = self.phys_cols or get_phys_cols() + self.help_cols = self.help_cols or HELP_COLS + self.exp_cols = self.exp_cols or get_exp_cols() + + +class LNPDataset(Dataset): + """ + LNP 数据集,用于 PyTorch DataLoader。 + + 返回: + - smiles: str + - tabular: Dict[str, Tensor] with keys "comp", "phys", "help", "exp" + - targets: Dict[str, Tensor] with keys "size", "pdi", "ee", "delivery", "biodist", "toxic" + - mask: Dict[str, Tensor] 标记哪些 target 有效(非缺失) + """ + + def __init__( + self, + df: pd.DataFrame, + config: Optional[LNPDatasetConfig] = None, + ): + self.config = config or LNPDatasetConfig() + self.df = process_dataframe(df) + + # 提取数据 + self.smiles = self.df[SMILES_COL].tolist() + + # Tabular features + self.comp = self.df[self.config.comp_cols].values.astype(np.float32) + self.phys = self.df[self.config.phys_cols].values.astype(np.float32) + self.help = self.df[self.config.help_cols].values.astype(np.float32) + self.exp = self.df[self.config.exp_cols].values.astype(np.float32) + + # Targets + self.size = self.df["size"].values.astype(np.float32) if "size" in self.df.columns else None + self.delivery = self.df["quantified_delivery"].values.astype(np.float32) if "quantified_delivery" in self.df.columns else None + self.toxic = self.df[TARGET_TOXIC].values.astype(np.int64) if TARGET_TOXIC in self.df.columns else None + + # PDI: one-hot -> class index + if all(col in self.df.columns for col in TARGET_CLASSIFICATION_PDI): + pdi_onehot = self.df[TARGET_CLASSIFICATION_PDI].values + self.pdi = np.argmax(pdi_onehot, axis=1).astype(np.int64) + self.pdi_valid = pdi_onehot.sum(axis=1) > 0 + else: + self.pdi = None + self.pdi_valid = None + + # EE: one-hot -> class index + if all(col in self.df.columns for col in TARGET_CLASSIFICATION_EE): + ee_onehot = self.df[TARGET_CLASSIFICATION_EE].values + self.ee = np.argmax(ee_onehot, axis=1).astype(np.int64) + self.ee_valid = ee_onehot.sum(axis=1) > 0 + else: + self.ee = None + self.ee_valid = None + + # Biodistribution + if all(col in self.df.columns for col in TARGET_BIODIST): + self.biodist = self.df[TARGET_BIODIST].values.astype(np.float32) + self.biodist_valid = self.biodist.sum(axis=1) > 0 + else: + self.biodist = None + self.biodist_valid = None + + def __len__(self) -> int: + return len(self.smiles) + + def __getitem__(self, idx: int) -> Dict: + item = { + "smiles": self.smiles[idx], + "tabular": { + "comp": torch.from_numpy(self.comp[idx]), + "phys": torch.from_numpy(self.phys[idx]), + "help": torch.from_numpy(self.help[idx]), + "exp": torch.from_numpy(self.exp[idx]), + }, + "targets": {}, + "mask": {}, + } + + # Targets and masks + if self.size is not None: + item["targets"]["size"] = torch.tensor(self.size[idx], dtype=torch.float32) + item["mask"]["size"] = torch.tensor(not np.isnan(self.size[idx]), dtype=torch.bool) + + if self.delivery is not None: + item["targets"]["delivery"] = torch.tensor(self.delivery[idx], dtype=torch.float32) + item["mask"]["delivery"] = torch.tensor(not np.isnan(self.delivery[idx]), dtype=torch.bool) + + if self.toxic is not None: + item["targets"]["toxic"] = torch.tensor(self.toxic[idx], dtype=torch.long) + item["mask"]["toxic"] = torch.tensor(self.toxic[idx] >= 0, dtype=torch.bool) + + if self.pdi is not None: + item["targets"]["pdi"] = torch.tensor(self.pdi[idx], dtype=torch.long) + item["mask"]["pdi"] = torch.tensor(self.pdi_valid[idx], dtype=torch.bool) + + if self.ee is not None: + item["targets"]["ee"] = torch.tensor(self.ee[idx], dtype=torch.long) + item["mask"]["ee"] = torch.tensor(self.ee_valid[idx], dtype=torch.bool) + + if self.biodist is not None: + item["targets"]["biodist"] = torch.from_numpy(self.biodist[idx]) + item["mask"]["biodist"] = torch.tensor(self.biodist_valid[idx], dtype=torch.bool) + + return item + + +def collate_fn(batch: List[Dict]) -> Dict: + """ + 自定义 collate 函数,用于 DataLoader。 + + Returns: + - smiles: List[str] + - tabular: Dict[str, Tensor] with batched tensors + - targets: Dict[str, Tensor] with batched tensors + - mask: Dict[str, Tensor] with batched masks + """ + smiles = [item["smiles"] for item in batch] + + tabular = { + key: torch.stack([item["tabular"][key] for item in batch]) + for key in batch[0]["tabular"].keys() + } + + targets = {} + mask = {} + for key in batch[0]["targets"].keys(): + targets[key] = torch.stack([item["targets"][key] for item in batch]) + mask[key] = torch.stack([item["mask"][key] for item in batch]) + + return { + "smiles": smiles, + "tabular": tabular, + "targets": targets, + "mask": mask, + } + + +def load_dataset( + path: Path, + train_ratio: float = 0.8, + val_ratio: float = 0.1, + seed: int = 42, +) -> Tuple[LNPDataset, LNPDataset, LNPDataset]: + """ + 加载并划分数据集。 + + Args: + path: CSV 文件路径 + train_ratio: 训练集比例 + val_ratio: 验证集比例 + seed: 随机种子 + + Returns: + (train_dataset, val_dataset, test_dataset) + """ + df = pd.read_csv(path) + + # 随机打乱 + df = df.sample(frac=1, random_state=seed).reset_index(drop=True) + + n = len(df) + n_train = int(n * train_ratio) + n_val = int(n * val_ratio) + + train_df = df.iloc[:n_train] + val_df = df.iloc[n_train:n_train + n_val] + test_df = df.iloc[n_train + n_val:] + + config = LNPDatasetConfig() + + return ( + LNPDataset(train_df, config), + LNPDataset(val_df, config), + LNPDataset(test_df, config), + ) diff --git a/lnp_ml/features.py b/lnp_ml/features.py new file mode 100644 index 0000000..f103db7 --- /dev/null +++ b/lnp_ml/features.py @@ -0,0 +1,29 @@ +from pathlib import Path + +from loguru import logger +from tqdm import tqdm +import typer + +from lnp_ml.config import PROCESSED_DATA_DIR + +app = typer.Typer() + + +@app.command() +def main( + # ---- REPLACE DEFAULT PATHS AS APPROPRIATE ---- + input_path: Path = PROCESSED_DATA_DIR / "dataset.csv", + output_path: Path = PROCESSED_DATA_DIR / "features.csv", + # ----------------------------------------- +): + # ---- REPLACE THIS WITH YOUR OWN CODE ---- + logger.info("Generating features from dataset...") + for i in tqdm(range(10), total=10): + if i == 5: + logger.info("Something happened for iteration 5.") + logger.success("Features generation complete.") + # ----------------------------------------- + + +if __name__ == "__main__": + app() diff --git a/lnp_ml/featurization/__init__.py b/lnp_ml/featurization/__init__.py new file mode 100644 index 0000000..8b42cde --- /dev/null +++ b/lnp_ml/featurization/__init__.py @@ -0,0 +1,4 @@ +from lnp_ml.featurization.smiles import RDKitFeaturizer + +__all__ = ["RDKitFeaturizer"] + diff --git a/lnp_ml/featurization/smiles.py b/lnp_ml/featurization/smiles.py new file mode 100644 index 0000000..64a8cef --- /dev/null +++ b/lnp_ml/featurization/smiles.py @@ -0,0 +1,156 @@ +"""SMILES 分子特征提取器""" + +from dataclasses import dataclass, field +from typing import List, Optional, Dict +import numpy as np + +# Suppress RDKit deprecation warnings +from rdkit import RDLogger +RDLogger.DisableLog("rdApp.*") + +from rdkit import Chem +from rdkit.Chem import AllChem, MACCSkeys, Descriptors + +import torch +from chemprop.utils import load_checkpoint +from chemprop.features import mol2graph + + +@dataclass +class RDKitFeaturizer: + """ + SMILES -> RDKit 特征向量,返回 Dict[str, np.ndarray]。 + """ + + morgan_radius: int = 2 + morgan_nbits: int = 1024 + + def transform(self, smiles_list: List[str]) -> Dict[str, np.ndarray]: + """SMILES 特征字典 -> value: (N, D_i) arrays""" + encoded = [self._encode_one(s) for s in smiles_list] + return { + "morgan": np.vstack([enc["morgan"] for enc in encoded]), + "maccs": np.vstack([enc["maccs"] for enc in encoded]), + "desc": np.vstack([enc["desc"] for enc in encoded]) + } + + def _encode_morgan(self, mol: Chem.Mol) -> np.ndarray: + return np.array(AllChem.GetMorganFingerprintAsBitVect( + mol, radius=self.morgan_radius, nBits=self.morgan_nbits + ).ToList(), dtype=np.float32) + + def _encode_maccs(self, mol: Chem.Mol) -> np.ndarray: + return np.array(MACCSkeys.GenMACCSKeys(mol).ToList(), dtype=np.float32) + + def _encode_desc(self, mol: Chem.Mol) -> np.ndarray: + return np.array(list(Descriptors.CalcMolDescriptors(mol).values()), dtype=np.float32) + + def _encode_one(self, smiles: str) -> Dict[str, np.ndarray]: + mol = Chem.MolFromSmiles(smiles) + if mol is None: + raise ValueError(f"Invalid SMILES: {smiles!r}") + + return { + "morgan": self._encode_morgan(mol), + "maccs": self._encode_maccs(mol), + "desc": self._encode_desc(mol) + } + + +@dataclass +class MPNNFeaturizer: + """ + SMILES -> D-MPNN 预训练特征向量 (N, hidden_size=600)。 + + 从训练好的 chemprop 模型中提取 D-MPNN 编码器的输出作为分子特征。 + + Args: + checkpoint_path: 模型检查点路径(.pt文件) + device: 计算设备 ("cpu" 或 "cuda") + ensemble_paths: 可选,多个模型路径列表用于集成(取平均) + """ + checkpoint_path: Optional[str] = None + device: str = "cpu" + ensemble_paths: Optional[List[str]] = None + + # 内部状态(不由用户设置) + _encoders: List = field(default_factory=list, init=False, repr=False) + _hidden_size: int = field(default=0, init=False, repr=False) + _initialized: bool = field(default=False, init=False, repr=False) + + def __post_init__(self) -> None: + """延迟初始化,在首次调用 transform 时加载模型""" + if self.checkpoint_path is None and self.ensemble_paths is None: + raise ValueError("必须提供 checkpoint_path 或 ensemble_paths") + + def _lazy_init(self) -> None: + """延迟加载模型,避免在创建对象时就加载大模型""" + if self._initialized: + return + + device = torch.device(self.device) + paths = self.ensemble_paths or [self.checkpoint_path] + + for path in paths: + model = load_checkpoint(path, device=device) + model.eval() + # 提取 MPNEncoder(D-MPNN 核心部分) + encoder = model.encoder.encoder[0] + # 冻结参数 + for param in encoder.parameters(): + param.requires_grad = False + self._encoders.append(encoder) + self._hidden_size = encoder.hidden_size + + self._initialized = True + + def transform(self, smiles_list: List[str]) -> Dict[str, np.ndarray]: + """ + SMILES 列表 -> tuple of (N, hidden_size) array + + Args: + smiles_list: SMILES 字符串列表 + + Returns: + tuple 包含一个形状为 (N, hidden_size) 的 numpy 数组 + 如果使用集成模型,返回所有模型输出的平均值 + """ + self._lazy_init() + + # 验证 SMILES 有效性 + for smi in smiles_list: + mol = Chem.MolFromSmiles(smi) + if mol is None: + raise ValueError(f"Invalid SMILES: {smi!r}") + + # 构建分子图(批量处理) + batch_mol_graph = mol2graph(smiles_list) + + # 从所有编码器提取特征 + all_features = [] + with torch.no_grad(): + for encoder in self._encoders: + features = encoder(batch_mol_graph) + all_features.append(features.cpu().numpy()) + + # 如果是集成模型,取平均 + if len(all_features) > 1: + features_array = np.mean(all_features, axis=0).astype(np.float32) + else: + features_array = all_features[0].astype(np.float32) + + return { + "mpnn": features_array + } + + @property + def hidden_size(self) -> int: + """返回特征维度""" + self._lazy_init() + return self._hidden_size + + @property + def n_models(self) -> int: + """返回集成模型数量""" + self._lazy_init() + return len(self._encoders) diff --git a/lnp_ml/modeling/__init__.py b/lnp_ml/modeling/__init__.py new file mode 100644 index 0000000..f1dc506 --- /dev/null +++ b/lnp_ml/modeling/__init__.py @@ -0,0 +1,11 @@ +from lnp_ml.modeling.models import LNPModel, LNPModelWithoutMPNN +from lnp_ml.modeling.heads import MultiTaskHead, RegressionHead, ClassificationHead, DistributionHead + +__all__ = [ + "LNPModel", + "LNPModelWithoutMPNN", + "MultiTaskHead", + "RegressionHead", + "ClassificationHead", + "DistributionHead", +] diff --git a/lnp_ml/modeling/encoders/__init__.py b/lnp_ml/modeling/encoders/__init__.py new file mode 100644 index 0000000..2ab762a --- /dev/null +++ b/lnp_ml/modeling/encoders/__init__.py @@ -0,0 +1,5 @@ +from lnp_ml.modeling.encoders.rdkit_encoder import CachedRDKitEncoder +from lnp_ml.modeling.encoders.mpnn_encoder import CachedMPNNEncoder + +__all__ = ["CachedRDKitEncoder", "CachedMPNNEncoder"] + diff --git a/lnp_ml/modeling/encoders/mpnn_encoder.py b/lnp_ml/modeling/encoders/mpnn_encoder.py new file mode 100644 index 0000000..db77a0e --- /dev/null +++ b/lnp_ml/modeling/encoders/mpnn_encoder.py @@ -0,0 +1,67 @@ +from typing import List, Optional, Dict + +import torch +import torch.nn as nn +import numpy as np + +from lnp_ml.featurization.smiles import MPNNFeaturizer + + +class CachedMPNNEncoder(nn.Module): + """ + 带内存缓存的 D-MPNN 特征提取模块。 + + - 使用预训练 chemprop 模型的 encoder 提取特征 + - 不可训练,不参与反向传播 + - 缓存已计算的 SMILES 特征,避免重复计算 + - forward 返回 Dict[str, Tensor],key: "mpnn" + """ + + def __init__( + self, + checkpoint_path: Optional[str] = None, + ensemble_paths: Optional[List[str]] = None, + device: str = "cpu", + ) -> None: + super().__init__() + self._featurizer = MPNNFeaturizer( + checkpoint_path=checkpoint_path, + ensemble_paths=ensemble_paths, + device=device, + ) + self._cache: Dict[str, np.ndarray] = {} + + def forward(self, smiles_list: List[str]) -> Dict[str, torch.Tensor]: + """ + SMILES 列表 -> Dict[str, Tensor] + + Returns: + {"mpnn": (N, hidden_size)} + """ + # 分离:已缓存 vs 需计算 + to_compute = [s for s in smiles_list if s not in self._cache] + + # 批量计算未缓存的 + if to_compute: + new_features = self._featurizer.transform(to_compute) + for idx, smiles in enumerate(to_compute): + self._cache[smiles] = new_features["mpnn"][idx] + + # 按原顺序组装结果 + return { + "mpnn": torch.from_numpy(np.stack([self._cache[s] for s in smiles_list])) + } + + def clear_cache(self) -> None: + """清空缓存""" + self._cache.clear() + + @property + def cache_size(self) -> int: + """当前缓存的 SMILES 数量""" + return len(self._cache) + + @property + def hidden_size(self) -> int: + """特征维度""" + return self._featurizer.hidden_size diff --git a/lnp_ml/modeling/encoders/rdkit_encoder.py b/lnp_ml/modeling/encoders/rdkit_encoder.py new file mode 100644 index 0000000..9cf4bc0 --- /dev/null +++ b/lnp_ml/modeling/encoders/rdkit_encoder.py @@ -0,0 +1,58 @@ +import torch +import torch.nn as nn +import numpy as np +from typing import List, Dict + +from lnp_ml.featurization.smiles import RDKitFeaturizer + + +class CachedRDKitEncoder(nn.Module): + """ + 带内存缓存的 RDKit 特征提取模块。 + + - 不可训练,不参与反向传播 + - 缓存已计算的 SMILES 特征,避免重复计算 + - forward 返回 Dict[str, Tensor],keys: "morgan", "maccs", "desc" + """ + + def __init__(self, morgan_radius: int = 2, morgan_nbits: int = 1024) -> None: + super().__init__() + self._featurizer = RDKitFeaturizer( + morgan_radius=morgan_radius, + morgan_nbits=morgan_nbits, + ) + self._cache: Dict[str, Dict[str, np.ndarray]] = {} + + def forward(self, smiles_list: List[str]) -> Dict[str, torch.Tensor]: + """ + SMILES 列表 -> Dict[str, Tensor] + + Returns: + {"morgan": (N, 1024), "maccs": (N, 167), "desc": (N, 210)} + """ + # 分离:已缓存 vs 需计算 + to_compute = [s for s in smiles_list if s not in self._cache] + + # 批量计算未缓存的 + if to_compute: + new_features = self._featurizer.transform(to_compute) + for idx, smiles in enumerate(to_compute): + self._cache[smiles] = { + k: new_features[k][idx] for k in new_features + } + + # 按原顺序组装结果 + keys = ["morgan", "maccs", "desc"] + return { + k: torch.from_numpy(np.stack([self._cache[s][k] for s in smiles_list])) + for k in keys + } + + def clear_cache(self) -> None: + """清空缓存""" + self._cache.clear() + + @property + def cache_size(self) -> int: + """当前缓存的 SMILES 数量""" + return len(self._cache) diff --git a/lnp_ml/modeling/encoders/tabular_encoder.py b/lnp_ml/modeling/encoders/tabular_encoder.py new file mode 100644 index 0000000..a1826a1 --- /dev/null +++ b/lnp_ml/modeling/encoders/tabular_encoder.py @@ -0,0 +1,24 @@ +import torch +import torch.nn as nn +from typing import Dict + + +class TabularEncoder(nn.Module): + + def __init__(self) -> None: + super().__init__() + + def forward(self, tabular_data: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: + # Input: Dict with keys 'comp', 'phys', 'help', 'exp' + # Each value is a tensor [B, D_i] where D_i is the feature dimension + # Output: Same dict (pass-through, features already grouped by DataLoader) + + # The DataLoader (trainer.py) already groups features correctly: + # - 'comp': [B, 9] - composition features + # - 'phys': [B, 9] - physical features (including processed PDI) + # - 'help': [B, 4] - helper lipid one-hot features + # - 'exp': [B, 20] - experimental condition one-hot features (including processed Purity) + + # Simply return the dict as-is + # If we wanted to add learned transformations, we could add linear layers here + return tabular_data diff --git a/lnp_ml/modeling/heads.py b/lnp_ml/modeling/heads.py new file mode 100644 index 0000000..b0cdb3d --- /dev/null +++ b/lnp_ml/modeling/heads.py @@ -0,0 +1,118 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from typing import Dict + + +class RegressionHead(nn.Module): + """回归任务头:输出单个 float 值""" + + def __init__(self, in_dim: int, hidden_dim: int = 128, dropout: float = 0.1) -> None: + super().__init__() + self.net = nn.Sequential( + nn.Linear(in_dim, hidden_dim), + nn.ReLU(), + nn.Dropout(dropout), + nn.Linear(hidden_dim, 1), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """[B, in_dim] -> [B, 1]""" + return self.net(x) + + +class ClassificationHead(nn.Module): + """分类任务头:输出 logits""" + + def __init__( + self, in_dim: int, num_classes: int, hidden_dim: int = 128, dropout: float = 0.1 + ) -> None: + super().__init__() + self.net = nn.Sequential( + nn.Linear(in_dim, hidden_dim), + nn.ReLU(), + nn.Dropout(dropout), + nn.Linear(hidden_dim, num_classes), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """[B, in_dim] -> [B, num_classes] (logits)""" + return self.net(x) + + +class DistributionHead(nn.Module): + """分布任务头:输出和为 1 的概率分布(用于 Biodistribution)""" + + def __init__( + self, in_dim: int, num_outputs: int, hidden_dim: int = 128, dropout: float = 0.1 + ) -> None: + super().__init__() + self.net = nn.Sequential( + nn.Linear(in_dim, hidden_dim), + nn.ReLU(), + nn.Dropout(dropout), + nn.Linear(hidden_dim, num_outputs), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """[B, in_dim] -> [B, num_outputs] (softmax, sum=1)""" + logits = self.net(x) + return F.softmax(logits, dim=-1) + + +class MultiTaskHead(nn.Module): + """ + 多任务预测头,根据任务配置自动创建对应的子头。 + + 输出: + - size: [B, 1] 回归 + - pdi: [B, 4] 分类 logits + - ee: [B, 3] 分类 logits + - delivery: [B, 1] 回归 + - biodist: [B, 7] softmax 分布 + - toxic: [B, 2] 二分类 logits + """ + + def __init__(self, in_dim: int, hidden_dim: int = 128, dropout: float = 0.1) -> None: + super().__init__() + + # size: 回归 (log-transformed) + self.size_head = RegressionHead(in_dim, hidden_dim, dropout) + + # PDI: 4 分类 + self.pdi_head = ClassificationHead(in_dim, num_classes=4, hidden_dim=hidden_dim, dropout=dropout) + + # Encapsulation Efficiency: 3 分类 + self.ee_head = ClassificationHead(in_dim, num_classes=3, hidden_dim=hidden_dim, dropout=dropout) + + # quantified_delivery: 回归 (z-scored) + self.delivery_head = RegressionHead(in_dim, hidden_dim, dropout) + + # Biodistribution: 7 输出,softmax (sum=1) + self.biodist_head = DistributionHead(in_dim, num_outputs=7, hidden_dim=hidden_dim, dropout=dropout) + + # toxic: 二分类 + self.toxic_head = ClassificationHead(in_dim, num_classes=2, hidden_dim=hidden_dim, dropout=dropout) + + def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]: + """ + Args: + x: [B, in_dim] fusion 层输出 + + Returns: + Dict with keys: + - "size": [B, 1] + - "pdi": [B, 4] logits + - "ee": [B, 3] logits + - "delivery": [B, 1] + - "biodist": [B, 7] probabilities (sum=1) + - "toxic": [B, 2] logits + """ + return { + "size": self.size_head(x), + "pdi": self.pdi_head(x), + "ee": self.ee_head(x), + "delivery": self.delivery_head(x), + "biodist": self.biodist_head(x), + "toxic": self.toxic_head(x), + } diff --git a/lnp_ml/modeling/layers/__init__.py b/lnp_ml/modeling/layers/__init__.py new file mode 100644 index 0000000..f019b23 --- /dev/null +++ b/lnp_ml/modeling/layers/__init__.py @@ -0,0 +1,6 @@ +from lnp_ml.modeling.layers.token_projector import TokenProjector +from lnp_ml.modeling.layers.bidirectional_cross_attention import CrossModalAttention +from lnp_ml.modeling.layers.fusion import FusionLayer + +__all__ = ["TokenProjector", "CrossModalAttention", "FusionLayer"] + diff --git a/lnp_ml/modeling/layers/bidirectional_cross_attention.py b/lnp_ml/modeling/layers/bidirectional_cross_attention.py new file mode 100644 index 0000000..cb918d3 --- /dev/null +++ b/lnp_ml/modeling/layers/bidirectional_cross_attention.py @@ -0,0 +1,124 @@ +import torch +import torch.nn as nn +from typing import Tuple + + +class CrossAttentionLayer(nn.Module): + """单层双向交叉注意力""" + + def __init__(self, d_model: int, num_heads: int, dropout: float = 0.1) -> None: + super().__init__() + assert d_model % num_heads == 0, "d_model must be divisible by num_heads" + + # A -> B: A as Q, B as K/V + self.cross_attn_a2b = nn.MultiheadAttention( + embed_dim=d_model, + num_heads=num_heads, + dropout=dropout, + batch_first=True, + ) + # B -> A: B as Q, A as K/V + self.cross_attn_b2a = nn.MultiheadAttention( + embed_dim=d_model, + num_heads=num_heads, + dropout=dropout, + batch_first=True, + ) + + # LayerNorm + FFN for channel A + self.norm_a1 = nn.LayerNorm(d_model) + self.norm_a2 = nn.LayerNorm(d_model) + self.ffn_a = nn.Sequential( + nn.Linear(d_model, d_model * 4), + nn.GELU(), + nn.Dropout(dropout), + nn.Linear(d_model * 4, d_model), + nn.Dropout(dropout), + ) + + # LayerNorm + FFN for channel B + self.norm_b1 = nn.LayerNorm(d_model) + self.norm_b2 = nn.LayerNorm(d_model) + self.ffn_b = nn.Sequential( + nn.Linear(d_model, d_model * 4), + nn.GELU(), + nn.Dropout(dropout), + nn.Linear(d_model * 4, d_model), + nn.Dropout(dropout), + ) + + def forward( + self, a: torch.Tensor, b: torch.Tensor + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Args: + a: [B, seq_len, d_model] + b: [B, seq_len, d_model] + + Returns: + (a_out, b_out): 更新后的两个 channel + """ + # Cross attention: A attends to B + a_attn, _ = self.cross_attn_a2b(query=a, key=b, value=b) + a = self.norm_a1(a + a_attn) + a = self.norm_a2(a + self.ffn_a(a)) + + # Cross attention: B attends to A + b_attn, _ = self.cross_attn_b2a(query=b, key=a, value=a) + b = self.norm_b1(b + b_attn) + b = self.norm_b2(b + self.ffn_b(b)) + + return a, b + + +class CrossModalAttention(nn.Module): + """ + 双向交叉注意力模块。 + + 输入 stacked tokens [B, 8, d_model],split 成两个 channel 后执行 + n_layers 层双向交叉注意力。 + """ + + def __init__( + self, + d_model: int, + num_heads: int, + n_layers: int, + split_idx: int = 4, + dropout: float = 0.1, + ) -> None: + """ + Args: + d_model: 特征维度 + num_heads: 注意力头数,d_head = d_model / num_heads + n_layers: 交叉注意力层数 + split_idx: channel split 的位置,默认 4 (0:4, 4:) + dropout: dropout 比例 + """ + super().__init__() + self.split_idx = split_idx + + self.layers = nn.ModuleList([ + CrossAttentionLayer(d_model, num_heads, dropout) + for _ in range(n_layers) + ]) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """ + Args: + x: [B, 8, d_model] stacked tokens + + Returns: + [B, 8, d_model] 融合后的 tokens + """ + # Split: [B, 8, d_model] -> [B, 4, d_model], [B, 4, d_model] + a = x[:, : self.split_idx, :] + b = x[:, self.split_idx :, :] + + # N layers of bidirectional cross attention + for layer in self.layers: + a, b = layer(a, b) + + # Concat back: [B, 8, d_model] + return torch.cat([a, b], dim=1) + diff --git a/lnp_ml/modeling/layers/fusion.py b/lnp_ml/modeling/layers/fusion.py new file mode 100644 index 0000000..dff583a --- /dev/null +++ b/lnp_ml/modeling/layers/fusion.py @@ -0,0 +1,99 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F +from typing import Dict, Literal, Union + + +PoolingStrategy = Literal["concat", "avg", "max", "attention"] + + +class FusionLayer(nn.Module): + """ + 将多个 token 融合成单个向量。 + + 输入: Dict[str, Tensor] 或 [B, n_tokens, d_model] + 输出: [B, fusion_dim] + + 策略: + - concat: [B, n_tokens, d_model] -> [B, n_tokens * d_model] + - avg: [B, n_tokens, d_model] -> [B, d_model] + - max: [B, n_tokens, d_model] -> [B, d_model] + - attention: [B, n_tokens, d_model] -> [B, d_model] (learnable attention pooling) + """ + + def __init__( + self, + d_model: int, + n_tokens: int, + strategy: PoolingStrategy = "attention", + ) -> None: + """ + Args: + d_model: 每个 token 的维度 + n_tokens: token 数量(如 8) + strategy: 融合策略 + """ + super().__init__() + self.d_model = d_model + self.n_tokens = n_tokens + self.strategy = strategy + + if strategy == "concat": + self.fusion_dim = n_tokens * d_model + else: + self.fusion_dim = d_model + + # Attention pooling: learnable query + if strategy == "attention": + self.attn_query = nn.Parameter(torch.randn(1, 1, d_model)) + self.attn_proj = nn.Linear(d_model, d_model) + + def forward(self, x: Union[Dict[str, torch.Tensor], torch.Tensor]) -> torch.Tensor: + """ + Args: + x: Dict[str, Tensor] 每个 [B, d_model],或已 stack 的 [B, n_tokens, d_model] + + Returns: + [B, fusion_dim] + """ + # 如果输入是 dict,先 stack + if isinstance(x, dict): + x = torch.stack(list(x.values()), dim=1) # [B, n_tokens, d_model] + + if self.strategy == "concat": + return x.flatten(start_dim=1) # [B, n_tokens * d_model] + + elif self.strategy == "avg": + return x.mean(dim=1) # [B, d_model] + + elif self.strategy == "max": + return x.max(dim=1).values # [B, d_model] + + elif self.strategy == "attention": + return self._attention_pooling(x) + + else: + raise ValueError(f"Unknown strategy: {self.strategy}") + + def _attention_pooling(self, x: torch.Tensor) -> torch.Tensor: + """ + Attention pooling: 用可学习 query 对 tokens 做加权求和 + + Args: + x: [B, n_tokens, d_model] + + Returns: + [B, d_model] + """ + B = x.size(0) + # query: [1, 1, d_model] -> [B, 1, d_model] + query = self.attn_query.expand(B, -1, -1) + + # Attention scores: [B, 1, n_tokens] + keys = self.attn_proj(x) # [B, n_tokens, d_model] + scores = torch.bmm(query, keys.transpose(1, 2)) / (self.d_model ** 0.5) + attn_weights = F.softmax(scores, dim=-1) # [B, 1, n_tokens] + + # Weighted sum: [B, 1, d_model] -> [B, d_model] + out = torch.bmm(attn_weights, x).squeeze(1) + return out diff --git a/lnp_ml/modeling/layers/token_projector.py b/lnp_ml/modeling/layers/token_projector.py new file mode 100644 index 0000000..a362787 --- /dev/null +++ b/lnp_ml/modeling/layers/token_projector.py @@ -0,0 +1,59 @@ +import torch +import torch.nn as nn +from typing import Dict + + +class TokenProjector(nn.Module): + """ + 将不同维度的特征投影到统一的 d_model 维度。 + + 每个特征分支的流程: + [B, input_dim_i] -> BN -> Linear -> [B, d_model] -> ReLU -> BN -> Dropout -> * sigmoid(weight_i) + """ + + def __init__( + self, + input_dims: Dict[str, int], + d_model: int, + dropout: float = 0.1, + ) -> None: + """ + Args: + input_dims: 各特征的输入维度,如 {"morgan": 1024, "maccs": 167, "desc": 210} + d_model: 统一的输出维度 + dropout: dropout 比例 + """ + super().__init__() + self.keys = list(input_dims.keys()) + + # 为每个特征分支创建投影层 + self.projectors = nn.ModuleDict() + for key, in_dim in input_dims.items(): + self.projectors[key] = nn.Sequential( + nn.BatchNorm1d(in_dim), + nn.Linear(in_dim, d_model), + nn.ReLU(), + nn.BatchNorm1d(d_model), + nn.Dropout(dropout), + ) + + # 每个分支的可学习权重(初始化为 0,sigmoid 后为 0.5) + self.weights = nn.ParameterDict({ + key: nn.Parameter(torch.zeros(1)) for key in self.keys + }) + + def forward(self, features: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]: + """ + Args: + features: Dict[str, Tensor],每个 tensor 形状为 (B, input_dim_i) + + Returns: + Dict[str, Tensor],每个 tensor 形状为 (B, d_model) + """ + out = {} + for key in self.keys: + x = self.projectors[key](features[key]) + w = torch.sigmoid(self.weights[key]) + out[key] = x * w + return out + diff --git a/lnp_ml/modeling/models.py b/lnp_ml/modeling/models.py new file mode 100644 index 0000000..b37d0ea --- /dev/null +++ b/lnp_ml/modeling/models.py @@ -0,0 +1,227 @@ +"""LNP 多任务预测模型""" + +import torch +import torch.nn as nn +from typing import Dict, List, Optional, Literal + +from lnp_ml.modeling.encoders import CachedRDKitEncoder, CachedMPNNEncoder +from lnp_ml.modeling.layers import TokenProjector, CrossModalAttention, FusionLayer +from lnp_ml.modeling.heads import MultiTaskHead + + +PoolingStrategy = Literal["concat", "avg", "max", "attention"] + + +# Token 维度配置(根据 ARCHITECTURE.md) +DEFAULT_INPUT_DIMS = { + # Channel A: 化学特征 + "mpnn": 600, # D-MPNN embedding + "morgan": 1024, # Morgan fingerprint + "maccs": 167, # MACCS keys + "desc": 210, # RDKit descriptors + # Channel B: 配方/实验条件 + "comp": 5, # 配方比例 + "phys": 12, # 物理参数 one-hot + "help": 4, # Helper lipid one-hot + "exp": 32, # 实验条件 one-hot +} + +# Token 顺序(前 4 个为 Channel A,后 4 个为 Channel B) +TOKEN_ORDER = ["mpnn", "morgan", "maccs", "desc", "comp", "phys", "help", "exp"] + + +class LNPModel(nn.Module): + """ + LNP 药物递送性能预测模型。 + + 架构流程: + 1. Encoders: SMILES -> 化学特征; tabular -> 配方/实验特征 + 2. TokenProjector: 统一到 d_model + 3. Stack: [B, 8, d_model] + 4. CrossModalAttention: Channel A (化学) <-> Channel B (配方/实验) + 5. FusionLayer: [B, 8, d_model] -> [B, fusion_dim] + 6. MultiTaskHead: 多任务预测 + """ + + def __init__( + self, + # 模型维度 + d_model: int = 256, + # Cross attention + num_heads: int = 8, + n_attn_layers: int = 4, + # Fusion + fusion_strategy: PoolingStrategy = "attention", + # Head + head_hidden_dim: int = 128, + # Dropout + dropout: float = 0.1, + # MPNN encoder (可选,如果不用 MPNN 可以设为 None) + mpnn_checkpoint: Optional[str] = None, + mpnn_ensemble_paths: Optional[List[str]] = None, + mpnn_device: str = "cpu", + # 输入维度配置 + input_dims: Optional[Dict[str, int]] = None, + ) -> None: + super().__init__() + + self.input_dims = input_dims or DEFAULT_INPUT_DIMS + self.d_model = d_model + self.use_mpnn = mpnn_checkpoint is not None or mpnn_ensemble_paths is not None + + # ============ Encoders ============ + # RDKit encoder (always used) + self.rdkit_encoder = CachedRDKitEncoder() + + # MPNN encoder (optional) + if self.use_mpnn: + self.mpnn_encoder = CachedMPNNEncoder( + checkpoint_path=mpnn_checkpoint, + ensemble_paths=mpnn_ensemble_paths, + device=mpnn_device, + ) + else: + self.mpnn_encoder = None + + # ============ Token Projector ============ + # 根据是否使用 MPNN 调整输入维度 + proj_input_dims = {k: v for k, v in self.input_dims.items()} + if not self.use_mpnn: + proj_input_dims.pop("mpnn", None) + + self.token_projector = TokenProjector( + input_dims=proj_input_dims, + d_model=d_model, + dropout=dropout, + ) + + # ============ Cross Modal Attention ============ + n_tokens = 8 if self.use_mpnn else 7 + split_idx = 4 if self.use_mpnn else 3 # Channel A 的 token 数量 + + self.cross_attention = CrossModalAttention( + d_model=d_model, + num_heads=num_heads, + n_layers=n_attn_layers, + split_idx=split_idx, + dropout=dropout, + ) + + # ============ Fusion Layer ============ + self.fusion = FusionLayer( + d_model=d_model, + n_tokens=n_tokens, + strategy=fusion_strategy, + ) + + # ============ Multi-Task Head ============ + self.head = MultiTaskHead( + in_dim=self.fusion.fusion_dim, + hidden_dim=head_hidden_dim, + dropout=dropout, + ) + + def forward( + self, + smiles: List[str], + tabular: Dict[str, torch.Tensor], + ) -> Dict[str, torch.Tensor]: + """ + Args: + smiles: SMILES 字符串列表,长度为 B + tabular: Dict[str, Tensor],包含: + - "comp": [B, 5] 配方比例 + - "phys": [B, 12] 物理参数 + - "help": [B, 4] Helper lipid + - "exp": [B, 32] 实验条件 + + Returns: + Dict[str, Tensor]: + - "size": [B, 1] + - "pdi": [B, 4] + - "ee": [B, 3] + - "delivery": [B, 1] + - "biodist": [B, 7] + - "toxic": [B, 2] + """ + # 1. Encode SMILES + rdkit_features = self.rdkit_encoder(smiles) # {"morgan", "maccs", "desc"} + + # 2. 合并所有特征 + all_features: Dict[str, torch.Tensor] = {} + + # MPNN 特征(如果启用) + if self.use_mpnn: + mpnn_features = self.mpnn_encoder(smiles) + all_features["mpnn"] = mpnn_features["mpnn"] + + # RDKit 特征 + all_features["morgan"] = rdkit_features["morgan"] + all_features["maccs"] = rdkit_features["maccs"] + all_features["desc"] = rdkit_features["desc"] + + # Tabular 特征 + all_features["comp"] = tabular["comp"] + all_features["phys"] = tabular["phys"] + all_features["help"] = tabular["help"] + all_features["exp"] = tabular["exp"] + + # 3. Token Projector: 统一维度 + projected = self.token_projector(all_features) # Dict[str, [B, d_model]] + + # 4. Stack tokens: [B, n_tokens, d_model] + # 按顺序 stack:Channel A (化学) + Channel B (配方/实验) + if self.use_mpnn: + token_order = ["mpnn", "morgan", "maccs", "desc", "comp", "phys", "help", "exp"] + else: + token_order = ["morgan", "maccs", "desc", "comp", "phys", "help", "exp"] + + stacked = torch.stack([projected[k] for k in token_order], dim=1) + + # 5. Cross Modal Attention + attended = self.cross_attention(stacked) + + # 6. Fusion + fused = self.fusion(attended) + + # 7. Multi-Task Head + outputs = self.head(fused) + + return outputs + + def clear_cache(self) -> None: + """清空所有 encoder 的缓存""" + self.rdkit_encoder.clear_cache() + if self.mpnn_encoder is not None: + self.mpnn_encoder.clear_cache() + + +class LNPModelWithoutMPNN(LNPModel): + """不使用 MPNN 的简化版本""" + + def __init__( + self, + d_model: int = 256, + num_heads: int = 8, + n_attn_layers: int = 4, + fusion_strategy: PoolingStrategy = "attention", + head_hidden_dim: int = 128, + dropout: float = 0.1, + input_dims: Optional[Dict[str, int]] = None, + ) -> None: + # 移除 mpnn 维度 + dims = input_dims or DEFAULT_INPUT_DIMS.copy() + dims.pop("mpnn", None) + + super().__init__( + d_model=d_model, + num_heads=num_heads, + n_attn_layers=n_attn_layers, + fusion_strategy=fusion_strategy, + head_hidden_dim=head_hidden_dim, + dropout=dropout, + mpnn_checkpoint=None, + mpnn_ensemble_paths=None, + input_dims=dims, + ) + diff --git a/lnp_ml/modeling/predict.py b/lnp_ml/modeling/predict.py new file mode 100644 index 0000000..f3df290 --- /dev/null +++ b/lnp_ml/modeling/predict.py @@ -0,0 +1,313 @@ +"""预测脚本:使用训练好的模型进行推理""" + +from pathlib import Path +from typing import Dict, List + +import pandas as pd +import torch +from torch.utils.data import DataLoader +from loguru import logger +import typer + +from lnp_ml.config import MODELS_DIR, PROCESSED_DATA_DIR +from lnp_ml.dataset import LNPDataset, collate_fn +from lnp_ml.modeling.models import LNPModelWithoutMPNN + + +app = typer.Typer() + + +def load_model(model_path: Path, device: torch.device) -> LNPModelWithoutMPNN: + """加载训练好的模型""" + checkpoint = torch.load(model_path, map_location=device) + config = checkpoint["config"] + + model = LNPModelWithoutMPNN( + d_model=config["d_model"], + num_heads=config["num_heads"], + n_attn_layers=config["n_attn_layers"], + fusion_strategy=config["fusion_strategy"], + head_hidden_dim=config["head_hidden_dim"], + dropout=config["dropout"], + ) + model.load_state_dict(checkpoint["model_state_dict"]) + model.to(device) + model.eval() + + logger.info(f"Loaded model from {model_path}") + logger.info(f"Model config: {config}") + logger.info(f"Best val_loss: {checkpoint.get('best_val_loss', 'N/A')}") + + return model + + +@torch.no_grad() +def predict_batch( + model: LNPModelWithoutMPNN, + loader: DataLoader, + device: torch.device, +) -> Dict[str, List]: + """对整个数据集进行预测""" + model.eval() + + all_preds = { + "size": [], + "pdi": [], + "ee": [], + "delivery": [], + "biodist": [], + "toxic": [], + } + all_smiles = [] + + for batch in loader: + smiles = batch["smiles"] + tabular = {k: v.to(device) for k, v in batch["tabular"].items()} + + outputs = model(smiles, tabular) + + all_smiles.extend(smiles) + + # 回归任务 + all_preds["size"].extend(outputs["size"].squeeze(-1).cpu().tolist()) + all_preds["delivery"].extend(outputs["delivery"].squeeze(-1).cpu().tolist()) + + # 分类任务:取 argmax + all_preds["pdi"].extend(outputs["pdi"].argmax(dim=-1).cpu().tolist()) + all_preds["ee"].extend(outputs["ee"].argmax(dim=-1).cpu().tolist()) + all_preds["toxic"].extend(outputs["toxic"].argmax(dim=-1).cpu().tolist()) + + # 分布任务 + all_preds["biodist"].extend(outputs["biodist"].cpu().tolist()) + + return {"smiles": all_smiles, **all_preds} + + +def predictions_to_dataframe(predictions: Dict) -> pd.DataFrame: + """将预测结果转换为 DataFrame""" + # 基本列 + df = pd.DataFrame({ + "smiles": predictions["smiles"], + "pred_size": predictions["size"], + "pred_delivery": predictions["delivery"], + "pred_pdi_class": predictions["pdi"], + "pred_ee_class": predictions["ee"], + "pred_toxic": predictions["toxic"], + }) + + # PDI 类别映射 + pdi_labels = ["0_0to0_2", "0_2to0_3", "0_3to0_4", "0_4to0_5"] + df["pred_pdi_label"] = df["pred_pdi_class"].map(lambda x: pdi_labels[x]) + + # EE 类别映射 + ee_labels = ["EE<50", "50<=EE<80", "80 0 + if mask.any(): + y_true = pdi_true[mask] + y_pred = np.array(predictions["pdi"])[mask] + results["detailed_metrics"]["pdi"] = { + "accuracy": float(accuracy_score(y_true, y_pred)), + } + + # 分类指标:EE + ee_cols = ["Encapsulation_Efficiency_EE<50", "Encapsulation_Efficiency_50<=EE<80", "Encapsulation_Efficiency_80 0 + if mask.any(): + y_true = ee_true[mask] + y_pred = np.array(predictions["ee"])[mask] + results["detailed_metrics"]["ee"] = { + "accuracy": float(accuracy_score(y_true, y_pred)), + } + + # 分类指标:toxic + if "toxic" in test_df.columns: + mask = test_df["toxic"].notna() & (test_df["toxic"] >= 0) + if mask.any(): + y_true = test_df.loc[mask, "toxic"].astype(int).values + y_pred = np.array(predictions["toxic"])[mask.values] + results["detailed_metrics"]["toxic"] = { + "accuracy": float(accuracy_score(y_true, y_pred)), + } + + # 打印结果 + logger.info("\n" + "=" * 50) + logger.info("TEST RESULTS") + logger.info("=" * 50) + + logger.info("\n[Loss Metrics]") + for k, v in loss_metrics.items(): + logger.info(f" {k}: {v:.4f}") + + logger.info("\n[Detailed Metrics]") + for task, metrics in results["detailed_metrics"].items(): + logger.info(f"\n {task}:") + for k, v in metrics.items(): + logger.info(f" {k}: {v:.4f}") + + # 保存结果 + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, "w") as f: + json.dump(results, f, indent=2) + logger.success(f"\nSaved test results to {output_path}") + + +# 保留旧的 evaluate 作为 test 的别名 +@app.command() +def evaluate( + test_path: Path = PROCESSED_DATA_DIR / "test.parquet", + model_path: Path = MODELS_DIR / "model.pt", + batch_size: int = 64, + device: str = "cuda" if torch.cuda.is_available() else "cpu", +): + """ + [已废弃] 请使用 'test' 命令。 + """ + test(test_path, model_path, PROCESSED_DATA_DIR / "test_results.json", batch_size, device) + + +if __name__ == "__main__": + app() diff --git a/lnp_ml/modeling/train.py b/lnp_ml/modeling/train.py new file mode 100644 index 0000000..a447993 --- /dev/null +++ b/lnp_ml/modeling/train.py @@ -0,0 +1,315 @@ +"""训练脚本:支持超参数调优""" + +import json +from pathlib import Path +from typing import Optional + +import pandas as pd +import torch +from torch.utils.data import DataLoader +from loguru import logger +import typer + +from lnp_ml.config import MODELS_DIR, PROCESSED_DATA_DIR +from lnp_ml.dataset import LNPDataset, collate_fn +from lnp_ml.modeling.models import LNPModelWithoutMPNN +from lnp_ml.modeling.trainer import ( + train_epoch, + validate, + EarlyStopping, + LossWeights, +) + + +app = typer.Typer() + + +def create_model( + d_model: int = 256, + num_heads: int = 8, + n_attn_layers: int = 4, + fusion_strategy: str = "attention", + head_hidden_dim: int = 128, + dropout: float = 0.1, +) -> LNPModelWithoutMPNN: + """创建模型""" + return LNPModelWithoutMPNN( + d_model=d_model, + num_heads=num_heads, + n_attn_layers=n_attn_layers, + fusion_strategy=fusion_strategy, + head_hidden_dim=head_hidden_dim, + dropout=dropout, + ) + + +def train_model( + train_loader: DataLoader, + val_loader: DataLoader, + model: torch.nn.Module, + device: torch.device, + lr: float = 1e-4, + weight_decay: float = 1e-5, + epochs: int = 100, + patience: int = 15, + loss_weights: Optional[LossWeights] = None, +) -> dict: + """ + 训练模型。 + + Returns: + 训练历史和最佳验证损失 + """ + model = model.to(device) + optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=weight_decay) + scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( + optimizer, mode="min", factor=0.5, patience=5, verbose=True + ) + early_stopping = EarlyStopping(patience=patience) + + history = {"train": [], "val": []} + best_val_loss = float("inf") + best_state = None + + for epoch in range(epochs): + # Train + train_metrics = train_epoch(model, train_loader, optimizer, device, loss_weights) + + # Validate + val_metrics = validate(model, val_loader, device, loss_weights) + + # Log + logger.info( + f"Epoch {epoch+1}/{epochs} | " + f"Train Loss: {train_metrics['loss']:.4f} | " + f"Val Loss: {val_metrics['loss']:.4f}" + ) + + history["train"].append(train_metrics) + history["val"].append(val_metrics) + + # Learning rate scheduling + scheduler.step(val_metrics["loss"]) + + # Save best model + if val_metrics["loss"] < best_val_loss: + best_val_loss = val_metrics["loss"] + best_state = {k: v.cpu().clone() for k, v in model.state_dict().items()} + logger.info(f" -> New best model (val_loss={best_val_loss:.4f})") + + # Early stopping + if early_stopping(val_metrics["loss"]): + logger.info(f"Early stopping at epoch {epoch+1}") + break + + # Restore best model + if best_state is not None: + model.load_state_dict(best_state) + + return { + "history": history, + "best_val_loss": best_val_loss, + } + + +def run_hyperparameter_tuning( + train_loader: DataLoader, + val_loader: DataLoader, + device: torch.device, + n_trials: int = 20, + epochs_per_trial: int = 30, +) -> dict: + """ + 使用 Optuna 进行超参数调优。 + + Returns: + 最佳超参数 + """ + try: + import optuna + except ImportError: + logger.error("Optuna not installed. Run: pip install optuna") + raise + + def objective(trial: optuna.Trial) -> float: + # 采样超参数 + d_model = trial.suggest_categorical("d_model", [128, 256, 512]) + num_heads = trial.suggest_categorical("num_heads", [4, 8]) + n_attn_layers = trial.suggest_int("n_attn_layers", 2, 6) + fusion_strategy = trial.suggest_categorical( + "fusion_strategy", ["attention", "avg", "max"] + ) + head_hidden_dim = trial.suggest_categorical("head_hidden_dim", [64, 128, 256]) + dropout = trial.suggest_float("dropout", 0.05, 0.3) + lr = trial.suggest_float("lr", 1e-5, 1e-3, log=True) + weight_decay = trial.suggest_float("weight_decay", 1e-6, 1e-4, log=True) + + # 创建模型 + model = create_model( + d_model=d_model, + num_heads=num_heads, + n_attn_layers=n_attn_layers, + fusion_strategy=fusion_strategy, + head_hidden_dim=head_hidden_dim, + dropout=dropout, + ) + + # 训练 + result = train_model( + train_loader=train_loader, + val_loader=val_loader, + model=model, + device=device, + lr=lr, + weight_decay=weight_decay, + epochs=epochs_per_trial, + patience=10, + ) + + return result["best_val_loss"] + + # 运行优化 + study = optuna.create_study(direction="minimize") + study.optimize(objective, n_trials=n_trials, show_progress_bar=True) + + logger.info(f"Best trial: {study.best_trial.number}") + logger.info(f"Best val_loss: {study.best_trial.value:.4f}") + logger.info(f"Best params: {study.best_trial.params}") + + return study.best_trial.params + + +@app.command() +def main( + train_path: Path = PROCESSED_DATA_DIR / "train.parquet", + val_path: Path = PROCESSED_DATA_DIR / "val.parquet", + output_dir: Path = MODELS_DIR, + # 模型参数 + d_model: int = 256, + num_heads: int = 8, + n_attn_layers: int = 4, + fusion_strategy: str = "attention", + head_hidden_dim: int = 128, + dropout: float = 0.1, + # 训练参数 + batch_size: int = 32, + lr: float = 1e-4, + weight_decay: float = 1e-5, + epochs: int = 100, + patience: int = 15, + # 超参数调优 + tune: bool = False, + n_trials: int = 20, + epochs_per_trial: int = 30, + # 设备 + device: str = "cuda" if torch.cuda.is_available() else "cpu", +): + """ + 训练 LNP 预测模型。 + + 使用 --tune 启用超参数调优。 + """ + logger.info(f"Using device: {device}") + device = torch.device(device) + + # 加载数据 + logger.info(f"Loading train data from {train_path}") + train_df = pd.read_parquet(train_path) + train_dataset = LNPDataset(train_df) + train_loader = DataLoader( + train_dataset, batch_size=batch_size, shuffle=True, collate_fn=collate_fn + ) + + logger.info(f"Loading val data from {val_path}") + val_df = pd.read_parquet(val_path) + val_dataset = LNPDataset(val_df) + val_loader = DataLoader( + val_dataset, batch_size=batch_size, shuffle=False, collate_fn=collate_fn + ) + + logger.info(f"Train samples: {len(train_dataset)}, Val samples: {len(val_dataset)}") + + output_dir.mkdir(parents=True, exist_ok=True) + + # 超参数调优 + if tune: + logger.info(f"Starting hyperparameter tuning with {n_trials} trials...") + best_params = run_hyperparameter_tuning( + train_loader=train_loader, + val_loader=val_loader, + device=device, + n_trials=n_trials, + epochs_per_trial=epochs_per_trial, + ) + + # 保存最佳参数 + params_path = output_dir / "best_params.json" + with open(params_path, "w") as f: + json.dump(best_params, f, indent=2) + logger.success(f"Saved best params to {params_path}") + + # 使用最佳参数重新训练 + d_model = best_params["d_model"] + num_heads = best_params["num_heads"] + n_attn_layers = best_params["n_attn_layers"] + fusion_strategy = best_params["fusion_strategy"] + head_hidden_dim = best_params["head_hidden_dim"] + dropout = best_params["dropout"] + lr = best_params["lr"] + weight_decay = best_params["weight_decay"] + + # 创建模型 + logger.info("Creating model...") + model = create_model( + d_model=d_model, + num_heads=num_heads, + n_attn_layers=n_attn_layers, + fusion_strategy=fusion_strategy, + head_hidden_dim=head_hidden_dim, + dropout=dropout, + ) + + # 打印模型信息 + n_params = sum(p.numel() for p in model.parameters() if p.requires_grad) + logger.info(f"Model parameters: {n_params:,}") + + # 训练 + logger.info("Starting training...") + result = train_model( + train_loader=train_loader, + val_loader=val_loader, + model=model, + device=device, + lr=lr, + weight_decay=weight_decay, + epochs=epochs, + patience=patience, + ) + + # 保存模型 + model_path = output_dir / "model.pt" + torch.save({ + "model_state_dict": model.state_dict(), + "config": { + "d_model": d_model, + "num_heads": num_heads, + "n_attn_layers": n_attn_layers, + "fusion_strategy": fusion_strategy, + "head_hidden_dim": head_hidden_dim, + "dropout": dropout, + }, + "best_val_loss": result["best_val_loss"], + }, model_path) + logger.success(f"Saved model to {model_path}") + + # 保存训练历史 + history_path = output_dir / "history.json" + with open(history_path, "w") as f: + json.dump(result["history"], f, indent=2) + logger.success(f"Saved training history to {history_path}") + + logger.success(f"Training complete! Best val_loss: {result['best_val_loss']:.4f}") + + +if __name__ == "__main__": + app() diff --git a/lnp_ml/modeling/trainer.py b/lnp_ml/modeling/trainer.py new file mode 100644 index 0000000..aa2c3cd --- /dev/null +++ b/lnp_ml/modeling/trainer.py @@ -0,0 +1,212 @@ +"""训练器:封装训练、验证、损失计算逻辑""" + +from typing import Dict, Optional, Tuple +from dataclasses import dataclass + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +from tqdm import tqdm + + +@dataclass +class LossWeights: + """各任务的损失权重""" + size: float = 1.0 + pdi: float = 1.0 + ee: float = 1.0 + delivery: float = 1.0 + biodist: float = 1.0 + toxic: float = 1.0 + + +def compute_multitask_loss( + outputs: Dict[str, torch.Tensor], + targets: Dict[str, torch.Tensor], + mask: Dict[str, torch.Tensor], + weights: Optional[LossWeights] = None, +) -> Tuple[torch.Tensor, Dict[str, torch.Tensor]]: + """ + 计算多任务损失。 + + Args: + outputs: 模型输出 + targets: 真实标签 + mask: 有效样本掩码 + weights: 各任务权重 + + Returns: + (total_loss, loss_dict) 总损失和各任务损失 + """ + weights = weights or LossWeights() + losses = {} + total_loss = torch.tensor(0.0, device=next(iter(outputs.values())).device) + + # size: MSE loss + if "size" in targets and mask["size"].any(): + m = mask["size"] + pred = outputs["size"][m].squeeze(-1) + tgt = targets["size"][m] + losses["size"] = F.mse_loss(pred, tgt) + total_loss = total_loss + weights.size * losses["size"] + + # delivery: MSE loss + if "delivery" in targets and mask["delivery"].any(): + m = mask["delivery"] + pred = outputs["delivery"][m].squeeze(-1) + tgt = targets["delivery"][m] + losses["delivery"] = F.mse_loss(pred, tgt) + total_loss = total_loss + weights.delivery * losses["delivery"] + + # pdi: CrossEntropy + if "pdi" in targets and mask["pdi"].any(): + m = mask["pdi"] + pred = outputs["pdi"][m] + tgt = targets["pdi"][m] + losses["pdi"] = F.cross_entropy(pred, tgt) + total_loss = total_loss + weights.pdi * losses["pdi"] + + # ee: CrossEntropy + if "ee" in targets and mask["ee"].any(): + m = mask["ee"] + pred = outputs["ee"][m] + tgt = targets["ee"][m] + losses["ee"] = F.cross_entropy(pred, tgt) + total_loss = total_loss + weights.ee * losses["ee"] + + # toxic: CrossEntropy + if "toxic" in targets and mask["toxic"].any(): + m = mask["toxic"] + pred = outputs["toxic"][m] + tgt = targets["toxic"][m] + losses["toxic"] = F.cross_entropy(pred, tgt) + total_loss = total_loss + weights.toxic * losses["toxic"] + + # biodist: KL divergence + if "biodist" in targets and mask["biodist"].any(): + m = mask["biodist"] + pred = outputs["biodist"][m] + tgt = targets["biodist"][m] + # KL divergence: KL(target || pred) + losses["biodist"] = F.kl_div( + pred.log().clamp(min=-100), + tgt, + reduction="batchmean", + ) + total_loss = total_loss + weights.biodist * losses["biodist"] + + return total_loss, losses + + +def train_epoch( + model: nn.Module, + loader: DataLoader, + optimizer: torch.optim.Optimizer, + device: torch.device, + weights: Optional[LossWeights] = None, +) -> Dict[str, float]: + """训练一个 epoch""" + model.train() + total_loss = 0.0 + task_losses = {k: 0.0 for k in ["size", "pdi", "ee", "delivery", "biodist", "toxic"]} + n_batches = 0 + + for batch in tqdm(loader, desc="Training", leave=False): + smiles = batch["smiles"] + tabular = {k: v.to(device) for k, v in batch["tabular"].items()} + targets = {k: v.to(device) for k, v in batch["targets"].items()} + mask = {k: v.to(device) for k, v in batch["mask"].items()} + + optimizer.zero_grad() + outputs = model(smiles, tabular) + loss, losses = compute_multitask_loss(outputs, targets, mask, weights) + + loss.backward() + torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) + optimizer.step() + + total_loss += loss.item() + for k, v in losses.items(): + task_losses[k] += v.item() + n_batches += 1 + + return { + "loss": total_loss / n_batches, + **{f"loss_{k}": v / n_batches for k, v in task_losses.items() if v > 0}, + } + + +@torch.no_grad() +def validate( + model: nn.Module, + loader: DataLoader, + device: torch.device, + weights: Optional[LossWeights] = None, +) -> Dict[str, float]: + """验证""" + model.eval() + total_loss = 0.0 + task_losses = {k: 0.0 for k in ["size", "pdi", "ee", "delivery", "biodist", "toxic"]} + n_batches = 0 + + # 用于计算准确率 + correct = {k: 0 for k in ["pdi", "ee", "toxic"]} + total = {k: 0 for k in ["pdi", "ee", "toxic"]} + + for batch in tqdm(loader, desc="Validating", leave=False): + smiles = batch["smiles"] + tabular = {k: v.to(device) for k, v in batch["tabular"].items()} + targets = {k: v.to(device) for k, v in batch["targets"].items()} + mask = {k: v.to(device) for k, v in batch["mask"].items()} + + outputs = model(smiles, tabular) + loss, losses = compute_multitask_loss(outputs, targets, mask, weights) + + total_loss += loss.item() + for k, v in losses.items(): + task_losses[k] += v.item() + n_batches += 1 + + # 计算分类准确率 + for k in ["pdi", "ee", "toxic"]: + if k in targets and mask[k].any(): + m = mask[k] + pred = outputs[k][m].argmax(dim=-1) + tgt = targets[k][m] + correct[k] += (pred == tgt).sum().item() + total[k] += m.sum().item() + + metrics = { + "loss": total_loss / n_batches, + **{f"loss_{k}": v / n_batches for k, v in task_losses.items() if v > 0}, + } + + # 添加准确率 + for k in ["pdi", "ee", "toxic"]: + if total[k] > 0: + metrics[f"acc_{k}"] = correct[k] / total[k] + + return metrics + + +class EarlyStopping: + """早停机制""" + + def __init__(self, patience: int = 10, min_delta: float = 0.0): + self.patience = patience + self.min_delta = min_delta + self.counter = 0 + self.best_loss = float("inf") + self.should_stop = False + + def __call__(self, val_loss: float) -> bool: + if val_loss < self.best_loss - self.min_delta: + self.best_loss = val_loss + self.counter = 0 + else: + self.counter += 1 + if self.counter >= self.patience: + self.should_stop = True + return self.should_stop + diff --git a/lnp_ml/plots.py b/lnp_ml/plots.py new file mode 100644 index 0000000..34628ab --- /dev/null +++ b/lnp_ml/plots.py @@ -0,0 +1,29 @@ +from pathlib import Path + +from loguru import logger +from tqdm import tqdm +import typer + +from lnp_ml.config import FIGURES_DIR, PROCESSED_DATA_DIR + +app = typer.Typer() + + +@app.command() +def main( + # ---- REPLACE DEFAULT PATHS AS APPROPRIATE ---- + input_path: Path = PROCESSED_DATA_DIR / "dataset.csv", + output_path: Path = FIGURES_DIR / "plot.png", + # ----------------------------------------- +): + # ---- REPLACE THIS WITH YOUR OWN CODE ---- + logger.info("Generating plot from data...") + for i in tqdm(range(10), total=10): + if i == 5: + logger.info("Something happened for iteration 5.") + logger.success("Plot generation complete.") + # ----------------------------------------- + + +if __name__ == "__main__": + app() diff --git a/models/.gitkeep b/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/models/history.json b/models/history.json new file mode 100644 index 0000000..a7bdd2f --- /dev/null +++ b/models/history.json @@ -0,0 +1,657 @@ +{ + "train": [ + { + "loss": 19.801735162734985, + "loss_size": 14.35811984539032, + "loss_pdi": 1.419530838727951, + "loss_ee": 1.0615579634904861, + "loss_delivery": 1.1513914689421654, + "loss_biodist": 1.238842561841011, + "loss_toxic": 0.5722923278808594 + }, + { + "loss": 8.425787031650543, + "loss_size": 3.660134330391884, + "loss_pdi": 1.175126627087593, + "loss_ee": 0.9683727994561195, + "loss_delivery": 1.1206831969320774, + "loss_biodist": 1.0895558297634125, + "loss_toxic": 0.4119141288101673 + }, + { + "loss": 4.890879034996033, + "loss_size": 0.5715616792440414, + "loss_pdi": 0.9125325158238411, + "loss_ee": 0.924556627869606, + "loss_delivery": 1.2105156518518925, + "loss_biodist": 0.9783133715391159, + "loss_toxic": 0.29339898377656937 + }, + { + "loss": 4.220313906669617, + "loss_size": 0.24587923847138882, + "loss_pdi": 0.77839545160532, + "loss_ee": 0.910746157169342, + "loss_delivery": 1.1220976933836937, + "loss_biodist": 0.9082718268036842, + "loss_toxic": 0.25492355413734913 + }, + { + "loss": 3.821678251028061, + "loss_size": 0.22942939028143883, + "loss_pdi": 0.6982513815164566, + "loss_ee": 0.8703903555870056, + "loss_delivery": 0.9583318457007408, + "loss_biodist": 0.8230277448892593, + "loss_toxic": 0.24224759358912706 + }, + { + "loss": 3.6422846913337708, + "loss_size": 0.2943352358415723, + "loss_pdi": 0.642115443944931, + "loss_ee": 0.834287479519844, + "loss_delivery": 0.9906296711415052, + "loss_biodist": 0.7021987289190292, + "loss_toxic": 0.1787180369719863 + }, + { + "loss": 3.2558620870113373, + "loss_size": 0.2535699959844351, + "loss_pdi": 0.6135993227362633, + "loss_ee": 0.7736481726169586, + "loss_delivery": 0.89691730029881, + "loss_biodist": 0.5883788056671619, + "loss_toxic": 0.12974846456199884 + }, + { + "loss": 3.095754951238632, + "loss_size": 0.3008947093039751, + "loss_pdi": 0.5887892656028271, + "loss_ee": 0.730943076312542, + "loss_delivery": 0.8772530537098646, + "loss_biodist": 0.5044719725847244, + "loss_toxic": 0.0934028816409409 + }, + { + "loss": 2.853863760828972, + "loss_size": 0.24649357236921787, + "loss_pdi": 0.5792484246194363, + "loss_ee": 0.6907523274421692, + "loss_delivery": 0.7996992748230696, + "loss_biodist": 0.4447066970169544, + "loss_toxic": 0.09296349296346307 + }, + { + "loss": 2.7370710372924805, + "loss_size": 0.2393215736374259, + "loss_pdi": 0.5310847833752632, + "loss_ee": 0.6698194481432438, + "loss_delivery": 0.8438112968578935, + "loss_biodist": 0.37191740795969963, + "loss_toxic": 0.08111646771430969 + }, + { + "loss": 2.5620490461587906, + "loss_size": 0.2313800435513258, + "loss_pdi": 0.5440232343971729, + "loss_ee": 0.6346339136362076, + "loss_delivery": 0.7632925817742944, + "loss_biodist": 0.311477467417717, + "loss_toxic": 0.07724180119112134 + }, + { + "loss": 2.4396377950906754, + "loss_size": 0.22545795049518347, + "loss_pdi": 0.5102365277707577, + "loss_ee": 0.5724069699645042, + "loss_delivery": 0.7819116842001677, + "loss_biodist": 0.2837779298424721, + "loss_toxic": 0.0658467230387032 + }, + { + "loss": 2.285309001803398, + "loss_size": 0.23147230129688978, + "loss_pdi": 0.4668895788490772, + "loss_ee": 0.6054624281823635, + "loss_delivery": 0.6695475745946169, + "loss_biodist": 0.2563781104981899, + "loss_toxic": 0.05555897764861584 + }, + { + "loss": 2.2476960122585297, + "loss_size": 0.23254966363310814, + "loss_pdi": 0.48378554731607437, + "loss_ee": 0.5625484213232994, + "loss_delivery": 0.6714967219159007, + "loss_biodist": 0.22976274229586124, + "loss_toxic": 0.06755285989493132 + }, + { + "loss": 2.075557619333267, + "loss_size": 0.1991214593872428, + "loss_pdi": 0.4666460305452347, + "loss_ee": 0.5353769697248936, + "loss_delivery": 0.5960409259423614, + "loss_biodist": 0.21837125346064568, + "loss_toxic": 0.060001003788784146 + }, + { + "loss": 2.5380745232105255, + "loss_size": 0.26692971400916576, + "loss_pdi": 0.46107836067676544, + "loss_ee": 0.5409572347998619, + "loss_delivery": 1.0045308656990528, + "loss_biodist": 0.20893055945634842, + "loss_toxic": 0.05564788053743541 + }, + { + "loss": 2.187355950474739, + "loss_size": 0.19620049092918634, + "loss_pdi": 0.4322536773979664, + "loss_ee": 0.5325545407831669, + "loss_delivery": 0.791555093601346, + "loss_biodist": 0.19126823358237743, + "loss_toxic": 0.0435238650534302 + }, + { + "loss": 2.0472205132246017, + "loss_size": 0.2132822722196579, + "loss_pdi": 0.4357164613902569, + "loss_ee": 0.4921276159584522, + "loss_delivery": 0.6838537249714136, + "loss_biodist": 0.17821292020380497, + "loss_toxic": 0.04402748285792768 + }, + { + "loss": 1.8377329260110855, + "loss_size": 0.1911225076764822, + "loss_pdi": 0.40041540563106537, + "loss_ee": 0.48244743049144745, + "loss_delivery": 0.5407265722751617, + "loss_biodist": 0.18436198495328426, + "loss_toxic": 0.03865901718381792 + }, + { + "loss": 1.767472356557846, + "loss_size": 0.1669206041842699, + "loss_pdi": 0.41016123816370964, + "loss_ee": 0.48856623098254204, + "loss_delivery": 0.4990109819918871, + "loss_biodist": 0.17120445892214775, + "loss_toxic": 0.03160886780824512 + }, + { + "loss": 1.7883769124746323, + "loss_size": 0.18442536424845457, + "loss_pdi": 0.40120166912674904, + "loss_ee": 0.46751197054982185, + "loss_delivery": 0.537370229139924, + "loss_biodist": 0.16241099871695042, + "loss_toxic": 0.03545669896993786 + }, + { + "loss": 1.7724643051624298, + "loss_size": 0.1618126342073083, + "loss_pdi": 0.40923435613512993, + "loss_ee": 0.4502934068441391, + "loss_delivery": 0.5432828362099826, + "loss_biodist": 0.17081433162093163, + "loss_toxic": 0.03702673775842413 + }, + { + "loss": 1.8993548452854156, + "loss_size": 0.15662006475031376, + "loss_pdi": 0.37853332981467247, + "loss_ee": 0.4373016320168972, + "loss_delivery": 0.7414433392696083, + "loss_biodist": 0.15876813046634197, + "loss_toxic": 0.02668837201781571 + }, + { + "loss": 1.7090002000331879, + "loss_size": 0.18559002690017223, + "loss_pdi": 0.3948023244738579, + "loss_ee": 0.4514715373516083, + "loss_delivery": 0.5189001243561506, + "loss_biodist": 0.13513169158250093, + "loss_toxic": 0.023104518884792924 + }, + { + "loss": 1.5656199902296066, + "loss_size": 0.1486712908372283, + "loss_pdi": 0.37246324494481087, + "loss_ee": 0.4362662769854069, + "loss_delivery": 0.4543162193149328, + "loss_biodist": 0.13266231305897236, + "loss_toxic": 0.021240679430775344 + }, + { + "loss": 1.6873565018177032, + "loss_size": 0.16270869225263596, + "loss_pdi": 0.3765699379146099, + "loss_ee": 0.4488464966416359, + "loss_delivery": 0.5208693165332079, + "loss_biodist": 0.15251764748245478, + "loss_toxic": 0.025844466988928616 + }, + { + "loss": 1.611741527915001, + "loss_size": 0.1608524639159441, + "loss_pdi": 0.3773089461028576, + "loss_ee": 0.4431660957634449, + "loss_delivery": 0.47964918427169323, + "loss_biodist": 0.13358874432742596, + "loss_toxic": 0.017176096327602863 + }, + { + "loss": 1.547684259712696, + "loss_size": 0.15734383463859558, + "loss_pdi": 0.3490111008286476, + "loss_ee": 0.4290902689099312, + "loss_delivery": 0.45983999967575073, + "loss_biodist": 0.1339349802583456, + "loss_toxic": 0.01846407217090018 + }, + { + "loss": 1.4728069305419922, + "loss_size": 0.16153435036540031, + "loss_pdi": 0.3516402244567871, + "loss_ee": 0.4158446751534939, + "loss_delivery": 0.3929086276330054, + "loss_biodist": 0.12818495463579893, + "loss_toxic": 0.02269406005507335 + }, + { + "loss": 1.494736596941948, + "loss_size": 0.13391354773193598, + "loss_pdi": 0.3454095683991909, + "loss_ee": 0.3995618261396885, + "loss_delivery": 0.47225130116567016, + "loss_biodist": 0.12276446260511875, + "loss_toxic": 0.02083588083041832 + }, + { + "loss": 1.5983823090791702, + "loss_size": 0.15655907429754734, + "loss_pdi": 0.3302378598600626, + "loss_ee": 0.40332265198230743, + "loss_delivery": 0.558618601411581, + "loss_biodist": 0.13051889464259148, + "loss_toxic": 0.019125229271594435 + } + ], + "val": [ + { + "loss": 10.642529010772705, + "loss_size": 6.192671060562134, + "loss_pdi": 1.2811625599861145, + "loss_ee": 1.0301620960235596, + "loss_delivery": 0.41728606820106506, + "loss_biodist": 1.2410815358161926, + "loss_toxic": 0.4801655113697052, + "acc_pdi": 0.45, + "acc_ee": 0.5833333333333334, + "acc_toxic": 0.9743589743589743 + }, + { + "loss": 4.37042236328125, + "loss_size": 0.6340591907501221, + "loss_pdi": 0.9936424493789673, + "loss_ee": 0.9678087830543518, + "loss_delivery": 0.41530802845954895, + "loss_biodist": 1.0821772813796997, + "loss_toxic": 0.2774266004562378, + "acc_pdi": 0.75, + "acc_ee": 0.5833333333333334, + "acc_toxic": 0.9743589743589743 + }, + { + "loss": 3.4235814809799194, + "loss_size": 0.07745273411273956, + "loss_pdi": 0.7955547869205475, + "loss_ee": 0.9630871117115021, + "loss_delivery": 0.41978873312473297, + "loss_biodist": 1.0081366300582886, + "loss_toxic": 0.1595613993704319, + "acc_pdi": 0.75, + "acc_ee": 0.5833333333333334, + "acc_toxic": 0.9743589743589743 + }, + { + "loss": 3.3056893348693848, + "loss_size": 0.13385668396949768, + "loss_pdi": 0.717946320772171, + "loss_ee": 0.9484646320343018, + "loss_delivery": 0.41560181975364685, + "loss_biodist": 0.9615574777126312, + "loss_toxic": 0.12826236337423325, + "acc_pdi": 0.75, + "acc_ee": 0.5833333333333334, + "acc_toxic": 0.9743589743589743 + }, + { + "loss": 3.1493594646453857, + "loss_size": 0.07856455072760582, + "loss_pdi": 0.668080747127533, + "loss_ee": 0.9540894627571106, + "loss_delivery": 0.474899023771286, + "loss_biodist": 0.8731786608695984, + "loss_toxic": 0.10054689273238182, + "acc_pdi": 0.75, + "acc_ee": 0.5833333333333334, + "acc_toxic": 0.9743589743589743 + }, + { + "loss": 2.8707590103149414, + "loss_size": 0.1222907267510891, + "loss_pdi": 0.6401174664497375, + "loss_ee": 0.9136309623718262, + "loss_delivery": 0.3875949829816818, + "loss_biodist": 0.7330293655395508, + "loss_toxic": 0.07409549504518509, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.6, + "acc_toxic": 0.9743589743589743 + }, + { + "loss": 2.6709553003311157, + "loss_size": 0.14854157716035843, + "loss_pdi": 0.6254143714904785, + "loss_ee": 0.8874242305755615, + "loss_delivery": 0.37635043263435364, + "loss_biodist": 0.5931203663349152, + "loss_toxic": 0.04010416939854622, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.5666666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.622614026069641, + "loss_size": 0.1486668586730957, + "loss_pdi": 0.6269595921039581, + "loss_ee": 0.8829602599143982, + "loss_delivery": 0.40370240807533264, + "loss_biodist": 0.535428449511528, + "loss_toxic": 0.024896428920328617, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + }, + { + "loss": 2.4939963817596436, + "loss_size": 0.11941515654325485, + "loss_pdi": 0.624951958656311, + "loss_ee": 0.882407933473587, + "loss_delivery": 0.38461272418498993, + "loss_biodist": 0.462909460067749, + "loss_toxic": 0.019699251279234886, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6, + "acc_toxic": 1.0 + }, + { + "loss": 2.4623881578445435, + "loss_size": 0.14110726118087769, + "loss_pdi": 0.5920329689979553, + "loss_ee": 0.8816524147987366, + "loss_delivery": 0.41789302229881287, + "loss_biodist": 0.40996842086315155, + "loss_toxic": 0.019733915105462074, + "acc_pdi": 0.75, + "acc_ee": 0.65, + "acc_toxic": 1.0 + }, + { + "loss": 2.396706700325012, + "loss_size": 0.11815935745835304, + "loss_pdi": 0.6046018600463867, + "loss_ee": 0.8763127326965332, + "loss_delivery": 0.4112636297941208, + "loss_biodist": 0.36830006539821625, + "loss_toxic": 0.018068938050419092, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.65, + "acc_toxic": 1.0 + }, + { + "loss": 2.457483172416687, + "loss_size": 0.13330847769975662, + "loss_pdi": 0.6174256205558777, + "loss_ee": 0.8725559711456299, + "loss_delivery": 0.46076954901218414, + "loss_biodist": 0.358450323343277, + "loss_toxic": 0.014973167330026627, + "acc_pdi": 0.75, + "acc_ee": 0.6333333333333333, + "acc_toxic": 1.0 + }, + { + "loss": 2.384940981864929, + "loss_size": 0.11283988133072853, + "loss_pdi": 0.5969351530075073, + "loss_ee": 0.8822423815727234, + "loss_delivery": 0.4492803066968918, + "loss_biodist": 0.3306152671575546, + "loss_toxic": 0.013027888257056475, + "acc_pdi": 0.75, + "acc_ee": 0.6, + "acc_toxic": 1.0 + }, + { + "loss": 2.395999550819397, + "loss_size": 0.1267366074025631, + "loss_pdi": 0.609168529510498, + "loss_ee": 0.8720199763774872, + "loss_delivery": 0.45844390988349915, + "loss_biodist": 0.31721335649490356, + "loss_toxic": 0.01241705659776926, + "acc_pdi": 0.75, + "acc_ee": 0.6333333333333333, + "acc_toxic": 1.0 + }, + { + "loss": 2.432988166809082, + "loss_size": 0.13193047046661377, + "loss_pdi": 0.6282809674739838, + "loss_ee": 0.889969140291214, + "loss_delivery": 0.45955638587474823, + "loss_biodist": 0.3108634203672409, + "loss_toxic": 0.012387921568006277, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6, + "acc_toxic": 1.0 + }, + { + "loss": 2.361279010772705, + "loss_size": 0.09955761209130287, + "loss_pdi": 0.6252501904964447, + "loss_ee": 0.8960326313972473, + "loss_delivery": 0.44326628744602203, + "loss_biodist": 0.285326212644577, + "loss_toxic": 0.011845993809401989, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6166666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.420892119407654, + "loss_size": 0.13956347852945328, + "loss_pdi": 0.6046904623508453, + "loss_ee": 0.9216890335083008, + "loss_delivery": 0.4730597734451294, + "loss_biodist": 0.27191491425037384, + "loss_toxic": 0.009974355343729258, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.5666666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.453316569328308, + "loss_size": 0.11874271184206009, + "loss_pdi": 0.6105562448501587, + "loss_ee": 0.923934280872345, + "loss_delivery": 0.5215270966291428, + "loss_biodist": 0.27011625468730927, + "loss_toxic": 0.008439893601462245, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + }, + { + "loss": 2.4440466165542603, + "loss_size": 0.13506918773055077, + "loss_pdi": 0.6187410056591034, + "loss_ee": 0.9395149946212769, + "loss_delivery": 0.48008452355861664, + "loss_biodist": 0.26326628774404526, + "loss_toxic": 0.007370669860392809, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.5666666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.4298155307769775, + "loss_size": 0.11216039955615997, + "loss_pdi": 0.6475824415683746, + "loss_ee": 0.9234196841716766, + "loss_delivery": 0.46182475984096527, + "loss_biodist": 0.2774467319250107, + "loss_toxic": 0.007381373317912221, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6333333333333333, + "acc_toxic": 1.0 + }, + { + "loss": 2.4662675857543945, + "loss_size": 0.07973097264766693, + "loss_pdi": 0.6542999148368835, + "loss_ee": 0.9407779574394226, + "loss_delivery": 0.4921555370092392, + "loss_biodist": 0.29242587089538574, + "loss_toxic": 0.006877265637740493, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + }, + { + "loss": 2.394904613494873, + "loss_size": 0.1067984513938427, + "loss_pdi": 0.6353477835655212, + "loss_ee": 0.9535529017448425, + "loss_delivery": 0.4185742735862732, + "loss_biodist": 0.27519282698631287, + "loss_toxic": 0.005438495893031359, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + }, + { + "loss": 2.424455165863037, + "loss_size": 0.09576653316617012, + "loss_pdi": 0.6532827019691467, + "loss_ee": 0.9595111310482025, + "loss_delivery": 0.4392053782939911, + "loss_biodist": 0.27055467665195465, + "loss_toxic": 0.006134827388450503, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.5666666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.5053725242614746, + "loss_size": 0.10888586193323135, + "loss_pdi": 0.656737744808197, + "loss_ee": 0.9638712704181671, + "loss_delivery": 0.4994397610425949, + "loss_biodist": 0.27049925923347473, + "loss_toxic": 0.005938541842624545, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.6166666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.539380669593811, + "loss_size": 0.11638890951871872, + "loss_pdi": 0.6549257636070251, + "loss_ee": 0.9761019647121429, + "loss_delivery": 0.5175990015268326, + "loss_biodist": 0.26853571832180023, + "loss_toxic": 0.0058291994500905275, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + }, + { + "loss": 2.4885005950927734, + "loss_size": 0.12185845524072647, + "loss_pdi": 0.667609304189682, + "loss_ee": 0.9739555716514587, + "loss_delivery": 0.45484381914138794, + "loss_biodist": 0.26541487127542496, + "loss_toxic": 0.00481854728423059, + "acc_pdi": 0.7166666666666667, + "acc_ee": 0.6, + "acc_toxic": 1.0 + }, + { + "loss": 2.514465093612671, + "loss_size": 0.11423381045460701, + "loss_pdi": 0.6905614733695984, + "loss_ee": 0.9758535623550415, + "loss_delivery": 0.47175830602645874, + "loss_biodist": 0.257549487054348, + "loss_toxic": 0.004508488811552525, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6, + "acc_toxic": 1.0 + }, + { + "loss": 2.5609203577041626, + "loss_size": 0.11414870619773865, + "loss_pdi": 0.6677174866199493, + "loss_ee": 0.9934183955192566, + "loss_delivery": 0.5167151391506195, + "loss_biodist": 0.2642747312784195, + "loss_toxic": 0.0046457564458251, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6166666666666667, + "acc_toxic": 1.0 + }, + { + "loss": 2.5480626821517944, + "loss_size": 0.10506367310881615, + "loss_pdi": 0.6713496148586273, + "loss_ee": 0.9947319328784943, + "loss_delivery": 0.5066179037094116, + "loss_biodist": 0.2658369243144989, + "loss_toxic": 0.004462693585082889, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.6, + "acc_toxic": 1.0 + }, + { + "loss": 2.5397441387176514, + "loss_size": 0.10522815585136414, + "loss_pdi": 0.6821762025356293, + "loss_ee": 0.9780809879302979, + "loss_delivery": 0.5069089531898499, + "loss_biodist": 0.2627965956926346, + "loss_toxic": 0.0045532952062785625, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + }, + { + "loss": 2.5472241640090942, + "loss_size": 0.11468468606472015, + "loss_pdi": 0.6848073601722717, + "loss_ee": 0.9830659925937653, + "loss_delivery": 0.5032574832439423, + "loss_biodist": 0.25694920122623444, + "loss_toxic": 0.0044593592174351215, + "acc_pdi": 0.7333333333333333, + "acc_ee": 0.5833333333333334, + "acc_toxic": 1.0 + } + ] +} \ No newline at end of file diff --git a/models/model.pt b/models/model.pt new file mode 100644 index 0000000..3060b30 Binary files /dev/null and b/models/model.pt differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_0/args.json b/models/pretrained/all_amine_split_for_LiON/cv_0/args.json new file mode 100644 index 0000000..c1ff2c8 --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_0/args.json @@ -0,0 +1,165 @@ +{ + "activation": "ReLU", + "adding_bond_types": true, + "adding_h": false, + "aggregation": "mean", + "aggregation_norm": 100, + "atom_constraints": [], + "atom_descriptor_scaling": true, + "atom_descriptors": null, + "atom_descriptors_path": null, + "atom_descriptors_size": 0, + "atom_features_size": 0, + "atom_messages": false, + "atom_targets": [], + "batch_size": 50, + "bias": false, + "bias_solvent": false, + "bond_constraints": [], + "bond_descriptor_scaling": true, + "bond_descriptors": null, + "bond_descriptors_path": null, + "bond_descriptors_size": 0, + "bond_features_size": 0, + "bond_targets": [], + "cache_cutoff": 10000, + "checkpoint_dir": null, + "checkpoint_frzn": null, + "checkpoint_path": null, + "checkpoint_paths": null, + "class_balance": false, + "config_path": "../data/args_files/optimized_configs.json", + "constraints_path": null, + "crossval_index_dir": null, + "crossval_index_file": null, + "crossval_index_sets": null, + "cuda": true, + "data_path": "../data/crossval_splits/all_amine_split_for_paper/cv_0/train.csv", + "data_weights_path": "../data/crossval_splits/all_amine_split_for_paper/cv_0/train_weights.csv", + "dataset_type": "regression", + "depth": 4, + "depth_solvent": 3, + "device": { + "_string": "cuda", + "_type": "python_object (type = device)", + "_value": "gASVHwAAAAAAAACMBXRvcmNolIwGZGV2aWNllJOUjARjdWRhlIWUUpQu" + }, + "dropout": 0.1, + "empty_cache": false, + "ensemble_size": 1, + "epochs": 50, + "evidential_regularization": 0, + "explicit_h": false, + "extra_metrics": [], + "features_generator": null, + "features_only": false, + "features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_0/train_extra_x.csv" + ], + "features_scaling": true, + "features_size": null, + "ffn_hidden_size": 600, + "ffn_num_layers": 3, + "final_lr": 0.0001, + "folds_file": null, + "freeze_first_only": false, + "frzn_ffn_layers": 0, + "gpu": null, + "grad_clip": null, + "hidden_size": 600, + "hidden_size_solvent": 300, + "ignore_columns": null, + "ignore_nan_metrics": false, + "init_lr": 0.0001, + "is_atom_bond_targets": false, + "keeping_atom_map": false, + "log_frequency": 10, + "loss_function": "mse", + "max_data_size": null, + "max_lr": 0.001, + "metric": "rmse", + "metrics": [ + "rmse" + ], + "minimize_score": true, + "mpn_shared": false, + "multiclass_num_classes": 3, + "no_adding_bond_types": false, + "no_atom_descriptor_scaling": false, + "no_bond_descriptor_scaling": false, + "no_cache_mol": false, + "no_cuda": false, + "no_features_scaling": false, + "no_shared_atom_bond_ffn": false, + "num_folds": 1, + "num_lrs": 1, + "num_tasks": 1, + "num_workers": 8, + "number_of_molecules": 1, + "overwrite_default_atom_features": false, + "overwrite_default_bond_features": false, + "phase_features_path": null, + "pytorch_seed": 0, + "quantile_loss_alpha": 0.1, + "quantiles": [], + "quiet": false, + "reaction": false, + "reaction_mode": "reac_diff", + "reaction_solvent": false, + "reproducibility": { + "command_line": "python main_script.py train all_amine_split_for_paper", + "git_has_uncommitted_changes": true, + "git_root": "/media/andersonxps/wd_4tb/evan/LNP_ML", + "git_url": "https://github.com/jswitten/LNP_ML/tree/167822980dc26ba65c5c14539c4ce12b81b0b8f3", + "time": "Tue Jul 30 10:15:25 2024" + }, + "resume_experiment": false, + "save_dir": "../data/crossval_splits/all_amine_split_for_paper/cv_0", + "save_preds": false, + "save_smiles_splits": false, + "seed": 42, + "separate_test_atom_descriptors_path": null, + "separate_test_bond_descriptors_path": null, + "separate_test_constraints_path": null, + "separate_test_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_0/test_extra_x.csv" + ], + "separate_test_path": "../data/crossval_splits/all_amine_split_for_paper/cv_0/test.csv", + "separate_test_phase_features_path": null, + "separate_val_atom_descriptors_path": null, + "separate_val_bond_descriptors_path": null, + "separate_val_constraints_path": null, + "separate_val_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_0/valid_extra_x.csv" + ], + "separate_val_path": "../data/crossval_splits/all_amine_split_for_paper/cv_0/valid.csv", + "separate_val_phase_features_path": null, + "shared_atom_bond_ffn": true, + "show_individual_scores": false, + "smiles_columns": [ + "smiles" + ], + "spectra_activation": "exp", + "spectra_phase_mask_path": null, + "spectra_target_floor": 1e-08, + "split_key_molecule": 0, + "split_sizes": [ + 1.0, + 0.0, + 0.0 + ], + "split_type": "random", + "target_columns": null, + "target_weights": null, + "task_names": [ + "quantified_delivery" + ], + "test": false, + "test_fold_index": null, + "train_data_size": null, + "undirected": false, + "use_input_features": true, + "val_fold_index": null, + "warmup_epochs": 2.0, + "weights_ffn_num_layers": 2 +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/model_0/events.out.tfevents.1722348927.andersonxps b/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/model_0/events.out.tfevents.1722348927.andersonxps new file mode 100644 index 0000000..45c6e5f Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/model_0/events.out.tfevents.1722348927.andersonxps differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/model_0/model.pt b/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/model_0/model.pt new file mode 100644 index 0000000..f830a1b Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/model_0/model.pt differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/test_scores.json b/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/test_scores.json new file mode 100644 index 0000000..3d4a4d1 --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_0/fold_0/test_scores.json @@ -0,0 +1,5 @@ +{ + "rmse": [ + 0.8880622451903801 + ] +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_1/args.json b/models/pretrained/all_amine_split_for_LiON/cv_1/args.json new file mode 100644 index 0000000..f88628a --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_1/args.json @@ -0,0 +1,165 @@ +{ + "activation": "ReLU", + "adding_bond_types": true, + "adding_h": false, + "aggregation": "mean", + "aggregation_norm": 100, + "atom_constraints": [], + "atom_descriptor_scaling": true, + "atom_descriptors": null, + "atom_descriptors_path": null, + "atom_descriptors_size": 0, + "atom_features_size": 0, + "atom_messages": false, + "atom_targets": [], + "batch_size": 50, + "bias": false, + "bias_solvent": false, + "bond_constraints": [], + "bond_descriptor_scaling": true, + "bond_descriptors": null, + "bond_descriptors_path": null, + "bond_descriptors_size": 0, + "bond_features_size": 0, + "bond_targets": [], + "cache_cutoff": 10000, + "checkpoint_dir": null, + "checkpoint_frzn": null, + "checkpoint_path": null, + "checkpoint_paths": null, + "class_balance": false, + "config_path": "../data/args_files/optimized_configs.json", + "constraints_path": null, + "crossval_index_dir": null, + "crossval_index_file": null, + "crossval_index_sets": null, + "cuda": true, + "data_path": "../data/crossval_splits/all_amine_split_for_paper/cv_1/train.csv", + "data_weights_path": "../data/crossval_splits/all_amine_split_for_paper/cv_1/train_weights.csv", + "dataset_type": "regression", + "depth": 4, + "depth_solvent": 3, + "device": { + "_string": "cuda", + "_type": "python_object (type = device)", + "_value": "gASVHwAAAAAAAACMBXRvcmNolIwGZGV2aWNllJOUjARjdWRhlIWUUpQu" + }, + "dropout": 0.1, + "empty_cache": false, + "ensemble_size": 1, + "epochs": 50, + "evidential_regularization": 0, + "explicit_h": false, + "extra_metrics": [], + "features_generator": null, + "features_only": false, + "features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_1/train_extra_x.csv" + ], + "features_scaling": true, + "features_size": null, + "ffn_hidden_size": 600, + "ffn_num_layers": 3, + "final_lr": 0.0001, + "folds_file": null, + "freeze_first_only": false, + "frzn_ffn_layers": 0, + "gpu": null, + "grad_clip": null, + "hidden_size": 600, + "hidden_size_solvent": 300, + "ignore_columns": null, + "ignore_nan_metrics": false, + "init_lr": 0.0001, + "is_atom_bond_targets": false, + "keeping_atom_map": false, + "log_frequency": 10, + "loss_function": "mse", + "max_data_size": null, + "max_lr": 0.001, + "metric": "rmse", + "metrics": [ + "rmse" + ], + "minimize_score": true, + "mpn_shared": false, + "multiclass_num_classes": 3, + "no_adding_bond_types": false, + "no_atom_descriptor_scaling": false, + "no_bond_descriptor_scaling": false, + "no_cache_mol": false, + "no_cuda": false, + "no_features_scaling": false, + "no_shared_atom_bond_ffn": false, + "num_folds": 1, + "num_lrs": 1, + "num_tasks": 1, + "num_workers": 8, + "number_of_molecules": 1, + "overwrite_default_atom_features": false, + "overwrite_default_bond_features": false, + "phase_features_path": null, + "pytorch_seed": 0, + "quantile_loss_alpha": 0.1, + "quantiles": [], + "quiet": false, + "reaction": false, + "reaction_mode": "reac_diff", + "reaction_solvent": false, + "reproducibility": { + "command_line": "python main_script.py train all_amine_split_for_paper", + "git_has_uncommitted_changes": true, + "git_root": "/media/andersonxps/wd_4tb/evan/LNP_ML", + "git_url": "https://github.com/jswitten/LNP_ML/tree/167822980dc26ba65c5c14539c4ce12b81b0b8f3", + "time": "Tue Jul 30 10:21:40 2024" + }, + "resume_experiment": false, + "save_dir": "../data/crossval_splits/all_amine_split_for_paper/cv_1", + "save_preds": false, + "save_smiles_splits": false, + "seed": 42, + "separate_test_atom_descriptors_path": null, + "separate_test_bond_descriptors_path": null, + "separate_test_constraints_path": null, + "separate_test_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_1/test_extra_x.csv" + ], + "separate_test_path": "../data/crossval_splits/all_amine_split_for_paper/cv_1/test.csv", + "separate_test_phase_features_path": null, + "separate_val_atom_descriptors_path": null, + "separate_val_bond_descriptors_path": null, + "separate_val_constraints_path": null, + "separate_val_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_1/valid_extra_x.csv" + ], + "separate_val_path": "../data/crossval_splits/all_amine_split_for_paper/cv_1/valid.csv", + "separate_val_phase_features_path": null, + "shared_atom_bond_ffn": true, + "show_individual_scores": false, + "smiles_columns": [ + "smiles" + ], + "spectra_activation": "exp", + "spectra_phase_mask_path": null, + "spectra_target_floor": 1e-08, + "split_key_molecule": 0, + "split_sizes": [ + 1.0, + 0.0, + 0.0 + ], + "split_type": "random", + "target_columns": null, + "target_weights": null, + "task_names": [ + "quantified_delivery" + ], + "test": false, + "test_fold_index": null, + "train_data_size": null, + "undirected": false, + "use_input_features": true, + "val_fold_index": null, + "warmup_epochs": 2.0, + "weights_ffn_num_layers": 2 +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/model_0/events.out.tfevents.1722349301.andersonxps b/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/model_0/events.out.tfevents.1722349301.andersonxps new file mode 100644 index 0000000..056d219 Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/model_0/events.out.tfevents.1722349301.andersonxps differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/model_0/model.pt b/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/model_0/model.pt new file mode 100644 index 0000000..ef8c294 Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/model_0/model.pt differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/test_scores.json b/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/test_scores.json new file mode 100644 index 0000000..65ebb97 --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_1/fold_0/test_scores.json @@ -0,0 +1,5 @@ +{ + "rmse": [ + 1.01673724295223 + ] +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_2/args.json b/models/pretrained/all_amine_split_for_LiON/cv_2/args.json new file mode 100644 index 0000000..ea6dea8 --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_2/args.json @@ -0,0 +1,165 @@ +{ + "activation": "ReLU", + "adding_bond_types": true, + "adding_h": false, + "aggregation": "mean", + "aggregation_norm": 100, + "atom_constraints": [], + "atom_descriptor_scaling": true, + "atom_descriptors": null, + "atom_descriptors_path": null, + "atom_descriptors_size": 0, + "atom_features_size": 0, + "atom_messages": false, + "atom_targets": [], + "batch_size": 50, + "bias": false, + "bias_solvent": false, + "bond_constraints": [], + "bond_descriptor_scaling": true, + "bond_descriptors": null, + "bond_descriptors_path": null, + "bond_descriptors_size": 0, + "bond_features_size": 0, + "bond_targets": [], + "cache_cutoff": 10000, + "checkpoint_dir": null, + "checkpoint_frzn": null, + "checkpoint_path": null, + "checkpoint_paths": null, + "class_balance": false, + "config_path": "../data/args_files/optimized_configs.json", + "constraints_path": null, + "crossval_index_dir": null, + "crossval_index_file": null, + "crossval_index_sets": null, + "cuda": true, + "data_path": "../data/crossval_splits/all_amine_split_for_paper/cv_2/train.csv", + "data_weights_path": "../data/crossval_splits/all_amine_split_for_paper/cv_2/train_weights.csv", + "dataset_type": "regression", + "depth": 4, + "depth_solvent": 3, + "device": { + "_string": "cuda", + "_type": "python_object (type = device)", + "_value": "gASVHwAAAAAAAACMBXRvcmNolIwGZGV2aWNllJOUjARjdWRhlIWUUpQu" + }, + "dropout": 0.1, + "empty_cache": false, + "ensemble_size": 1, + "epochs": 50, + "evidential_regularization": 0, + "explicit_h": false, + "extra_metrics": [], + "features_generator": null, + "features_only": false, + "features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_2/train_extra_x.csv" + ], + "features_scaling": true, + "features_size": null, + "ffn_hidden_size": 600, + "ffn_num_layers": 3, + "final_lr": 0.0001, + "folds_file": null, + "freeze_first_only": false, + "frzn_ffn_layers": 0, + "gpu": null, + "grad_clip": null, + "hidden_size": 600, + "hidden_size_solvent": 300, + "ignore_columns": null, + "ignore_nan_metrics": false, + "init_lr": 0.0001, + "is_atom_bond_targets": false, + "keeping_atom_map": false, + "log_frequency": 10, + "loss_function": "mse", + "max_data_size": null, + "max_lr": 0.001, + "metric": "rmse", + "metrics": [ + "rmse" + ], + "minimize_score": true, + "mpn_shared": false, + "multiclass_num_classes": 3, + "no_adding_bond_types": false, + "no_atom_descriptor_scaling": false, + "no_bond_descriptor_scaling": false, + "no_cache_mol": false, + "no_cuda": false, + "no_features_scaling": false, + "no_shared_atom_bond_ffn": false, + "num_folds": 1, + "num_lrs": 1, + "num_tasks": 1, + "num_workers": 8, + "number_of_molecules": 1, + "overwrite_default_atom_features": false, + "overwrite_default_bond_features": false, + "phase_features_path": null, + "pytorch_seed": 0, + "quantile_loss_alpha": 0.1, + "quantiles": [], + "quiet": false, + "reaction": false, + "reaction_mode": "reac_diff", + "reaction_solvent": false, + "reproducibility": { + "command_line": "python main_script.py train all_amine_split_for_paper", + "git_has_uncommitted_changes": true, + "git_root": "/media/andersonxps/wd_4tb/evan/LNP_ML", + "git_url": "https://github.com/jswitten/LNP_ML/tree/167822980dc26ba65c5c14539c4ce12b81b0b8f3", + "time": "Tue Jul 30 10:28:04 2024" + }, + "resume_experiment": false, + "save_dir": "../data/crossval_splits/all_amine_split_for_paper/cv_2", + "save_preds": false, + "save_smiles_splits": false, + "seed": 42, + "separate_test_atom_descriptors_path": null, + "separate_test_bond_descriptors_path": null, + "separate_test_constraints_path": null, + "separate_test_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_2/test_extra_x.csv" + ], + "separate_test_path": "../data/crossval_splits/all_amine_split_for_paper/cv_2/test.csv", + "separate_test_phase_features_path": null, + "separate_val_atom_descriptors_path": null, + "separate_val_bond_descriptors_path": null, + "separate_val_constraints_path": null, + "separate_val_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_2/valid_extra_x.csv" + ], + "separate_val_path": "../data/crossval_splits/all_amine_split_for_paper/cv_2/valid.csv", + "separate_val_phase_features_path": null, + "shared_atom_bond_ffn": true, + "show_individual_scores": false, + "smiles_columns": [ + "smiles" + ], + "spectra_activation": "exp", + "spectra_phase_mask_path": null, + "spectra_target_floor": 1e-08, + "split_key_molecule": 0, + "split_sizes": [ + 1.0, + 0.0, + 0.0 + ], + "split_type": "random", + "target_columns": null, + "target_weights": null, + "task_names": [ + "quantified_delivery" + ], + "test": false, + "test_fold_index": null, + "train_data_size": null, + "undirected": false, + "use_input_features": true, + "val_fold_index": null, + "warmup_epochs": 2.0, + "weights_ffn_num_layers": 2 +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/model_0/events.out.tfevents.1722349684.andersonxps b/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/model_0/events.out.tfevents.1722349684.andersonxps new file mode 100644 index 0000000..3ae5207 Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/model_0/events.out.tfevents.1722349684.andersonxps differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/model_0/model.pt b/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/model_0/model.pt new file mode 100644 index 0000000..f97a0c6 Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/model_0/model.pt differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/test_scores.json b/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/test_scores.json new file mode 100644 index 0000000..ccba0be --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_2/fold_0/test_scores.json @@ -0,0 +1,5 @@ +{ + "rmse": [ + 0.8788072588544181 + ] +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_3/args.json b/models/pretrained/all_amine_split_for_LiON/cv_3/args.json new file mode 100644 index 0000000..1bf69ce --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_3/args.json @@ -0,0 +1,165 @@ +{ + "activation": "ReLU", + "adding_bond_types": true, + "adding_h": false, + "aggregation": "mean", + "aggregation_norm": 100, + "atom_constraints": [], + "atom_descriptor_scaling": true, + "atom_descriptors": null, + "atom_descriptors_path": null, + "atom_descriptors_size": 0, + "atom_features_size": 0, + "atom_messages": false, + "atom_targets": [], + "batch_size": 50, + "bias": false, + "bias_solvent": false, + "bond_constraints": [], + "bond_descriptor_scaling": true, + "bond_descriptors": null, + "bond_descriptors_path": null, + "bond_descriptors_size": 0, + "bond_features_size": 0, + "bond_targets": [], + "cache_cutoff": 10000, + "checkpoint_dir": null, + "checkpoint_frzn": null, + "checkpoint_path": null, + "checkpoint_paths": null, + "class_balance": false, + "config_path": "../data/args_files/optimized_configs.json", + "constraints_path": null, + "crossval_index_dir": null, + "crossval_index_file": null, + "crossval_index_sets": null, + "cuda": true, + "data_path": "../data/crossval_splits/all_amine_split_for_paper/cv_3/train.csv", + "data_weights_path": "../data/crossval_splits/all_amine_split_for_paper/cv_3/train_weights.csv", + "dataset_type": "regression", + "depth": 4, + "depth_solvent": 3, + "device": { + "_string": "cuda", + "_type": "python_object (type = device)", + "_value": "gASVHwAAAAAAAACMBXRvcmNolIwGZGV2aWNllJOUjARjdWRhlIWUUpQu" + }, + "dropout": 0.1, + "empty_cache": false, + "ensemble_size": 1, + "epochs": 50, + "evidential_regularization": 0, + "explicit_h": false, + "extra_metrics": [], + "features_generator": null, + "features_only": false, + "features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_3/train_extra_x.csv" + ], + "features_scaling": true, + "features_size": null, + "ffn_hidden_size": 600, + "ffn_num_layers": 3, + "final_lr": 0.0001, + "folds_file": null, + "freeze_first_only": false, + "frzn_ffn_layers": 0, + "gpu": null, + "grad_clip": null, + "hidden_size": 600, + "hidden_size_solvent": 300, + "ignore_columns": null, + "ignore_nan_metrics": false, + "init_lr": 0.0001, + "is_atom_bond_targets": false, + "keeping_atom_map": false, + "log_frequency": 10, + "loss_function": "mse", + "max_data_size": null, + "max_lr": 0.001, + "metric": "rmse", + "metrics": [ + "rmse" + ], + "minimize_score": true, + "mpn_shared": false, + "multiclass_num_classes": 3, + "no_adding_bond_types": false, + "no_atom_descriptor_scaling": false, + "no_bond_descriptor_scaling": false, + "no_cache_mol": false, + "no_cuda": false, + "no_features_scaling": false, + "no_shared_atom_bond_ffn": false, + "num_folds": 1, + "num_lrs": 1, + "num_tasks": 1, + "num_workers": 8, + "number_of_molecules": 1, + "overwrite_default_atom_features": false, + "overwrite_default_bond_features": false, + "phase_features_path": null, + "pytorch_seed": 0, + "quantile_loss_alpha": 0.1, + "quantiles": [], + "quiet": false, + "reaction": false, + "reaction_mode": "reac_diff", + "reaction_solvent": false, + "reproducibility": { + "command_line": "python main_script.py train all_amine_split_for_paper", + "git_has_uncommitted_changes": true, + "git_root": "/media/andersonxps/wd_4tb/evan/LNP_ML", + "git_url": "https://github.com/jswitten/LNP_ML/tree/167822980dc26ba65c5c14539c4ce12b81b0b8f3", + "time": "Tue Jul 30 10:34:31 2024" + }, + "resume_experiment": false, + "save_dir": "../data/crossval_splits/all_amine_split_for_paper/cv_3", + "save_preds": false, + "save_smiles_splits": false, + "seed": 42, + "separate_test_atom_descriptors_path": null, + "separate_test_bond_descriptors_path": null, + "separate_test_constraints_path": null, + "separate_test_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_3/test_extra_x.csv" + ], + "separate_test_path": "../data/crossval_splits/all_amine_split_for_paper/cv_3/test.csv", + "separate_test_phase_features_path": null, + "separate_val_atom_descriptors_path": null, + "separate_val_bond_descriptors_path": null, + "separate_val_constraints_path": null, + "separate_val_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_3/valid_extra_x.csv" + ], + "separate_val_path": "../data/crossval_splits/all_amine_split_for_paper/cv_3/valid.csv", + "separate_val_phase_features_path": null, + "shared_atom_bond_ffn": true, + "show_individual_scores": false, + "smiles_columns": [ + "smiles" + ], + "spectra_activation": "exp", + "spectra_phase_mask_path": null, + "spectra_target_floor": 1e-08, + "split_key_molecule": 0, + "split_sizes": [ + 1.0, + 0.0, + 0.0 + ], + "split_type": "random", + "target_columns": null, + "target_weights": null, + "task_names": [ + "quantified_delivery" + ], + "test": false, + "test_fold_index": null, + "train_data_size": null, + "undirected": false, + "use_input_features": true, + "val_fold_index": null, + "warmup_epochs": 2.0, + "weights_ffn_num_layers": 2 +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/model_0/events.out.tfevents.1722350072.andersonxps b/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/model_0/events.out.tfevents.1722350072.andersonxps new file mode 100644 index 0000000..1de1809 Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/model_0/events.out.tfevents.1722350072.andersonxps differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/model_0/model.pt b/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/model_0/model.pt new file mode 100644 index 0000000..3f54b88 Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/model_0/model.pt differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/test_scores.json b/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/test_scores.json new file mode 100644 index 0000000..4dbcddc --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_3/fold_0/test_scores.json @@ -0,0 +1,5 @@ +{ + "rmse": [ + 0.9245934905333985 + ] +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_4/args.json b/models/pretrained/all_amine_split_for_LiON/cv_4/args.json new file mode 100644 index 0000000..39a02a4 --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_4/args.json @@ -0,0 +1,165 @@ +{ + "activation": "ReLU", + "adding_bond_types": true, + "adding_h": false, + "aggregation": "mean", + "aggregation_norm": 100, + "atom_constraints": [], + "atom_descriptor_scaling": true, + "atom_descriptors": null, + "atom_descriptors_path": null, + "atom_descriptors_size": 0, + "atom_features_size": 0, + "atom_messages": false, + "atom_targets": [], + "batch_size": 50, + "bias": false, + "bias_solvent": false, + "bond_constraints": [], + "bond_descriptor_scaling": true, + "bond_descriptors": null, + "bond_descriptors_path": null, + "bond_descriptors_size": 0, + "bond_features_size": 0, + "bond_targets": [], + "cache_cutoff": 10000, + "checkpoint_dir": null, + "checkpoint_frzn": null, + "checkpoint_path": null, + "checkpoint_paths": null, + "class_balance": false, + "config_path": "../data/args_files/optimized_configs.json", + "constraints_path": null, + "crossval_index_dir": null, + "crossval_index_file": null, + "crossval_index_sets": null, + "cuda": true, + "data_path": "../data/crossval_splits/all_amine_split_for_paper/cv_4/train.csv", + "data_weights_path": "../data/crossval_splits/all_amine_split_for_paper/cv_4/train_weights.csv", + "dataset_type": "regression", + "depth": 4, + "depth_solvent": 3, + "device": { + "_string": "cuda", + "_type": "python_object (type = device)", + "_value": "gASVHwAAAAAAAACMBXRvcmNolIwGZGV2aWNllJOUjARjdWRhlIWUUpQu" + }, + "dropout": 0.1, + "empty_cache": false, + "ensemble_size": 1, + "epochs": 50, + "evidential_regularization": 0, + "explicit_h": false, + "extra_metrics": [], + "features_generator": null, + "features_only": false, + "features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_4/train_extra_x.csv" + ], + "features_scaling": true, + "features_size": null, + "ffn_hidden_size": 600, + "ffn_num_layers": 3, + "final_lr": 0.0001, + "folds_file": null, + "freeze_first_only": false, + "frzn_ffn_layers": 0, + "gpu": null, + "grad_clip": null, + "hidden_size": 600, + "hidden_size_solvent": 300, + "ignore_columns": null, + "ignore_nan_metrics": false, + "init_lr": 0.0001, + "is_atom_bond_targets": false, + "keeping_atom_map": false, + "log_frequency": 10, + "loss_function": "mse", + "max_data_size": null, + "max_lr": 0.001, + "metric": "rmse", + "metrics": [ + "rmse" + ], + "minimize_score": true, + "mpn_shared": false, + "multiclass_num_classes": 3, + "no_adding_bond_types": false, + "no_atom_descriptor_scaling": false, + "no_bond_descriptor_scaling": false, + "no_cache_mol": false, + "no_cuda": false, + "no_features_scaling": false, + "no_shared_atom_bond_ffn": false, + "num_folds": 1, + "num_lrs": 1, + "num_tasks": 1, + "num_workers": 8, + "number_of_molecules": 1, + "overwrite_default_atom_features": false, + "overwrite_default_bond_features": false, + "phase_features_path": null, + "pytorch_seed": 0, + "quantile_loss_alpha": 0.1, + "quantiles": [], + "quiet": false, + "reaction": false, + "reaction_mode": "reac_diff", + "reaction_solvent": false, + "reproducibility": { + "command_line": "python main_script.py train all_amine_split_for_paper", + "git_has_uncommitted_changes": true, + "git_root": "/media/andersonxps/wd_4tb/evan/LNP_ML", + "git_url": "https://github.com/jswitten/LNP_ML/tree/167822980dc26ba65c5c14539c4ce12b81b0b8f3", + "time": "Tue Jul 30 10:40:44 2024" + }, + "resume_experiment": false, + "save_dir": "../data/crossval_splits/all_amine_split_for_paper/cv_4", + "save_preds": false, + "save_smiles_splits": false, + "seed": 42, + "separate_test_atom_descriptors_path": null, + "separate_test_bond_descriptors_path": null, + "separate_test_constraints_path": null, + "separate_test_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_4/test_extra_x.csv" + ], + "separate_test_path": "../data/crossval_splits/all_amine_split_for_paper/cv_4/test.csv", + "separate_test_phase_features_path": null, + "separate_val_atom_descriptors_path": null, + "separate_val_bond_descriptors_path": null, + "separate_val_constraints_path": null, + "separate_val_features_path": [ + "../data/crossval_splits/all_amine_split_for_paper/cv_4/valid_extra_x.csv" + ], + "separate_val_path": "../data/crossval_splits/all_amine_split_for_paper/cv_4/valid.csv", + "separate_val_phase_features_path": null, + "shared_atom_bond_ffn": true, + "show_individual_scores": false, + "smiles_columns": [ + "smiles" + ], + "spectra_activation": "exp", + "spectra_phase_mask_path": null, + "spectra_target_floor": 1e-08, + "split_key_molecule": 0, + "split_sizes": [ + 1.0, + 0.0, + 0.0 + ], + "split_type": "random", + "target_columns": null, + "target_weights": null, + "task_names": [ + "quantified_delivery" + ], + "test": false, + "test_fold_index": null, + "train_data_size": null, + "undirected": false, + "use_input_features": true, + "val_fold_index": null, + "warmup_epochs": 2.0, + "weights_ffn_num_layers": 2 +} \ No newline at end of file diff --git a/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/model_0/events.out.tfevents.1722350444.andersonxps b/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/model_0/events.out.tfevents.1722350444.andersonxps new file mode 100644 index 0000000..30468ad Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/model_0/events.out.tfevents.1722350444.andersonxps differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/model_0/model.pt b/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/model_0/model.pt new file mode 100644 index 0000000..3bfbe0a Binary files /dev/null and b/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/model_0/model.pt differ diff --git a/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/test_scores.json b/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/test_scores.json new file mode 100644 index 0000000..d27a9dd --- /dev/null +++ b/models/pretrained/all_amine_split_for_LiON/cv_4/fold_0/test_scores.json @@ -0,0 +1,5 @@ +{ + "rmse": [ + 0.8268900471469541 + ] +} \ No newline at end of file diff --git a/notebooks/.gitkeep b/notebooks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 0000000..a0ccf55 --- /dev/null +++ b/pixi.lock @@ -0,0 +1,2540 @@ +version: 6 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.2-hf4e2dac_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/loguru-0.7.2-py38h578d9bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.8.20-h4a871b0_2_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.8-8_cp38.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.12-h4196e79_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.12.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.12.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.12.5-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + - pypi: https://files.pythonhosted.org/packages/64/88/c7083fc61120ab661c5d0b82cb77079fc1429d3f913a456c1c82cf4658f7/alabaster-0.7.13-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/fa/cf5bb2409a385f78750e78c8d2e24780964976acdaaed65dbd6083ae5b40/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a0/ad/72361203906e2dbe9baa776c64e9246d555b516808dd0cce385f07f4cf71/chemprop-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8e/71/7f20855592cc929bc206810432b991ec4c702dc26b0567b132e52c85536f/contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/15/1a/c6eae628480aa1fc5f6f85437c7d8ec0d1172597acd1c61182202a902c0f/cramjam-2.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/3c/67bfc03db3f6527a54a0f9b696809bca55eb6de27928829218ab7e6c40e7/descriptastorus-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/31/673a677c137d8b2429813f56be77986f44bcb43f5c113336a4c8347e0eb0/fastparquet-2024.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/af/6a/00d144ac1626fbb44c4ff36519712e258128985a5d0ae43344778ae5cbb9/Flask-2.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a5/0e/b6314a09a4d561aaa7e09de43fa700917be91e701f07df6178865962666c/fonttools-4.57.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b6/cd/5b3334d39276067f54618ce0d0b48ed69d91352fbf137468c7095170d0e5/hyperopt-0.2.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/6a/4604f9ae2fa62ef47b9de2fa5ad599589d28c9fd1d335f32759813dfa91e/importlib_resources-6.4.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/36/ae40d7a3171e06f55ac77fe5536079e7be1d8be2a8210e08975c7f9b4d54/kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c7/bd/50319665ce81bb10e90d1cf76f9e1aa269ea6f7fa30ab4521f14d122a3df/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/30/33/cc27211d2ffeee4fd7402dca137b6e8a83f6dcae3d4be8d0ad5068555561/matplotlib-3.7.5-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/05/9d4f9b78ead6b2661d6e8ea772e111fc4a9fbd866ad0c81906c11206b55e/networkx-3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/98/5d/5738903efe0ecb73e51eb44feafba32bdba2081263d40c5043568ff60faf/numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/37/6d/121efd7382d5b0284239f4ab1fc1590d86d34ed4a4a2fdb13b30ca8e5740/nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/7e/00/6b218edd739ecfc60524e585ba8e6b00554dd908de2c9c66c1af3e44e18d/nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b6/9f/c64c03f49d6fbc56196664d05dba14e3a561038a81a638eeb47f4d4cfd48/nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/eb/d5/c68b1d2cdfcc59e72e8a5949a37ddb22ae6cade80cd4a57a84d4c8b55472/nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/86/94/eb540db023ce1d162e7bea9f8f5aa781d57c65aed513c33ee9a5123ead4d/nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/44/31/4890b1c9abc496303412947fc7dcea3d14861720642b49e8ceed89636705/nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/4b/2a/0a131f572aa09f741c30ccd45a8e56316e8be8dfc7bc19bf0ab7cfef7b19/nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/46/0c/c75bbfb967457a0b7670b8ad267bfc4fffdf341c074e0a80db06c24ccfd4/nvidia_nvjitlink_cu12-12.9.86-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/da/d3/8057f0587683ed2fcd4dbfbdfdfa807b9160b809976099d36b8f60d08f03/nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f8/7f/5b047effafbdd34e52c9e2d7e44f729a0655efafb22198c45cf692cdc157/pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/5d/e6/71ed4d95676098159b533c4a4c424cf453fec9614edaff1a0633fe228eef/pandas_flavor-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/ad/435fe29865f98a8fbdc64add8875a6e4f8c97749a93577a8919ec6f32c64/pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/bd/db/ea0203e495be491c85af87b66e37acfd3bf756fd985f87e46fc5e3bf022c/py4j-0.10.9.9-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e6/c1/4c6bcdf7a820034aa91a8b4d25fef38809be79b42ca7aaa16d4680b0bbac/pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e5/0c/0e3c05b1c87bb6a1c76d281b0f35e78d2d80ac91b5f8f524cebf77f51049/pyparsing-3.1.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3d/84/63b2e66f5c7cb97ce994769afbbef85a1ac364fedbcb7d4a3c0f15d318a5/rdkit-2024.3.5-cp38-cp38-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3f/48/6fdd99f5717045f9984616b5c2ec683d6286d30c0ac234563062132b83ab/scikit_learn-1.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/69/f0/fb07a9548e48b687b8bf2fa81d71aba9cfc548d365046ca1c791e24db99d/scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/48/17/325cf6a257d84751a48ae90752b3d8fe0be8f9535b6253add61c49d0d9bc/sphinx-7.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/06/c1/5e2cafbd03105ce50d8500f9b4e8a6e8d02e22d0475b574c3b3e9451a15f/sphinxcontrib_applehelp-1.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c5/09/5de5ed43a521387f18bdf5f5af31d099605c992fd25372b2b9b825ce48ee/sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6e/ee/a1f5e39046cbb5f8bc8fba87d1ddf1c6643fbc9194e58d26e606de4b9074/sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2b/14/05f9206cf4e9cfca1afb5fd224c7cd434dcc3a433d6d9e4e0264d29c6cdb/sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c6/77/5464ec50dd0f1c1037e3c93249b040c8fc8078fdda97530eeb02424b6eea/sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/44/71/f3e7c9b2ab67e28c572ab4e9d5fa3499e0d252650f96d8a3a03e26677f53/tensorboardX-2.6.2.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/71/45aac46b75742e08d2d6f9fc2b612223b5e36115b8b2ed673b23c21b5387/torch-2.4.1-cp38-cp38-manylinux1_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/4d/b4/c37e2776a1390bab7e78a6d52bd525441cb3cad7260a6a00b11b0b702e7c/triton-3.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/ea/07/0055bb513de2b7cf23b2bfc7eeeb6a6ae6ff7b287e2a62ab80cf403d61e9/typed_argument_parser-1.10.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/21/0a674dfe66e9df9072c46269c882e9f901d36d987d8ea50ead033a9c1e01/werkzeug-2.3.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b4/a7/897f484225bd8e179a4f39f8e9a4ca26c14e9f7055b495384b1d56e1382d/xarray-2023.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl + - pypi: . + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-h38cb7af_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.2-h1ae2325_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/loguru-0.7.2-py38h10201cd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.8.20-h7d35d02_2_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.8-8_cp38.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.12-hb0cad00_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.12.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.12.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.12.5-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.8.1-h9a6d368_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-gpl-tools-5.8.1-h9a6d368_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-tools-5.8.1-h39f12f2_2.conda + - pypi: https://files.pythonhosted.org/packages/64/88/c7083fc61120ab661c5d0b82cb77079fc1429d3f913a456c1c82cf4658f7/alabaster-0.7.13-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0a/4e/3926a1c11f0433791985727965263f788af00db3482d89a7545ca5ecc921/charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/a0/ad/72361203906e2dbe9baa776c64e9246d555b516808dd0cce385f07f4cf71/chemprop-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/82/29f5ff4ae074c3230e266bc9efef449ebde43721a727b989dd8ef8f97d73/contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/65/64/e34ee535519fd14cde3a7f3f8cd3b4ef54483b9df655e4180437eb884aab/cramjam-2.11.0-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7b/3c/67bfc03db3f6527a54a0f9b696809bca55eb6de27928829218ab7e6c40e7/descriptastorus-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/27/8e/ce64bc647728ee26bfffa9987fb375e7c99bba54b632404bc0d61b6e9fc2/fastparquet-2024.2.0-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/af/6a/00d144ac1626fbb44c4ff36519712e258128985a5d0ae43344778ae5cbb9/Flask-2.1.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8a/3f/c16dbbec7221783f37dcc2022d5a55f0d704ffc9feef67930f6eb517e8ce/fonttools-4.57.0-cp38-cp38-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b6/cd/5b3334d39276067f54618ce0d0b48ed69d91352fbf137468c7095170d0e5/hyperopt-0.2.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/6a/4604f9ae2fa62ef47b9de2fa5ad599589d28c9fd1d335f32759813dfa91e/importlib_resources-6.4.5-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/a7/bb8ab10e12cc8764f4da0245d72dee4731cc720bdec0f085d5e9c6005b98/kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/f8/ff/2c942a82c35a49df5de3a630ce0a8456ac2969691b230e530ac12314364c/MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/aa/59/4d13e5b6298b1ca5525eea8c68d3806ae93ab6d0bb17ca9846aa3156b92b/matplotlib-3.7.5-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/05/9d4f9b78ead6b2661d6e8ea772e111fc4a9fbd866ad0c81906c11206b55e/networkx-3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/ae/f53b7b265fdc701e663fbb322a8e9d4b14d9cb7b2385f45ddfabfc4327e4/numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/53/c3/f8e87361f7fdf42012def602bfa2a593423c729f5cb7c97aed7f51be66ac/pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/5d/e6/71ed4d95676098159b533c4a4c424cf453fec9614edaff1a0633fe228eef/pandas_flavor-0.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/10/43/105823d233c5e5d31cea13428f4474ded9d961652307800979a59d6a4276/pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/bd/db/ea0203e495be491c85af87b66e37acfd3bf756fd985f87e46fc5e3bf022c/py4j-0.10.9.9-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/d9/51e35550f2f18b8815a2ab25948f735434db32000c0e91eba3a32634782a/pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/e5/0c/0e3c05b1c87bb6a1c76d281b0f35e78d2d80ac91b5f8f524cebf77f51049/pyparsing-3.1.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/cb/c709b60f4815e18c00e1e8639204bdba04cb158e6278791d82f94f51a988/rdkit-2024.3.5-cp38-cp38-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/62/92e9cec3deca8b45abf62dd8f6469d688b3f28b9c170809fcc46f110b523/scikit_learn-1.3.2-cp38-cp38-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/93/4a/50c436de1353cce8b66b26e49a687f10b91fe7465bf34e4565d810153003/scipy-1.10.1-cp38-cp38-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/48/17/325cf6a257d84751a48ae90752b3d8fe0be8f9535b6253add61c49d0d9bc/sphinx-7.1.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/06/c1/5e2cafbd03105ce50d8500f9b4e8a6e8d02e22d0475b574c3b3e9451a15f/sphinxcontrib_applehelp-1.0.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c5/09/5de5ed43a521387f18bdf5f5af31d099605c992fd25372b2b9b825ce48ee/sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6e/ee/a1f5e39046cbb5f8bc8fba87d1ddf1c6643fbc9194e58d26e606de4b9074/sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2b/14/05f9206cf4e9cfca1afb5fd224c7cd434dcc3a433d6d9e4e0264d29c6cdb/sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c6/77/5464ec50dd0f1c1037e3c93249b040c8fc8078fdda97530eeb02424b6eea/sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/44/71/f3e7c9b2ab67e28c572ab4e9d5fa3499e0d252650f96d8a3a03e26677f53/tensorboardX-2.6.2.2-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c4/88/4d9f66de5fe732462a2713c9931cab614d3fd6a9b5d9ee1f04768ad64daa/torch-2.4.1-cp38-none-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ea/07/0055bb513de2b7cf23b2bfc7eeeb6a6ae6ff7b287e2a62ab80cf403d61e9/typed_argument_parser-1.10.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fd/21/0a674dfe66e9df9072c46269c882e9f901d36d987d8ea50ead033a9c1e01/werkzeug-2.3.8-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b4/a7/897f484225bd8e179a4f39f8e9a4ca26c14e9f7055b495384b1d56e1382d/xarray-2023.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl + - pypi: . +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + purls: [] + size: 2562 + timestamp: 1578324546067 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + build_number: 16 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 23621 + timestamp: 1650670423406 +- pypi: https://files.pythonhosted.org/packages/64/88/c7083fc61120ab661c5d0b82cb77079fc1429d3f913a456c1c82cf4658f7/alabaster-0.7.13-py3-none-any.whl + name: alabaster + version: 0.7.13 + sha256: 1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3 + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl + name: babel + version: 2.17.0 + sha256: 4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2 + requires_dist: + - pytz>=2015.7 ; python_full_version < '3.9' + - tzdata ; sys_platform == 'win32' and extra == 'dev' + - backports-zoneinfo ; python_full_version < '3.9' and extra == 'dev' + - freezegun~=1.0 ; extra == 'dev' + - jinja2>=3.0 ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - pytest>=6.0 ; extra == 'dev' + - pytz ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 + md5: 51a19bba1b8ebfb60df25cde030b7ebc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 260341 + timestamp: 1757437258798 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda + sha256: b456200636bd5fecb2bec63f7e0985ad2097cf1b83d60ce0b6968dffa6d02aa1 + md5: 58fd217444c2a5701a44244faf518206 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 125061 + timestamp: 1757437486465 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.1.4-hbd8a1cb_0.conda + sha256: b5974ec9b50e3c514a382335efa81ed02b05906849827a34061c496f4defa0b2 + md5: bddacf101bb4dd0e51811cb69c7790e2 + depends: + - __unix + license: ISC + purls: [] + size: 146519 + timestamp: 1767500828366 +- pypi: https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl + name: certifi + version: 2026.1.4 + sha256: 9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/0a/4e/3926a1c11f0433791985727965263f788af00db3482d89a7545ca5ecc921/charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl + name: charset-normalizer + version: 3.4.4 + sha256: ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/bf/fa/cf5bb2409a385f78750e78c8d2e24780964976acdaaed65dbd6083ae5b40/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: charset-normalizer + version: 3.4.4 + sha256: f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/a0/ad/72361203906e2dbe9baa776c64e9246d555b516808dd0cce385f07f4cf71/chemprop-1.7.0-py3-none-any.whl + name: chemprop + version: 1.7.0 + sha256: 1f6d691c3d0a552d80bf7639960bbae3d90729583ee23c25f8e7250f6a90c259 + requires_dist: + - flask>=1.1.2,<=2.1.3 + - werkzeug<3 + - hyperopt>=0.2.3 + - matplotlib>=3.1.3 + - numpy>=1.18.1 + - pandas>=1.0.3 + - pandas-flavor>=0.2.0 + - scikit-learn>=0.22.2.post1 + - sphinx>=3.1.2 + - sphinx-rtd-theme>=2.0.0 + - tensorboardx>=2.0 + - torch>=1.4.0 + - tqdm>=4.45.0 + - typed-argument-parser>=1.6.1 + - rdkit>=2020.3.1.0 + - scipy<1.11 ; python_full_version == '3.7.*' + - descriptastorus<2.6.1 ; python_full_version == '3.7.*' + - scipy>=1.9 ; python_full_version == '3.8.*' + - descriptastorus>=2.6.1 ; python_full_version == '3.8.*' + - pytest>=6.2.2 ; extra == 'test' + - parameterized>=0.8.1 ; extra == 'test' + requires_python: '>=3.7,<3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda + sha256: f0016cbab6ac4138a429e28dbcb904a90305b34b3fe41a9b89d697c90401caec + md5: f3ad426304898027fc619827ff428eca + depends: + - __unix + - python >=3.8 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/click?source=hash-mapping + size: 84437 + timestamp: 1692311973840 +- pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl + name: cloudpickle + version: 3.1.2 + sha256: 9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2 + sha256: 2c1b2e9755ce3102bca8d69e8f26e4f087ece73f50418186aee7c74bef8e1698 + md5: 3faab06a954c2a04039983f2c4a50d99 + depends: + - python >=3.7 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping + size: 25170 + timestamp: 1666700778190 +- pypi: https://files.pythonhosted.org/packages/8e/71/7f20855592cc929bc206810432b991ec4c702dc26b0567b132e52c85536f/contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: contourpy + version: 1.1.1 + sha256: 41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8 + requires_dist: + - numpy>=1.16,<2.0 ; python_full_version < '3.12' + - numpy>=1.26.0rc1,<2.0 ; python_full_version >= '3.12' + - furo ; extra == 'docs' + - sphinx>=7.2 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==1.4.1 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a6/82/29f5ff4ae074c3230e266bc9efef449ebde43721a727b989dd8ef8f97d73/contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl + name: contourpy + version: 1.1.1 + sha256: 8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9 + requires_dist: + - numpy>=1.16,<2.0 ; python_full_version < '3.12' + - numpy>=1.26.0rc1,<2.0 ; python_full_version >= '3.12' + - furo ; extra == 'docs' + - sphinx>=7.2 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==1.4.1 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/15/1a/c6eae628480aa1fc5f6f85437c7d8ec0d1172597acd1c61182202a902c0f/cramjam-2.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: cramjam + version: 2.11.0 + sha256: f6a32313a5fdbc4fc4fd681a1895d55ee4bf81275e88638b1643b54ecf850cbe + requires_dist: + - black==22.3.0 ; extra == 'dev' + - numpy ; extra == 'dev' + - pytest>=5.30 ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pytest-benchmark ; extra == 'dev' + - hypothesis==6.60.0 ; extra == 'dev' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/65/64/e34ee535519fd14cde3a7f3f8cd3b4ef54483b9df655e4180437eb884aab/cramjam-2.11.0-cp38-cp38-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl + name: cramjam + version: 2.11.0 + sha256: bf81b2e517baadf41eb85c4762ae596dd1dd2c852988ce86a2df6aa7e31d9228 + requires_dist: + - black==22.3.0 ; extra == 'dev' + - numpy ; extra == 'dev' + - pytest>=5.30 ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - pytest-benchmark ; extra == 'dev' + - hypothesis==6.60.0 ; extra == 'dev' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + name: cycler + version: 0.12.1 + sha256: 85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 + requires_dist: + - ipython ; extra == 'docs' + - matplotlib ; extra == 'docs' + - numpydoc ; extra == 'docs' + - sphinx ; extra == 'docs' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/7b/3c/67bfc03db3f6527a54a0f9b696809bca55eb6de27928829218ab7e6c40e7/descriptastorus-2.8.0-py3-none-any.whl + name: descriptastorus + version: 2.8.0 + sha256: cc0c8201d6d9d8534dc8a45ce629aeaf4cf1e62ba848b9b1e392b2600ca834dc + requires_dist: + - pandas-flavor + - rdkit + - scipy + - numpy +- pypi: https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl + name: docstring-parser + version: 0.17.0 + sha256: cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708 + requires_dist: + - pre-commit>=2.16.0 ; python_full_version >= '3.9' and extra == 'dev' + - pydoctor>=25.4.0 ; extra == 'dev' + - pytest ; extra == 'dev' + - pydoctor>=25.4.0 ; extra == 'docs' + - pytest ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl + name: docutils + version: 0.20.1 + sha256: 96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl + name: et-xmlfile + version: 2.0.0 + sha256: 7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/27/8e/ce64bc647728ee26bfffa9987fb375e7c99bba54b632404bc0d61b6e9fc2/fastparquet-2024.2.0-cp38-cp38-macosx_11_0_arm64.whl + name: fastparquet + version: 2024.2.0 + sha256: ee36f1ea8f08cb9b8710161eee4e752e74f34ef3e7aebc58db4e5468d29ff34c + requires_dist: + - pandas>=1.5.0 + - numpy>=1.20.3 + - cramjam>=2.3 + - fsspec + - packaging + - python-lzo ; extra == 'lzo' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a7/31/673a677c137d8b2429813f56be77986f44bcb43f5c113336a4c8347e0eb0/fastparquet-2024.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: fastparquet + version: 2024.2.0 + sha256: 6509837887e35bdcb08ba252eeb930b1056e129b6d31c14901443339567ee95a + requires_dist: + - pandas>=1.5.0 + - numpy>=1.20.3 + - cramjam>=2.3 + - fsspec + - packaging + - python-lzo ; extra == 'lzo' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl + name: filelock + version: 3.16.1 + sha256: 2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 + requires_dist: + - furo>=2024.8.6 ; extra == 'docs' + - sphinx-autodoc-typehints>=2.4.1 ; extra == 'docs' + - sphinx>=8.0.2 ; extra == 'docs' + - covdefaults>=2.3 ; extra == 'testing' + - coverage>=7.6.1 ; extra == 'testing' + - diff-cover>=9.2 ; extra == 'testing' + - pytest-asyncio>=0.24 ; extra == 'testing' + - pytest-cov>=5 ; extra == 'testing' + - pytest-mock>=3.14 ; extra == 'testing' + - pytest-timeout>=2.3.1 ; extra == 'testing' + - pytest>=8.3.3 ; extra == 'testing' + - virtualenv>=20.26.4 ; extra == 'testing' + - typing-extensions>=4.12.2 ; python_full_version < '3.11' and extra == 'typing' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/af/6a/00d144ac1626fbb44c4ff36519712e258128985a5d0ae43344778ae5cbb9/Flask-2.1.3-py3-none-any.whl + name: flask + version: 2.1.3 + sha256: 9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c + requires_dist: + - werkzeug>=2.0 + - jinja2>=3.0 + - itsdangerous>=2.0 + - click>=8.0 + - importlib-metadata>=3.6.0 ; python_full_version < '3.10' + - asgiref>=3.2 ; extra == 'async' + - python-dotenv ; extra == 'dotenv' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/8a/3f/c16dbbec7221783f37dcc2022d5a55f0d704ffc9feef67930f6eb517e8ce/fonttools-4.57.0-cp38-cp38-macosx_10_9_universal2.whl + name: fonttools + version: 4.57.0 + sha256: 9d57b4e23ebbe985125d3f0cabbf286efa191ab60bbadb9326091050d88e8213 + requires_dist: + - fs>=2.2.0,<3 ; extra == 'all' + - lxml>=4.0 ; extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.23.0 ; extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - unicodedata2>=15.1.0 ; python_full_version < '3.13' and extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'graphite' + - pycairo ; extra == 'interpolatable' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - lxml>=4.0 ; extra == 'lxml' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - matplotlib ; extra == 'plot' + - uharfbuzz>=0.23.0 ; extra == 'repacker' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - fs>=2.2.0,<3 ; extra == 'ufo' + - unicodedata2>=15.1.0 ; python_full_version < '3.13' and extra == 'unicode' + - zopfli>=0.1.4 ; extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a5/0e/b6314a09a4d561aaa7e09de43fa700917be91e701f07df6178865962666c/fonttools-4.57.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: fonttools + version: 4.57.0 + sha256: a1968f2a2003c97c4ce6308dc2498d5fd4364ad309900930aa5a503c9851aec8 + requires_dist: + - fs>=2.2.0,<3 ; extra == 'all' + - lxml>=4.0 ; extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.23.0 ; extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - unicodedata2>=15.1.0 ; python_full_version < '3.13' and extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'graphite' + - pycairo ; extra == 'interpolatable' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - lxml>=4.0 ; extra == 'lxml' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - matplotlib ; extra == 'plot' + - uharfbuzz>=0.23.0 ; extra == 'repacker' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - fs>=2.2.0,<3 ; extra == 'ufo' + - unicodedata2>=15.1.0 ; python_full_version < '3.13' and extra == 'unicode' + - zopfli>=0.1.4 ; extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/56/53/eb690efa8513166adef3e0669afd31e95ffde69fb3c52ec2ac7223ed6018/fsspec-2025.3.0-py3-none-any.whl + name: fsspec + version: 2025.3.0 + sha256: efb87af3efa9103f94ca91a7f8cb7a4df91af9f74fc106c9c7ea0efd7277c1b3 + requires_dist: + - adlfs ; extra == 'abfs' + - adlfs ; extra == 'adl' + - pyarrow>=1 ; extra == 'arrow' + - dask ; extra == 'dask' + - distributed ; extra == 'dask' + - pre-commit ; extra == 'dev' + - ruff ; extra == 'dev' + - numpydoc ; extra == 'doc' + - sphinx ; extra == 'doc' + - sphinx-design ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - yarl ; extra == 'doc' + - dropbox ; extra == 'dropbox' + - dropboxdrivefs ; extra == 'dropbox' + - requests ; extra == 'dropbox' + - adlfs ; extra == 'full' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'full' + - dask ; extra == 'full' + - distributed ; extra == 'full' + - dropbox ; extra == 'full' + - dropboxdrivefs ; extra == 'full' + - fusepy ; extra == 'full' + - gcsfs ; extra == 'full' + - libarchive-c ; extra == 'full' + - ocifs ; extra == 'full' + - panel ; extra == 'full' + - paramiko ; extra == 'full' + - pyarrow>=1 ; extra == 'full' + - pygit2 ; extra == 'full' + - requests ; extra == 'full' + - s3fs ; extra == 'full' + - smbprotocol ; extra == 'full' + - tqdm ; extra == 'full' + - fusepy ; extra == 'fuse' + - gcsfs ; extra == 'gcs' + - pygit2 ; extra == 'git' + - requests ; extra == 'github' + - gcsfs ; extra == 'gs' + - panel ; extra == 'gui' + - pyarrow>=1 ; extra == 'hdfs' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'http' + - libarchive-c ; extra == 'libarchive' + - ocifs ; extra == 'oci' + - s3fs ; extra == 's3' + - paramiko ; extra == 'sftp' + - smbprotocol ; extra == 'smb' + - paramiko ; extra == 'ssh' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test' + - numpy ; extra == 'test' + - pytest ; extra == 'test' + - pytest-asyncio!=0.22.0 ; extra == 'test' + - pytest-benchmark ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-recording ; extra == 'test' + - pytest-rerunfailures ; extra == 'test' + - requests ; extra == 'test' + - aiobotocore>=2.5.4,<3.0.0 ; extra == 'test-downstream' + - dask[dataframe,test] ; extra == 'test-downstream' + - moto[server]>4,<5 ; extra == 'test-downstream' + - pytest-timeout ; extra == 'test-downstream' + - xarray ; extra == 'test-downstream' + - adlfs ; extra == 'test-full' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test-full' + - cloudpickle ; extra == 'test-full' + - dask ; extra == 'test-full' + - distributed ; extra == 'test-full' + - dropbox ; extra == 'test-full' + - dropboxdrivefs ; extra == 'test-full' + - fastparquet ; extra == 'test-full' + - fusepy ; extra == 'test-full' + - gcsfs ; extra == 'test-full' + - jinja2 ; extra == 'test-full' + - kerchunk ; extra == 'test-full' + - libarchive-c ; extra == 'test-full' + - lz4 ; extra == 'test-full' + - notebook ; extra == 'test-full' + - numpy ; extra == 'test-full' + - ocifs ; extra == 'test-full' + - pandas ; extra == 'test-full' + - panel ; extra == 'test-full' + - paramiko ; extra == 'test-full' + - pyarrow ; extra == 'test-full' + - pyarrow>=1 ; extra == 'test-full' + - pyftpdlib ; extra == 'test-full' + - pygit2 ; extra == 'test-full' + - pytest ; extra == 'test-full' + - pytest-asyncio!=0.22.0 ; extra == 'test-full' + - pytest-benchmark ; extra == 'test-full' + - pytest-cov ; extra == 'test-full' + - pytest-mock ; extra == 'test-full' + - pytest-recording ; extra == 'test-full' + - pytest-rerunfailures ; extra == 'test-full' + - python-snappy ; extra == 'test-full' + - requests ; extra == 'test-full' + - smbprotocol ; extra == 'test-full' + - tqdm ; extra == 'test-full' + - urllib3 ; extra == 'test-full' + - zarr ; extra == 'test-full' + - zstandard ; extra == 'test-full' + - tqdm ; extra == 'tqdm' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/da/71/ae30dadffc90b9006d77af76b393cb9dfbfc9629f339fc1574a1c52e6806/future-1.0.0-py3-none-any.whl + name: future + version: 1.0.0 + sha256: 929292d34f5872e70396626ef385ec22355a1fae8ad29e1a734c3e43f9fbc216 + requires_python: '>=2.6,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/b6/cd/5b3334d39276067f54618ce0d0b48ed69d91352fbf137468c7095170d0e5/hyperopt-0.2.7-py2.py3-none-any.whl + name: hyperopt + version: 0.2.7 + sha256: f3046d91fe4167dbf104365016596856b2524a609d22f047a066fc1ac796427c + requires_dist: + - numpy + - scipy + - six + - networkx>=2.2 + - future + - tqdm + - cloudpickle + - py4j + - lightgbm ; extra == 'atpe' + - scikit-learn ; extra == 'atpe' + - pymongo ; extra == 'mongotrials' + - pyspark ; extra == 'sparktrials' + - black ; extra == 'dev' + - pre-commit ; extra == 'dev' + - nose ; extra == 'dev' + - pytest ; extra == 'dev' +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda + sha256: 142a722072fa96cf16ff98eaaf641f54ab84744af81754c292cb81e0881c0329 + md5: 186a18e3ba246eccfc7cff00cd19a870 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 12728445 + timestamp: 1767969922681 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.2-h38cb7af_0.conda + sha256: d4cefbca587429d1192509edc52c88de52bc96c2447771ddc1f8bee928aed5ef + md5: 1e93aca311da0210e660d2247812fa02 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 12358010 + timestamp: 1767970350308 +- pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl + name: idna + version: '3.11' + sha256: 771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea + requires_dist: + - ruff>=0.6.2 ; extra == 'all' + - mypy>=1.11.2 ; extra == 'all' + - pytest>=8.3.2 ; extra == 'all' + - flake8>=7.1.1 ; extra == 'all' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl + name: imagesize + version: 1.4.1 + sha256: 0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*' +- pypi: https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl + name: importlib-metadata + version: 8.5.0 + sha256: 45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b + requires_dist: + - zipp>=3.20 + - typing-extensions>=3.6.4 ; python_full_version < '3.8' + - pytest-checkdocs>=2.4 ; extra == 'check' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' + - pytest-cov ; extra == 'cover' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pytest-enabler>=2.2 ; extra == 'enabler' + - ipython ; extra == 'perf' + - pytest>=6,!=8.1.* ; extra == 'test' + - packaging ; extra == 'test' + - pyfakefs ; extra == 'test' + - flufl-flake8 ; extra == 'test' + - pytest-perf>=0.9.2 ; extra == 'test' + - jaraco-test>=5.4 ; extra == 'test' + - importlib-resources>=1.3 ; python_full_version < '3.9' and extra == 'test' + - pytest-mypy ; extra == 'type' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/e1/6a/4604f9ae2fa62ef47b9de2fa5ad599589d28c9fd1d335f32759813dfa91e/importlib_resources-6.4.5-py3-none-any.whl + name: importlib-resources + version: 6.4.5 + sha256: ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717 + requires_dist: + - zipp>=3.1.0 ; python_full_version < '3.10' + - pytest-checkdocs>=2.4 ; extra == 'check' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' + - pytest-cov ; extra == 'cover' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pytest-enabler>=2.2 ; extra == 'enabler' + - pytest>=6,!=8.1.* ; extra == 'test' + - zipp>=3.17 ; extra == 'test' + - jaraco-test>=5.4 ; extra == 'test' + - pytest-mypy ; extra == 'type' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl + name: itsdangerous + version: 2.2.0 + sha256: c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + name: jinja2 + version: 3.1.6 + sha256: 85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + requires_dist: + - markupsafe>=2.0 + - babel>=2.7 ; extra == 'i18n' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl + name: joblib + version: 1.4.2 + sha256: 06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6 + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/14/a7/bb8ab10e12cc8764f4da0245d72dee4731cc720bdec0f085d5e9c6005b98/kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl + name: kiwisolver + version: 1.4.7 + sha256: b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/76/36/ae40d7a3171e06f55ac77fe5536079e7be1d8be2a8210e08975c7f9b4d54/kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl + name: kiwisolver + version: 1.4.7 + sha256: a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_105.conda + sha256: 1027bd8aa0d5144e954e426ab6218fd5c14e54a98f571985675468b339c808ca + md5: 3ec0aa5037d39b06554109a01e6fb0c6 + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.45 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 730831 + timestamp: 1766513089214 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + sha256: 25cbdfa65580cfab1b8d15ee90b4c9f1e0d72128f1661449c9a999d341377d54 + md5: 35f29eec58405aaf55e01cb470d8c26a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 57821 + timestamp: 1760295480630 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda + sha256: 9b8acdf42df61b7bfe8bdc545c016c29e61985e79748c64ad66df47dbc2e295f + md5: 411ff7cd5d1472bba0f55c0faf04453b + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 40251 + timestamp: 1760295839166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda + sha256: 6eed58051c2e12b804d53ceff5994a350c61baf117ec83f5f10c953a3f311451 + md5: 6d0363467e6ed84f11435eb309f2ff06 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==15.2.0=*_16 + - libgomp 15.2.0 he0feb66_16 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1042798 + timestamp: 1765256792743 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda + sha256: 5f07f9317f596a201cc6e095e5fc92621afca64829785e483738d935f8cab361 + md5: 5a68259fac2da8f2ee6f7bfe49c9eb8b + depends: + - libgcc 15.2.0 he0feb66_16 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 27256 + timestamp: 1765256804124 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda + sha256: 5b3e5e4e9270ecfcd48f47e3a68f037f5ab0f529ccb223e8e5d5ac75a58fc687 + md5: 26c46f90d0e727e95c6c9498a33a09f3 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 603284 + timestamp: 1765256703881 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 + md5: 1a580f7796c7bf6393fddb8bbbde58dc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz 5.8.1.* + license: 0BSD + purls: [] + size: 112894 + timestamp: 1749230047870 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 + md5: d6df911d4564d77c4374b02552cb17d1 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.1.* + license: 0BSD + purls: [] + size: 92286 + timestamp: 1749230283517 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_2.conda + sha256: 329e66330a8f9cbb6a8d5995005478188eb4ba8a6b6391affa849744f4968492 + md5: f61edadbb301530bd65a32646bd81552 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_2 + license: 0BSD + purls: [] + size: 439868 + timestamp: 1749230061968 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.8.1-h39f12f2_2.conda + sha256: 974804430e24f0b00f3a48b67ec10c9f5441c9bb3d82cc0af51ba45b8a75a241 + md5: 1201137f1a5ec9556032ffc04dcdde8d + depends: + - __osx >=11.0 + - liblzma 5.8.1 h39f12f2_2 + license: 0BSD + purls: [] + size: 116244 + timestamp: 1749230297170 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 + md5: d864d34357c3b65a4b731f78c0801dc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33731 + timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.2-hf4e2dac_0.conda + sha256: 04596fcee262a870e4b7c9807224680ff48d4d0cc0dac076a602503d3dc6d217 + md5: da5be73701eecd0e8454423fd6ffcf30 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.2,<79.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 942808 + timestamp: 1768147973361 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.2-h1ae2325_0.conda + sha256: 6e9b9f269732cbc4698c7984aa5b9682c168e2a8d1e0406e1ff10091ca046167 + md5: 4b0bf313c53c3e89692f020fb55d5f2c + depends: + - __osx >=11.0 + - icu >=78.2,<79.0a0 + - libzlib >=1.3.1,<2.0a0 + license: blessing + purls: [] + size: 909777 + timestamp: 1768148320535 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda + sha256: 813427918316a00c904723f1dfc3da1bbc1974c5cfe1ed1e704c6f4e0798cbc6 + md5: 68f68355000ec3f1d6f26ea13e8f525f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.2.0 he0feb66_16 + constrains: + - libstdcxx-ng ==15.2.0=*_16 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 5856456 + timestamp: 1765256838573 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda + sha256: 1a7539cfa7df00714e8943e18de0b06cceef6778e420a5ee3a2a145773758aee + md5: db409b7c1720428638e7c0d509d3e1b5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 40311 + timestamp: 1766271528534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda + sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c + md5: 5aa797f8787fe7a17d1b0821485b5adc + depends: + - libgcc-ng >=12 + license: LGPL-2.1-or-later + purls: [] + size: 100393 + timestamp: 1702724383534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 60963 + timestamp: 1727963148474 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + purls: [] + size: 46438 + timestamp: 1727963202283 +- pypi: . + name: lnp-ml + version: 0.0.1 + sha256: 07282d62de3ae8b33614654c41729a9b7a300ddb70ce6a7cd53d61218c18f893 + requires_python: '>=3.8' + editable: true +- conda: https://conda.anaconda.org/conda-forge/linux-64/loguru-0.7.2-py38h578d9bd_1.conda + sha256: 63bb68b6de508e7282b196b91ad980b3bc0f508073c9ed08fa85ecaf139f445a + md5: 7efdc20a45de05fd6237c1553c166941 + depends: + - python >=3.8,<3.9.0a0 + - python_abi 3.8.* *_cp38 + license: MIT + license_family: MIT + purls: + - pkg:pypi/loguru?source=hash-mapping + size: 97279 + timestamp: 1695547470382 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/loguru-0.7.2-py38h10201cd_1.conda + sha256: 21ff138566123e2ea085564dd8b78c37c96c0a8b0929351ab8a8979443808ff7 + md5: 9c9f9d27ed59f85fe6162ddf7207429f + depends: + - python >=3.8,<3.9.0a0 + - python >=3.8,<3.9.0a0 *_cpython + - python_abi 3.8.* *_cp38 + license: MIT + license_family: MIT + purls: + - pkg:pypi/loguru?source=hash-mapping + size: 97617 + timestamp: 1695547715271 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_0.conda + sha256: c041b0eaf7a6af3344d5dd452815cdc148d6284fec25a4fa3f4263b3a021e962 + md5: 93a8e71256479c62074356ef6ebf501b + depends: + - mdurl >=0.1,<1 + - python >=3.8 + license: MIT + license_family: MIT + purls: + - pkg:pypi/markdown-it-py?source=hash-mapping + size: 64356 + timestamp: 1686175179621 +- pypi: https://files.pythonhosted.org/packages/c7/bd/50319665ce81bb10e90d1cf76f9e1aa269ea6f7fa30ab4521f14d122a3df/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: markupsafe + version: 2.1.5 + sha256: fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/f8/ff/2c942a82c35a49df5de3a630ce0a8456ac2969691b230e530ac12314364c/MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl + name: markupsafe + version: 2.1.5 + sha256: 656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/30/33/cc27211d2ffeee4fd7402dca137b6e8a83f6dcae3d4be8d0ad5068555561/matplotlib-3.7.5-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl + name: matplotlib + version: 3.7.5 + sha256: 53e64522934df6e1818b25fd48cf3b645b11740d78e6ef765fbb5fa5ce080d02 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.22.0 + - kiwisolver>=1.0.1 + - numpy>=1.20,<2 + - packaging>=20.0 + - pillow>=6.2.0 + - pyparsing>=2.3.1 + - python-dateutil>=2.7 + - importlib-resources>=3.2.0 ; python_full_version < '3.10' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/aa/59/4d13e5b6298b1ca5525eea8c68d3806ae93ab6d0bb17ca9846aa3156b92b/matplotlib-3.7.5-cp38-cp38-macosx_11_0_arm64.whl + name: matplotlib + version: 3.7.5 + sha256: 4cdf4ef46c2a1609a50411b66940b31778db1e4b73d4ecc2eaa40bd588979b13 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.22.0 + - kiwisolver>=1.0.1 + - numpy>=1.20,<2 + - packaging>=20.0 + - pillow>=6.2.0 + - pyparsing>=2.3.1 + - python-dateutil>=2.7 + - importlib-resources>=3.2.0 ; python_full_version < '3.10' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_0.conda + sha256: 64073dfb6bb429d52fff30891877b48c7ec0f89625b1bf844905b66a81cce6e1 + md5: 776a8dd9e824f77abac30e6ef43a8f7a + depends: + - python >=3.6 + license: MIT + license_family: MIT + purls: + - pkg:pypi/mdurl?source=hash-mapping + size: 14680 + timestamp: 1704317789138 +- pypi: https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl + name: mpmath + version: 1.3.0 + sha256: a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c + requires_dist: + - pytest>=4.6 ; extra == 'develop' + - pycodestyle ; extra == 'develop' + - pytest-cov ; extra == 'develop' + - codecov ; extra == 'develop' + - wheel ; extra == 'develop' + - sphinx ; extra == 'docs' + - gmpy2>=2.1.0a4 ; platform_python_implementation != 'PyPy' and extra == 'gmpy' + - pytest>=4.6 ; extra == 'tests' +- pypi: https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl + name: mypy-extensions + version: 1.1.0 + sha256: 1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 + md5: 47e340acb35de30501a76c7c799c41d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: X11 AND BSD-3-Clause + purls: [] + size: 891641 + timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + sha256: 2827ada40e8d9ca69a153a45f7fd14f32b2ead7045d3bbb5d10964898fe65733 + md5: 068d497125e4bf8a66bf707254fff5ae + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + purls: [] + size: 797030 + timestamp: 1738196177597 +- pypi: https://files.pythonhosted.org/packages/a8/05/9d4f9b78ead6b2661d6e8ea772e111fc4a9fbd866ad0c81906c11206b55e/networkx-3.1-py3-none-any.whl + name: networkx + version: '3.1' + sha256: 4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36 + requires_dist: + - numpy>=1.20 ; extra == 'default' + - scipy>=1.8 ; extra == 'default' + - matplotlib>=3.4 ; extra == 'default' + - pandas>=1.3 ; extra == 'default' + - pre-commit>=3.2 ; extra == 'developer' + - mypy>=1.1 ; extra == 'developer' + - sphinx>=6.1 ; extra == 'doc' + - pydata-sphinx-theme>=0.13 ; extra == 'doc' + - sphinx-gallery>=0.12 ; extra == 'doc' + - numpydoc>=1.5 ; extra == 'doc' + - pillow>=9.4 ; extra == 'doc' + - nb2plots>=0.6 ; extra == 'doc' + - texext>=0.6.7 ; extra == 'doc' + - lxml>=4.6 ; extra == 'extra' + - pygraphviz>=1.10 ; extra == 'extra' + - pydot>=1.4.2 ; extra == 'extra' + - sympy>=1.10 ; extra == 'extra' + - pytest>=7.2 ; extra == 'test' + - pytest-cov>=4.0 ; extra == 'test' + - codecov>=2.1 ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/98/5d/5738903efe0ecb73e51eb44feafba32bdba2081263d40c5043568ff60faf/numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: numpy + version: 1.24.4 + sha256: dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a7/ae/f53b7b265fdc701e663fbb322a8e9d4b14d9cb7b2385f45ddfabfc4327e4/numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl + name: numpy + version: 1.24.4 + sha256: 04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/37/6d/121efd7382d5b0284239f4ab1fc1590d86d34ed4a4a2fdb13b30ca8e5740/nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl + name: nvidia-cublas-cu12 + version: 12.1.3.1 + sha256: ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/7e/00/6b218edd739ecfc60524e585ba8e6b00554dd908de2c9c66c1af3e44e18d/nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + name: nvidia-cuda-cupti-cu12 + version: 12.1.105 + sha256: e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/b6/9f/c64c03f49d6fbc56196664d05dba14e3a561038a81a638eeb47f4d4cfd48/nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + name: nvidia-cuda-nvrtc-cu12 + version: 12.1.105 + sha256: 339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/eb/d5/c68b1d2cdfcc59e72e8a5949a37ddb22ae6cade80cd4a57a84d4c8b55472/nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + name: nvidia-cuda-runtime-cu12 + version: 12.1.105 + sha256: 6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl + name: nvidia-cudnn-cu12 + version: 9.1.0.70 + sha256: 165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f + requires_dist: + - nvidia-cublas-cu12 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/86/94/eb540db023ce1d162e7bea9f8f5aa781d57c65aed513c33ee9a5123ead4d/nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl + name: nvidia-cufft-cu12 + version: 11.0.2.54 + sha256: 794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/44/31/4890b1c9abc496303412947fc7dcea3d14861720642b49e8ceed89636705/nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl + name: nvidia-curand-cu12 + version: 10.3.2.106 + sha256: 9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl + name: nvidia-cusolver-cu12 + version: 11.4.5.107 + sha256: 8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd + requires_dist: + - nvidia-cublas-cu12 + - nvidia-nvjitlink-cu12 + - nvidia-cusparse-cu12 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl + name: nvidia-cusparse-cu12 + version: 12.1.0.106 + sha256: f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c + requires_dist: + - nvidia-nvjitlink-cu12 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/4b/2a/0a131f572aa09f741c30ccd45a8e56316e8be8dfc7bc19bf0ab7cfef7b19/nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl + name: nvidia-nccl-cu12 + version: 2.20.5 + sha256: 057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/46/0c/c75bbfb967457a0b7670b8ad267bfc4fffdf341c074e0a80db06c24ccfd4/nvidia_nvjitlink_cu12-12.9.86-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl + name: nvidia-nvjitlink-cu12 + version: 12.9.86 + sha256: e3f1171dbdc83c5932a45f0f4c99180a70de9bd2718c1ab77d14104f6d7147f9 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/da/d3/8057f0587683ed2fcd4dbfbdfdfa807b9160b809976099d36b8f60d08f03/nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl + name: nvidia-nvtx-cu12 + version: 12.1.105 + sha256: dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5 + requires_python: '>=3' +- pypi: https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl + name: openpyxl + version: 3.1.5 + sha256: 5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2 + requires_dist: + - et-xmlfile + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + sha256: a47271202f4518a484956968335b2521409c8173e123ab381e775c358c67fe6d + md5: 9ee58d5c534af06558933af3c845a780 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3165399 + timestamp: 1762839186699 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda + sha256: ebe93dafcc09e099782fe3907485d4e1671296bc14f8c383cb6f3dfebb773988 + md5: b34dc4172653c13dcf453862f251af2b + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3108371 + timestamp: 1762839712322 +- pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl + name: packaging + version: '25.0' + sha256: 29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/53/c3/f8e87361f7fdf42012def602bfa2a593423c729f5cb7c97aed7f51be66ac/pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl + name: pandas + version: 2.0.3 + sha256: 32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5 + requires_dist: + - python-dateutil>=2.8.2 + - pytz>=2020.1 + - tzdata>=2022.1 + - numpy>=1.20.3 ; python_full_version < '3.10' + - numpy>=1.21.0 ; python_full_version >= '3.10' + - numpy>=1.23.2 ; python_full_version >= '3.11' + - beautifulsoup4>=4.9.3 ; extra == 'all' + - bottleneck>=1.3.2 ; extra == 'all' + - brotlipy>=0.7.0 ; extra == 'all' + - fastparquet>=0.6.3 ; extra == 'all' + - fsspec>=2021.7.0 ; extra == 'all' + - gcsfs>=2021.7.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.34.2 ; extra == 'all' + - jinja2>=3.0.0 ; extra == 'all' + - lxml>=4.6.3 ; extra == 'all' + - matplotlib>=3.6.1 ; extra == 'all' + - numba>=0.53.1 ; extra == 'all' + - numexpr>=2.7.3 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.0.7 ; extra == 'all' + - pandas-gbq>=0.15.0 ; extra == 'all' + - psycopg2>=2.8.6 ; extra == 'all' + - pyarrow>=7.0.0 ; extra == 'all' + - pymysql>=1.0.2 ; extra == 'all' + - pyqt5>=5.15.1 ; extra == 'all' + - pyreadstat>=1.1.2 ; extra == 'all' + - pytest>=7.3.2 ; extra == 'all' + - pytest-xdist>=2.2.0 ; extra == 'all' + - pytest-asyncio>=0.17.0 ; extra == 'all' + - python-snappy>=0.6.0 ; extra == 'all' + - pyxlsb>=1.0.8 ; extra == 'all' + - qtpy>=2.2.0 ; extra == 'all' + - scipy>=1.7.1 ; extra == 'all' + - s3fs>=2021.8.0 ; extra == 'all' + - sqlalchemy>=1.4.16 ; extra == 'all' + - tables>=3.6.1 ; extra == 'all' + - tabulate>=0.8.9 ; extra == 'all' + - xarray>=0.21.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=1.4.3 ; extra == 'all' + - zstandard>=0.15.2 ; extra == 'all' + - s3fs>=2021.8.0 ; extra == 'aws' + - pyqt5>=5.15.1 ; extra == 'clipboard' + - qtpy>=2.2.0 ; extra == 'clipboard' + - brotlipy>=0.7.0 ; extra == 'compression' + - python-snappy>=0.6.0 ; extra == 'compression' + - zstandard>=0.15.2 ; extra == 'compression' + - scipy>=1.7.1 ; extra == 'computation' + - xarray>=0.21.0 ; extra == 'computation' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.0.7 ; extra == 'excel' + - pyxlsb>=1.0.8 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=1.4.3 ; extra == 'excel' + - pyarrow>=7.0.0 ; extra == 'feather' + - fsspec>=2021.7.0 ; extra == 'fss' + - gcsfs>=2021.7.0 ; extra == 'gcp' + - pandas-gbq>=0.15.0 ; extra == 'gcp' + - tables>=3.6.1 ; extra == 'hdf5' + - beautifulsoup4>=4.9.3 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=4.6.3 ; extra == 'html' + - sqlalchemy>=1.4.16 ; extra == 'mysql' + - pymysql>=1.0.2 ; extra == 'mysql' + - jinja2>=3.0.0 ; extra == 'output-formatting' + - tabulate>=0.8.9 ; extra == 'output-formatting' + - pyarrow>=7.0.0 ; extra == 'parquet' + - bottleneck>=1.3.2 ; extra == 'performance' + - numba>=0.53.1 ; extra == 'performance' + - numexpr>=2.7.1 ; extra == 'performance' + - matplotlib>=3.6.1 ; extra == 'plot' + - sqlalchemy>=1.4.16 ; extra == 'postgresql' + - psycopg2>=2.8.6 ; extra == 'postgresql' + - pyreadstat>=1.1.2 ; extra == 'spss' + - sqlalchemy>=1.4.16 ; extra == 'sql-other' + - hypothesis>=6.34.2 ; extra == 'test' + - pytest>=7.3.2 ; extra == 'test' + - pytest-xdist>=2.2.0 ; extra == 'test' + - pytest-asyncio>=0.17.0 ; extra == 'test' + - lxml>=4.6.3 ; extra == 'xml' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/f8/7f/5b047effafbdd34e52c9e2d7e44f729a0655efafb22198c45cf692cdc157/pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: pandas + version: 2.0.3 + sha256: 9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0 + requires_dist: + - python-dateutil>=2.8.2 + - pytz>=2020.1 + - tzdata>=2022.1 + - numpy>=1.20.3 ; python_full_version < '3.10' + - numpy>=1.21.0 ; python_full_version >= '3.10' + - numpy>=1.23.2 ; python_full_version >= '3.11' + - beautifulsoup4>=4.9.3 ; extra == 'all' + - bottleneck>=1.3.2 ; extra == 'all' + - brotlipy>=0.7.0 ; extra == 'all' + - fastparquet>=0.6.3 ; extra == 'all' + - fsspec>=2021.7.0 ; extra == 'all' + - gcsfs>=2021.7.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.34.2 ; extra == 'all' + - jinja2>=3.0.0 ; extra == 'all' + - lxml>=4.6.3 ; extra == 'all' + - matplotlib>=3.6.1 ; extra == 'all' + - numba>=0.53.1 ; extra == 'all' + - numexpr>=2.7.3 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.0.7 ; extra == 'all' + - pandas-gbq>=0.15.0 ; extra == 'all' + - psycopg2>=2.8.6 ; extra == 'all' + - pyarrow>=7.0.0 ; extra == 'all' + - pymysql>=1.0.2 ; extra == 'all' + - pyqt5>=5.15.1 ; extra == 'all' + - pyreadstat>=1.1.2 ; extra == 'all' + - pytest>=7.3.2 ; extra == 'all' + - pytest-xdist>=2.2.0 ; extra == 'all' + - pytest-asyncio>=0.17.0 ; extra == 'all' + - python-snappy>=0.6.0 ; extra == 'all' + - pyxlsb>=1.0.8 ; extra == 'all' + - qtpy>=2.2.0 ; extra == 'all' + - scipy>=1.7.1 ; extra == 'all' + - s3fs>=2021.8.0 ; extra == 'all' + - sqlalchemy>=1.4.16 ; extra == 'all' + - tables>=3.6.1 ; extra == 'all' + - tabulate>=0.8.9 ; extra == 'all' + - xarray>=0.21.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=1.4.3 ; extra == 'all' + - zstandard>=0.15.2 ; extra == 'all' + - s3fs>=2021.8.0 ; extra == 'aws' + - pyqt5>=5.15.1 ; extra == 'clipboard' + - qtpy>=2.2.0 ; extra == 'clipboard' + - brotlipy>=0.7.0 ; extra == 'compression' + - python-snappy>=0.6.0 ; extra == 'compression' + - zstandard>=0.15.2 ; extra == 'compression' + - scipy>=1.7.1 ; extra == 'computation' + - xarray>=0.21.0 ; extra == 'computation' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.0.7 ; extra == 'excel' + - pyxlsb>=1.0.8 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=1.4.3 ; extra == 'excel' + - pyarrow>=7.0.0 ; extra == 'feather' + - fsspec>=2021.7.0 ; extra == 'fss' + - gcsfs>=2021.7.0 ; extra == 'gcp' + - pandas-gbq>=0.15.0 ; extra == 'gcp' + - tables>=3.6.1 ; extra == 'hdf5' + - beautifulsoup4>=4.9.3 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=4.6.3 ; extra == 'html' + - sqlalchemy>=1.4.16 ; extra == 'mysql' + - pymysql>=1.0.2 ; extra == 'mysql' + - jinja2>=3.0.0 ; extra == 'output-formatting' + - tabulate>=0.8.9 ; extra == 'output-formatting' + - pyarrow>=7.0.0 ; extra == 'parquet' + - bottleneck>=1.3.2 ; extra == 'performance' + - numba>=0.53.1 ; extra == 'performance' + - numexpr>=2.7.1 ; extra == 'performance' + - matplotlib>=3.6.1 ; extra == 'plot' + - sqlalchemy>=1.4.16 ; extra == 'postgresql' + - psycopg2>=2.8.6 ; extra == 'postgresql' + - pyreadstat>=1.1.2 ; extra == 'spss' + - sqlalchemy>=1.4.16 ; extra == 'sql-other' + - hypothesis>=6.34.2 ; extra == 'test' + - pytest>=7.3.2 ; extra == 'test' + - pytest-xdist>=2.2.0 ; extra == 'test' + - pytest-asyncio>=0.17.0 ; extra == 'test' + - lxml>=4.6.3 ; extra == 'xml' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/5d/e6/71ed4d95676098159b533c4a4c424cf453fec9614edaff1a0633fe228eef/pandas_flavor-0.7.0-py3-none-any.whl + name: pandas-flavor + version: 0.7.0 + sha256: 7ee81e834b111e424679776f49c51abcffec88203b3ff0df2c9cb75550e06b1a + requires_dist: + - pandas>=0.23 + - xarray +- pypi: https://files.pythonhosted.org/packages/10/43/105823d233c5e5d31cea13428f4474ded9d961652307800979a59d6a4276/pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl + name: pillow + version: 10.4.0 + sha256: 7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=7.3 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - check-manifest ; extra == 'tests' + - coverage ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pyroma ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - typing-extensions ; python_full_version < '3.10' and extra == 'typing' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/fb/ad/435fe29865f98a8fbdc64add8875a6e4f8c97749a93577a8919ec6f32c64/pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl + name: pillow + version: 10.4.0 + sha256: 950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=7.3 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - check-manifest ; extra == 'tests' + - coverage ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pyroma ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - typing-extensions ; python_full_version < '3.10' and extra == 'typing' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda + sha256: 499313e72e20225f84c2e9690bbaf5b952c8d7e0bf34b728278538f766b81628 + md5: 5dd546fe99b44fda83963d15f84263b7 + depends: + - python >=3.8,<3.13.0a0 + - setuptools + - wheel + license: MIT + license_family: MIT + purls: + - pkg:pypi/pip?source=hash-mapping + size: 1243168 + timestamp: 1730203795600 +- pypi: https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl + name: protobuf + version: 5.29.5 + sha256: 63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61 + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl + name: protobuf + version: 5.29.5 + sha256: e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671 + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/bd/db/ea0203e495be491c85af87b66e37acfd3bf756fd985f87e46fc5e3bf022c/py4j-0.10.9.9-py2.py3-none-any.whl + name: py4j + version: 0.10.9.9 + sha256: c7c26e4158defb37b0bb124933163641a2ff6e3a3913f7811b0ddbe07ed61533 +- pypi: https://files.pythonhosted.org/packages/64/d9/51e35550f2f18b8815a2ab25948f735434db32000c0e91eba3a32634782a/pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl + name: pyarrow + version: 17.0.0 + sha256: edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8 + requires_dist: + - numpy>=1.16.6 + - pytest ; extra == 'test' + - hypothesis ; extra == 'test' + - cffi ; extra == 'test' + - pytz ; extra == 'test' + - pandas ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/e6/c1/4c6bcdf7a820034aa91a8b4d25fef38809be79b42ca7aaa16d4680b0bbac/pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl + name: pyarrow + version: 17.0.0 + sha256: d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c + requires_dist: + - numpy>=1.16.6 + - pytest ; extra == 'test' + - hypothesis ; extra == 'test' + - cffi ; extra == 'test' + - pytz ; extra == 'test' + - pandas ; extra == 'test' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda + sha256: 78267adf4e76d0d64ea2ffab008c501156c108bb08fecb703816fb63e279780b + md5: b7f5c092b8f9800150d998a71b76d5a1 + depends: + - python >=3.8 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=hash-mapping + size: 879295 + timestamp: 1714846885370 +- pypi: https://files.pythonhosted.org/packages/e5/0c/0e3c05b1c87bb6a1c76d281b0f35e78d2d80ac91b5f8f524cebf77f51049/pyparsing-3.1.4-py3-none-any.whl + name: pyparsing + version: 3.1.4 + sha256: a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c + requires_dist: + - railroad-diagrams ; extra == 'diagrams' + - jinja2 ; extra == 'diagrams' + requires_python: '>=3.6.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.8.20-h4a871b0_2_cpython.conda + build_number: 2 + sha256: 8043dcdb29e1e026d0def1056620d81b24c04f71fd98cc45888c58373b479845 + md5: 05ffff2f44ad60b94ecb53d029c6bdf7 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libffi >=3.4,<4.0a0 + - libgcc >=13 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.46.1,<4.0a0 + - libuuid >=2.38.1,<3.0a0 + - libxcrypt >=4.4.36 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.3.2,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - xz >=5.2.6,<6.0a0 + constrains: + - python_abi 3.8.* *_cp38 + license: Python-2.0 + purls: [] + size: 22176012 + timestamp: 1727719857908 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.8.20-h7d35d02_2_cpython.conda + build_number: 2 + sha256: cf8692e732697d47f0290ef83caa4b3115c7b277a3fb155b7de0f09fa1b5e27c + md5: 29ed2994beffea2a256a7e14f9468df8 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libffi >=3.4,<4.0a0 + - libsqlite >=3.46.1,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.3.2,<4.0a0 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - xz >=5.2.6,<6.0a0 + constrains: + - python_abi 3.8.* *_cp38 + license: Python-2.0 + purls: [] + size: 11774160 + timestamp: 1727718758277 +- pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + name: python-dateutil + version: 2.9.0.post0 + sha256: a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + requires_dist: + - six>=1.5 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl + name: python-dotenv + version: 1.0.1 + sha256: f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a + requires_dist: + - click>=5.0 ; extra == 'cli' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.8-8_cp38.conda + build_number: 8 + sha256: 83c22066a672ce0b16e693c84aa6d5efb68e02eff037a55e047d7095d0fdb5ca + md5: 4f7b6e3de4f15cc44e0f93b39f07205d + constrains: + - python 3.8.* *_cpython + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6960 + timestamp: 1752805923703 +- pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl + name: pytz + version: '2025.2' + sha256: 5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00 +- pypi: https://files.pythonhosted.org/packages/3d/84/63b2e66f5c7cb97ce994769afbbef85a1ac364fedbcb7d4a3c0f15d318a5/rdkit-2024.3.5-cp38-cp38-manylinux_2_28_x86_64.whl + name: rdkit + version: 2024.3.5 + sha256: f662ed10e14c7874d0e011c1f8260a6cf4a5547100806f9f4540f8550a3f1d36 + requires_dist: + - numpy + - pillow +- pypi: https://files.pythonhosted.org/packages/bf/cb/c709b60f4815e18c00e1e8639204bdba04cb158e6278791d82f94f51a988/rdkit-2024.3.5-cp38-cp38-macosx_11_0_arm64.whl + name: rdkit + version: 2024.3.5 + sha256: 53638d0d94f7c511273ff5765abc6a1fe60fe6c8633db13cad03bc527550a7a9 + requires_dist: + - numpy + - pillow +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 + md5: d7d95fc8287ea7bf33e0e7116d2b95ec + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 345073 + timestamp: 1765813471974 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 + md5: f8381319127120ce51e081dce4865cf4 + depends: + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 313930 + timestamp: 1765813902568 +- pypi: https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl + name: requests + version: 2.32.4 + sha256: 27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c + requires_dist: + - charset-normalizer>=2,<4 + - idna>=2.5,<4 + - urllib3>=1.21.1,<3 + - certifi>=2017.4.17 + - pysocks>=1.5.6,!=1.5.7 ; extra == 'socks' + - chardet>=3.0.2,<6 ; extra == 'use-chardet-on-py3' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-13.9.4-pyhd8ed1ab_0.conda + sha256: c009488fc07fd5557434c9c1ad32ab1dd50241d6a766e4b2b4125cd6498585a8 + md5: bcf8cc8924b5d20ead3d122130b8320b + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.8 + - typing_extensions >=4.0.0,<5.0.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/rich?source=hash-mapping + size: 185481 + timestamp: 1730592349978 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.12-h4196e79_0.conda + noarch: python + sha256: 26c9f201a249cf54acacfa5055e96ad6e8e272cac3a25ac7caee8fe048e66b5a + md5: b8b77763e87e13ecac834a190155600b + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ruff?source=compressed-mapping + size: 11513418 + timestamp: 1768513907806 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.12-hb0cad00_0.conda + noarch: python + sha256: 8da228ab3930cf1fbf52bd00e2b141af458a04ef698122d9beff151d6517fa84 + md5: b7049a4bfa0de48b8d27f158c1e9daf7 + depends: + - python + - __osx >=11.0 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/ruff?source=compressed-mapping + size: 10452516 + timestamp: 1768513987014 +- pypi: https://files.pythonhosted.org/packages/3f/48/6fdd99f5717045f9984616b5c2ec683d6286d30c0ac234563062132b83ab/scikit_learn-1.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: scikit-learn + version: 1.3.2 + sha256: 785a2213086b7b1abf037aeadbbd6d67159feb3e30263434139c98425e3dcfcf + requires_dist: + - numpy>=1.17.3,<2.0 + - scipy>=1.5.0 + - joblib>=1.1.1 + - threadpoolctl>=2.0.0 + - matplotlib>=3.1.3 ; extra == 'benchmark' + - pandas>=1.0.5 ; extra == 'benchmark' + - memory-profiler>=0.57.0 ; extra == 'benchmark' + - matplotlib>=3.1.3 ; extra == 'docs' + - scikit-image>=0.16.2 ; extra == 'docs' + - pandas>=1.0.5 ; extra == 'docs' + - seaborn>=0.9.0 ; extra == 'docs' + - memory-profiler>=0.57.0 ; extra == 'docs' + - sphinx>=6.0.0 ; extra == 'docs' + - sphinx-copybutton>=0.5.2 ; extra == 'docs' + - sphinx-gallery>=0.10.1 ; extra == 'docs' + - numpydoc>=1.2.0 ; extra == 'docs' + - pillow>=7.1.2 ; extra == 'docs' + - pooch>=1.6.0 ; extra == 'docs' + - sphinx-prompt>=1.3.0 ; extra == 'docs' + - sphinxext-opengraph>=0.4.2 ; extra == 'docs' + - plotly>=5.14.0 ; extra == 'docs' + - matplotlib>=3.1.3 ; extra == 'examples' + - scikit-image>=0.16.2 ; extra == 'examples' + - pandas>=1.0.5 ; extra == 'examples' + - seaborn>=0.9.0 ; extra == 'examples' + - pooch>=1.6.0 ; extra == 'examples' + - plotly>=5.14.0 ; extra == 'examples' + - matplotlib>=3.1.3 ; extra == 'tests' + - scikit-image>=0.16.2 ; extra == 'tests' + - pandas>=1.0.5 ; extra == 'tests' + - pytest>=7.1.2 ; extra == 'tests' + - pytest-cov>=2.9.0 ; extra == 'tests' + - ruff>=0.0.272 ; extra == 'tests' + - black>=23.3.0 ; extra == 'tests' + - mypy>=1.3 ; extra == 'tests' + - pyamg>=4.0.0 ; extra == 'tests' + - numpydoc>=1.2.0 ; extra == 'tests' + - pooch>=1.6.0 ; extra == 'tests' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a4/62/92e9cec3deca8b45abf62dd8f6469d688b3f28b9c170809fcc46f110b523/scikit_learn-1.3.2-cp38-cp38-macosx_12_0_arm64.whl + name: scikit-learn + version: 1.3.2 + sha256: b8692e395a03a60cd927125eef3a8e3424d86dde9b2370d544f0ea35f78a8073 + requires_dist: + - numpy>=1.17.3,<2.0 + - scipy>=1.5.0 + - joblib>=1.1.1 + - threadpoolctl>=2.0.0 + - matplotlib>=3.1.3 ; extra == 'benchmark' + - pandas>=1.0.5 ; extra == 'benchmark' + - memory-profiler>=0.57.0 ; extra == 'benchmark' + - matplotlib>=3.1.3 ; extra == 'docs' + - scikit-image>=0.16.2 ; extra == 'docs' + - pandas>=1.0.5 ; extra == 'docs' + - seaborn>=0.9.0 ; extra == 'docs' + - memory-profiler>=0.57.0 ; extra == 'docs' + - sphinx>=6.0.0 ; extra == 'docs' + - sphinx-copybutton>=0.5.2 ; extra == 'docs' + - sphinx-gallery>=0.10.1 ; extra == 'docs' + - numpydoc>=1.2.0 ; extra == 'docs' + - pillow>=7.1.2 ; extra == 'docs' + - pooch>=1.6.0 ; extra == 'docs' + - sphinx-prompt>=1.3.0 ; extra == 'docs' + - sphinxext-opengraph>=0.4.2 ; extra == 'docs' + - plotly>=5.14.0 ; extra == 'docs' + - matplotlib>=3.1.3 ; extra == 'examples' + - scikit-image>=0.16.2 ; extra == 'examples' + - pandas>=1.0.5 ; extra == 'examples' + - seaborn>=0.9.0 ; extra == 'examples' + - pooch>=1.6.0 ; extra == 'examples' + - plotly>=5.14.0 ; extra == 'examples' + - matplotlib>=3.1.3 ; extra == 'tests' + - scikit-image>=0.16.2 ; extra == 'tests' + - pandas>=1.0.5 ; extra == 'tests' + - pytest>=7.1.2 ; extra == 'tests' + - pytest-cov>=2.9.0 ; extra == 'tests' + - ruff>=0.0.272 ; extra == 'tests' + - black>=23.3.0 ; extra == 'tests' + - mypy>=1.3 ; extra == 'tests' + - pyamg>=4.0.0 ; extra == 'tests' + - numpydoc>=1.2.0 ; extra == 'tests' + - pooch>=1.6.0 ; extra == 'tests' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/69/f0/fb07a9548e48b687b8bf2fa81d71aba9cfc548d365046ca1c791e24db99d/scipy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: scipy + version: 1.10.1 + sha256: 07c3457ce0b3ad5124f98a86533106b643dd811dd61b548e78cf4c8786652f6f + requires_dist: + - numpy>=1.19.5,<1.27.0 + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-xdist ; extra == 'test' + - asv ; extra == 'test' + - mpmath ; extra == 'test' + - gmpy2 ; extra == 'test' + - threadpoolctl ; extra == 'test' + - scikit-umfpack ; extra == 'test' + - pooch ; extra == 'test' + - sphinx!=4.1.0 ; extra == 'doc' + - pydata-sphinx-theme==0.9.0 ; extra == 'doc' + - sphinx-design>=0.2.0 ; extra == 'doc' + - matplotlib>2 ; extra == 'doc' + - numpydoc ; extra == 'doc' + - mypy ; extra == 'dev' + - typing-extensions ; extra == 'dev' + - pycodestyle ; extra == 'dev' + - flake8 ; extra == 'dev' + - rich-click ; extra == 'dev' + - click ; extra == 'dev' + - doit>=0.36.0 ; extra == 'dev' + - pydevtool ; extra == 'dev' + requires_python: '>=3.8,<3.12' +- pypi: https://files.pythonhosted.org/packages/93/4a/50c436de1353cce8b66b26e49a687f10b91fe7465bf34e4565d810153003/scipy-1.10.1-cp38-cp38-macosx_12_0_arm64.whl + name: scipy + version: 1.10.1 + sha256: 39becb03541f9e58243f4197584286e339029e8908c46f7221abeea4b749fa88 + requires_dist: + - numpy>=1.19.5,<1.27.0 + - pytest ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-timeout ; extra == 'test' + - pytest-xdist ; extra == 'test' + - asv ; extra == 'test' + - mpmath ; extra == 'test' + - gmpy2 ; extra == 'test' + - threadpoolctl ; extra == 'test' + - scikit-umfpack ; extra == 'test' + - pooch ; extra == 'test' + - sphinx!=4.1.0 ; extra == 'doc' + - pydata-sphinx-theme==0.9.0 ; extra == 'doc' + - sphinx-design>=0.2.0 ; extra == 'doc' + - matplotlib>2 ; extra == 'doc' + - numpydoc ; extra == 'doc' + - mypy ; extra == 'dev' + - typing-extensions ; extra == 'dev' + - pycodestyle ; extra == 'dev' + - flake8 ; extra == 'dev' + - rich-click ; extra == 'dev' + - click ; extra == 'dev' + - doit>=0.36.0 ; extra == 'dev' + - pydevtool ; extra == 'dev' + requires_python: '>=3.8,<3.12' +- conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda + sha256: a36d020b9f32fc3f1a6488a1c4a9c13988c6468faf6895bf30ca69521a61230e + md5: 2ce9825396daf72baabaade36cee16da + depends: + - python >=3.8 + license: MIT + license_family: MIT + purls: + - pkg:pypi/setuptools?source=hash-mapping + size: 779561 + timestamp: 1730382173961 +- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_0.conda + sha256: 3c49a0a101c41b7cf6ac05a1872d7a1f91f1b6d02eecb4a36b605a19517862bb + md5: d08db09a552699ee9e7eec56b4eb3899 + depends: + - python >=3.7 + license: MIT + license_family: MIT + purls: + - pkg:pypi/shellingham?source=hash-mapping + size: 14568 + timestamp: 1698144516278 +- pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + name: six + version: 1.17.0 + sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl + name: snowballstemmer + version: 3.0.1 + sha256: 6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064 + requires_python: '!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/48/17/325cf6a257d84751a48ae90752b3d8fe0be8f9535b6253add61c49d0d9bc/sphinx-7.1.2-py3-none-any.whl + name: sphinx + version: 7.1.2 + sha256: d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe + requires_dist: + - sphinxcontrib-applehelp + - sphinxcontrib-devhelp + - sphinxcontrib-jsmath + - sphinxcontrib-htmlhelp>=2.0.0 + - sphinxcontrib-serializinghtml>=1.1.5 + - sphinxcontrib-qthelp + - jinja2>=3.0 + - pygments>=2.13 + - docutils>=0.18.1,<0.21 + - snowballstemmer>=2.0 + - babel>=2.9 + - alabaster>=0.7,<0.8 + - imagesize>=1.3 + - requests>=2.25.0 + - packaging>=21.0 + - importlib-metadata>=4.8 ; python_full_version < '3.10' + - colorama>=0.4.5 ; sys_platform == 'win32' + - sphinxcontrib-websupport ; extra == 'docs' + - flake8>=3.5.0 ; extra == 'lint' + - flake8-simplify ; extra == 'lint' + - isort ; extra == 'lint' + - ruff ; extra == 'lint' + - mypy>=0.990 ; extra == 'lint' + - sphinx-lint ; extra == 'lint' + - docutils-stubs ; extra == 'lint' + - types-requests ; extra == 'lint' + - pytest>=4.6 ; extra == 'test' + - html5lib ; extra == 'test' + - cython ; extra == 'test' + - filelock ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/87/c7/b5c8015d823bfda1a346adb2c634a2101d50bb75d421eb6dcb31acd25ebc/sphinx_rtd_theme-3.1.0-py2.py3-none-any.whl + name: sphinx-rtd-theme + version: 3.1.0 + sha256: 1785824ae8e6632060490f67cf3a72d404a85d2d9fc26bce3619944de5682b89 + requires_dist: + - sphinx>=6,<10 + - docutils>0.18,<0.23 + - sphinxcontrib-jquery>=4,<5 + - transifex-client ; extra == 'dev' + - bump2version ; extra == 'dev' + - wheel ; extra == 'dev' + - twine ; extra == 'dev' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/06/c1/5e2cafbd03105ce50d8500f9b4e8a6e8d02e22d0475b574c3b3e9451a15f/sphinxcontrib_applehelp-1.0.4-py3-none-any.whl + name: sphinxcontrib-applehelp + version: 1.0.4 + sha256: 29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228 + requires_dist: + - flake8 ; extra == 'lint' + - mypy ; extra == 'lint' + - docutils-stubs ; extra == 'lint' + - pytest ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/c5/09/5de5ed43a521387f18bdf5f5af31d099605c992fd25372b2b9b825ce48ee/sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl + name: sphinxcontrib-devhelp + version: 1.0.2 + sha256: 8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e + requires_dist: + - flake8 ; extra == 'lint' + - mypy ; extra == 'lint' + - docutils-stubs ; extra == 'lint' + - pytest ; extra == 'test' + requires_python: '>=3.5' +- pypi: https://files.pythonhosted.org/packages/6e/ee/a1f5e39046cbb5f8bc8fba87d1ddf1c6643fbc9194e58d26e606de4b9074/sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl + name: sphinxcontrib-htmlhelp + version: 2.0.1 + sha256: c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903 + requires_dist: + - flake8 ; extra == 'lint' + - mypy ; extra == 'lint' + - docutils-stubs ; extra == 'lint' + - pytest ; extra == 'test' + - html5lib ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl + name: sphinxcontrib-jquery + version: '4.1' + sha256: f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae + requires_dist: + - sphinx>=1.8 + requires_python: '>=2.7' +- pypi: https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl + name: sphinxcontrib-jsmath + version: 1.0.1 + sha256: 2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 + requires_dist: + - pytest ; extra == 'test' + - flake8 ; extra == 'test' + - mypy ; extra == 'test' + requires_python: '>=3.5' +- pypi: https://files.pythonhosted.org/packages/2b/14/05f9206cf4e9cfca1afb5fd224c7cd434dcc3a433d6d9e4e0264d29c6cdb/sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl + name: sphinxcontrib-qthelp + version: 1.0.3 + sha256: bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6 + requires_dist: + - flake8 ; extra == 'lint' + - mypy ; extra == 'lint' + - docutils-stubs ; extra == 'lint' + - pytest ; extra == 'test' + requires_python: '>=3.5' +- pypi: https://files.pythonhosted.org/packages/c6/77/5464ec50dd0f1c1037e3c93249b040c8fc8078fdda97530eeb02424b6eea/sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl + name: sphinxcontrib-serializinghtml + version: 1.1.5 + sha256: 352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd + requires_dist: + - flake8 ; extra == 'lint' + - mypy ; extra == 'lint' + - docutils-stubs ; extra == 'lint' + - pytest ; extra == 'test' + requires_python: '>=3.5' +- pypi: https://files.pythonhosted.org/packages/99/ff/c87e0622b1dadea79d2fb0b25ade9ed98954c9033722eb707053d310d4f3/sympy-1.13.3-py3-none-any.whl + name: sympy + version: 1.13.3 + sha256: 54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73 + requires_dist: + - mpmath>=1.1.0,<1.4 + - pytest>=7.1.0 ; extra == 'dev' + - hypothesis>=6.70.0 ; extra == 'dev' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/44/71/f3e7c9b2ab67e28c572ab4e9d5fa3499e0d252650f96d8a3a03e26677f53/tensorboardX-2.6.2.2-py2.py3-none-any.whl + name: tensorboardx + version: 2.6.2.2 + sha256: 160025acbf759ede23fd3526ae9d9bfbfd8b68eb16c38a010ebe326dc6395db8 + requires_dist: + - numpy + - packaging + - protobuf>=3.20 +- pypi: https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl + name: threadpoolctl + version: 3.5.0 + sha256: 56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467 + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + sha256: 1544760538a40bcd8ace2b1d8ebe3eb5807ac268641f8acdc18c69c5ebfeaf64 + md5: 86bc20552bf46075e3d92b67f089172d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + constrains: + - xorg-libx11 >=1.8.12,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3284905 + timestamp: 1763054914403 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda + sha256: ad0c67cb03c163a109820dc9ecf77faf6ec7150e942d1e8bb13e5d39dc058ab7 + md5: a73d54a5abba6543cb2f0af1bfbd6851 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + purls: [] + size: 3125484 + timestamp: 1763055028377 +- pypi: https://files.pythonhosted.org/packages/a9/71/45aac46b75742e08d2d6f9fc2b612223b5e36115b8b2ed673b23c21b5387/torch-2.4.1-cp38-cp38-manylinux1_x86_64.whl + name: torch + version: 2.4.1 + sha256: c99e1db4bf0c5347107845d715b4aa1097e601bdc36343d758963055e9599d93 + requires_dist: + - filelock + - typing-extensions>=4.8.0 + - sympy + - networkx + - jinja2 + - fsspec + - nvidia-cuda-nvrtc-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cuda-runtime-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cuda-cupti-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cudnn-cu12==9.1.0.70 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cublas-cu12==12.1.3.1 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cufft-cu12==11.0.2.54 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-curand-cu12==10.3.2.106 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusolver-cu12==11.4.5.107 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusparse-cu12==12.1.0.106 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nccl-cu12==2.20.5 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nvtx-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - triton==3.0.0 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux' + - opt-einsum>=3.3 ; extra == 'opt-einsum' + - optree>=0.11.0 ; extra == 'optree' + requires_python: '>=3.8.0' +- pypi: https://files.pythonhosted.org/packages/c4/88/4d9f66de5fe732462a2713c9931cab614d3fd6a9b5d9ee1f04768ad64daa/torch-2.4.1-cp38-none-macosx_11_0_arm64.whl + name: torch + version: 2.4.1 + sha256: 5fc1d4d7ed265ef853579caf272686d1ed87cebdcd04f2a498f800ffc53dab71 + requires_dist: + - filelock + - typing-extensions>=4.8.0 + - sympy + - networkx + - jinja2 + - fsspec + - nvidia-cuda-nvrtc-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cuda-runtime-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cuda-cupti-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cudnn-cu12==9.1.0.70 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cublas-cu12==12.1.3.1 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cufft-cu12==11.0.2.54 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-curand-cu12==10.3.2.106 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusolver-cu12==11.4.5.107 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-cusparse-cu12==12.1.0.106 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nccl-cu12==2.20.5 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - nvidia-nvtx-cu12==12.1.105 ; platform_machine == 'x86_64' and sys_platform == 'linux' + - triton==3.0.0 ; python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux' + - opt-einsum>=3.3 ; extra == 'opt-einsum' + - optree>=0.11.0 ; extra == 'optree' + requires_python: '>=3.8.0' +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_0.conda + sha256: 5673b7104350a6998cb86cccf1d0058217d86950e8d6c927d8530606028edb1d + md5: 4085c9db273a148e149c03627350e22c + depends: + - colorama + - python >=3.7 + license: MPL-2.0 or MIT + purls: + - pkg:pypi/tqdm?source=hash-mapping + size: 89484 + timestamp: 1732497312317 +- pypi: https://files.pythonhosted.org/packages/4d/b4/c37e2776a1390bab7e78a6d52bd525441cb3cad7260a6a00b11b0b702e7c/triton-3.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: triton + version: 3.0.0 + sha256: bcbf3b1c48af6a28011a5c40a5b3b9b5330530c3827716b5fbf6d7adcc1e53e9 + requires_dist: + - filelock + - cmake>=3.20 ; extra == 'build' + - lit ; extra == 'build' + - autopep8 ; extra == 'tests' + - flake8 ; extra == 'tests' + - isort ; extra == 'tests' + - numpy ; extra == 'tests' + - pytest ; extra == 'tests' + - scipy>=1.7.1 ; extra == 'tests' + - llnl-hatchet ; extra == 'tests' + - matplotlib ; extra == 'tutorials' + - pandas ; extra == 'tutorials' + - tabulate ; extra == 'tutorials' +- pypi: https://files.pythonhosted.org/packages/ea/07/0055bb513de2b7cf23b2bfc7eeeb6a6ae6ff7b287e2a62ab80cf403d61e9/typed_argument_parser-1.10.1-py3-none-any.whl + name: typed-argument-parser + version: 1.10.1 + sha256: 152cad218c84f23d9d4cbbfcb153a1a53fa0b330f0f7a4b85190dd4cb86f0c0a + requires_dist: + - docstring-parser>=0.15 + - packaging + - typing-inspect>=0.7.1 + - typed-argument-parser[dev-no-pydantic] ; extra == 'dev' + - pydantic>=2.5.0 ; extra == 'dev' + - pytest ; extra == 'dev-no-pydantic' + - pytest-cov ; extra == 'dev-no-pydantic' + - flake8 ; extra == 'dev-no-pydantic' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.12.5-pyhd8ed1ab_0.conda + sha256: da9ff9e27c5fa8268c2d5898335485a897d9496eef3b5b446cd9387a89d168de + md5: be70216cc1a5fe502c849676baabf498 + depends: + - python >=3.7 + - typer-slim-standard 0.12.5 hd8ed1ab_0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typer?source=hash-mapping + size: 53350 + timestamp: 1724613663049 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.12.5-pyhd8ed1ab_0.conda + sha256: 7be1876627495047f3f07c52c93ddc2ae2017b93affe58110a5474e5ebcb2662 + md5: a46aa56c0ca7cc2bd38baffc2686f0a6 + depends: + - click >=8.0.0 + - python >=3.7 + - typing_extensions >=3.7.4.3 + constrains: + - rich >=10.11.0 + - typer >=0.12.5,<0.12.6.0a0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/typer-slim?source=hash-mapping + size: 45641 + timestamp: 1724613646022 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.12.5-hd8ed1ab_0.conda + sha256: bb298b116159ec1085f6b29eaeb982006651a0997eda08de8b70cfb6177297f3 + md5: 2dc1ee4046de0692077e9aa9ba351d36 + depends: + - rich + - shellingham + - typer-slim 0.12.5 pyhd8ed1ab_0 + license: MIT + license_family: MIT + purls: [] + size: 46817 + timestamp: 1724613648907 +- pypi: https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl + name: typing-inspect + version: 0.9.0 + sha256: 9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f + requires_dist: + - mypy-extensions>=0.3.0 + - typing-extensions>=3.7.4 + - typing>=3.7.4 ; python_full_version < '3.5' +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda + sha256: 0fce54f8ec3e59f5ef3bb7641863be4e1bf1279623e5af3d3fa726e8f7628ddb + md5: ebe6952715e1d5eb567eeebf25250fa7 + depends: + - python >=3.8 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + size: 39888 + timestamp: 1717802653893 +- pypi: https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl + name: tzdata + version: '2025.3' + sha256: 06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1 + requires_python: '>=2' +- pypi: https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl + name: urllib3 + version: 2.2.3 + sha256: ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac + requires_dist: + - brotli>=1.0.9 ; platform_python_implementation == 'CPython' and extra == 'brotli' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'brotli' + - h2>=4,<5 ; extra == 'h2' + - pysocks>=1.5.6,!=1.5.7,<2.0 ; extra == 'socks' + - zstandard>=0.18.0 ; extra == 'zstd' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/fd/21/0a674dfe66e9df9072c46269c882e9f901d36d987d8ea50ead033a9c1e01/werkzeug-2.3.8-py3-none-any.whl + name: werkzeug + version: 2.3.8 + sha256: bba1f19f8ec89d4d607a3bd62f1904bd2e609472d93cd85e9d4e178f472c3748 + requires_dist: + - markupsafe>=2.1.1 + - watchdog>=2.3 ; extra == 'watchdog' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_0.conda + sha256: 24f6851a336a50c53d6b50b142c1654872494a62528d57c3ff40240cbd8b13be + md5: bdb2f437ce62fd2f1fef9119a37a12d9 + depends: + - python >=3.8 + license: MIT + license_family: MIT + purls: + - pkg:pypi/wheel?source=hash-mapping + size: 62998 + timestamp: 1732339880578 +- pypi: https://files.pythonhosted.org/packages/b4/a7/897f484225bd8e179a4f39f8e9a4ca26c14e9f7055b495384b1d56e1382d/xarray-2023.1.0-py3-none-any.whl + name: xarray + version: 2023.1.0 + sha256: 7e530b1deafdd43e5c2b577d0944e6b528fbe88045fd849e49a8d11871ecd522 + requires_dist: + - numpy>=1.20 + - pandas>=1.3 + - packaging>=21.3 + - scipy ; extra == 'accel' + - bottleneck ; extra == 'accel' + - numbagg ; extra == 'accel' + - flox ; extra == 'accel' + - netcdf4 ; extra == 'complete' + - h5netcdf ; extra == 'complete' + - scipy ; extra == 'complete' + - zarr ; extra == 'complete' + - fsspec ; extra == 'complete' + - cftime ; extra == 'complete' + - rasterio ; extra == 'complete' + - cfgrib ; extra == 'complete' + - pooch ; extra == 'complete' + - bottleneck ; extra == 'complete' + - numbagg ; extra == 'complete' + - flox ; extra == 'complete' + - dask[complete] ; extra == 'complete' + - matplotlib ; extra == 'complete' + - seaborn ; extra == 'complete' + - nc-time-axis ; extra == 'complete' + - pydap ; python_full_version < '3.10' and extra == 'complete' + - netcdf4 ; extra == 'docs' + - h5netcdf ; extra == 'docs' + - scipy ; extra == 'docs' + - zarr ; extra == 'docs' + - fsspec ; extra == 'docs' + - cftime ; extra == 'docs' + - rasterio ; extra == 'docs' + - cfgrib ; extra == 'docs' + - pooch ; extra == 'docs' + - bottleneck ; extra == 'docs' + - numbagg ; extra == 'docs' + - flox ; extra == 'docs' + - dask[complete] ; extra == 'docs' + - matplotlib ; extra == 'docs' + - seaborn ; extra == 'docs' + - nc-time-axis ; extra == 'docs' + - sphinx-autosummary-accessors ; extra == 'docs' + - sphinx-rtd-theme ; extra == 'docs' + - ipython ; extra == 'docs' + - ipykernel ; extra == 'docs' + - jupyter-client ; extra == 'docs' + - nbsphinx ; extra == 'docs' + - scanpydoc ; extra == 'docs' + - pydap ; python_full_version < '3.10' and extra == 'docs' + - netcdf4 ; extra == 'io' + - h5netcdf ; extra == 'io' + - scipy ; extra == 'io' + - zarr ; extra == 'io' + - fsspec ; extra == 'io' + - cftime ; extra == 'io' + - rasterio ; extra == 'io' + - cfgrib ; extra == 'io' + - pooch ; extra == 'io' + - pydap ; python_full_version < '3.10' and extra == 'io' + - dask[complete] ; extra == 'parallel' + - matplotlib ; extra == 'viz' + - seaborn ; extra == 'viz' + - nc-time-axis ; extra == 'viz' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_2.conda + sha256: 802725371682ea06053971db5b4fb7fbbcaee9cb1804ec688f55e51d74660617 + md5: 68eae977d7d1196d32b636a026dc015d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_2 + - liblzma-devel 5.8.1 hb9d3cd8_2 + - xz-gpl-tools 5.8.1 hbcc6ac9_2 + - xz-tools 5.8.1 hb9d3cd8_2 + license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] + size: 23987 + timestamp: 1749230104359 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.8.1-h9a6d368_2.conda + sha256: afb747cf017b67cc31d54c6e6c4bd1b1e179fe487a3d23a856232ed7fd0b099b + md5: 39435c82e5a007ef64cbb153ecc40cfd + depends: + - __osx >=11.0 + - liblzma 5.8.1 h39f12f2_2 + - liblzma-devel 5.8.1 h39f12f2_2 + - xz-gpl-tools 5.8.1 h9a6d368_2 + - xz-tools 5.8.1 h39f12f2_2 + license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] + size: 23995 + timestamp: 1749230346887 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_2.conda + sha256: 840838dca829ec53f1160f3fca6dbfc43f2388b85f15d3e867e69109b168b87b + md5: bf627c16aa26231720af037a2709ab09 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_2 + constrains: + - xz 5.8.1.* + license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] + size: 33911 + timestamp: 1749230090353 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-gpl-tools-5.8.1-h9a6d368_2.conda + sha256: a0790cfb48d240e7b655b0d797a00040219cf39e3ee38e2104e548515df4f9c2 + md5: 09b1442c1d49ac7c5f758c44695e77d1 + depends: + - __osx >=11.0 + - liblzma 5.8.1 h39f12f2_2 + constrains: + - xz 5.8.1.* + license: 0BSD AND LGPL-2.1-or-later AND GPL-2.0-or-later + purls: [] + size: 34103 + timestamp: 1749230329933 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_2.conda + sha256: 58034f3fca491075c14e61568ad8b25de00cb3ae479de3e69be6d7ee5d3ace28 + md5: 1bad2995c8f1c8075c6c331bf96e46fb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - liblzma 5.8.1 hb9d3cd8_2 + constrains: + - xz 5.8.1.* + license: 0BSD AND LGPL-2.1-or-later + purls: [] + size: 96433 + timestamp: 1749230076687 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xz-tools-5.8.1-h39f12f2_2.conda + sha256: 9d1232705e3d175f600dc8e344af9182d0341cdaa73d25330591a28532951063 + md5: 37996935aa33138fca43e4b4563b6a28 + depends: + - __osx >=11.0 + - liblzma 5.8.1 h39f12f2_2 + constrains: + - xz 5.8.1.* + license: 0BSD AND LGPL-2.1-or-later + purls: [] + size: 86425 + timestamp: 1749230316106 +- pypi: https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl + name: zipp + version: 3.20.2 + sha256: a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 + requires_dist: + - pytest-checkdocs>=2.4 ; extra == 'check' + - pytest-ruff>=0.2.1 ; sys_platform != 'cygwin' and extra == 'check' + - pytest-cov ; extra == 'cover' + - sphinx>=3.5 ; extra == 'doc' + - jaraco-packaging>=9.3 ; extra == 'doc' + - rst-linker>=1.9 ; extra == 'doc' + - furo ; extra == 'doc' + - sphinx-lint ; extra == 'doc' + - jaraco-tidelift>=1.4 ; extra == 'doc' + - pytest-enabler>=2.2 ; extra == 'enabler' + - pytest>=6,!=8.1.* ; extra == 'test' + - jaraco-itertools ; extra == 'test' + - jaraco-functools ; extra == 'test' + - more-itertools ; extra == 'test' + - big-o ; extra == 'test' + - pytest-ignore-flaky ; extra == 'test' + - jaraco-test ; extra == 'test' + - importlib-resources ; python_full_version < '3.9' and extra == 'test' + - pytest-mypy ; extra == 'type' + requires_python: '>=3.8' +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda + sha256: 68f0206ca6e98fea941e5717cec780ed2873ffabc0e1ed34428c061e2c6268c7 + md5: 4a13eeac0b5c8e5b8ab496e6c4ddd829 + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 601375 + timestamp: 1764777111296 diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 0000000..82edbdb --- /dev/null +++ b/pixi.toml @@ -0,0 +1,26 @@ +[workspace] +authors = ["RYDE-WORK "] +channels = ["conda-forge"] +name = "LNP_ML" +platforms = ["linux-64", "osx-arm64"] +version = "0.1.0" + +[tasks] + +[dependencies] +loguru = "*" +ruff = "*" +tqdm = "*" +typer = "*" +pip = "*" +python = "3.8.*" + +[pypi-dependencies] +lnp_ml = { path = ".", editable = true } +chemprop = "==1.7.0" +setuptools = "*" +pandas = ">=2.0.3, <3" +openpyxl = ">=3.1.5, <4" +python-dotenv = ">=1.0.1, <2" +pyarrow = ">=17.0.0, <18" +fastparquet = ">=2024.2.0, <2025" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..37f2854 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "lnp_ml" +version = "0.0.1" +description = "A short description of the project." +authors = [ + { name = "Wu Dinghong" }, +] +license = { file = "LICENSE" } +readme = "README.md" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License" +] +requires-python = ">=3.8" + + +[tool.ruff] +line-length = 99 +src = ["lnp_ml"] +include = ["pyproject.toml", "lnp_ml/**/*.py"] + +[tool.ruff.lint] +extend-select = ["I"] # Add import sorting + +[tool.ruff.lint.isort] +known-first-party = ["lnp_ml"] +force-sort-within-sections = true + diff --git a/references/.gitkeep b/references/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/reports/.gitkeep b/reports/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/reports/figures/.gitkeep b/reports/figures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/data_cleaning.py b/scripts/data_cleaning.py new file mode 100644 index 0000000..8858626 --- /dev/null +++ b/scripts/data_cleaning.py @@ -0,0 +1,85 @@ +"""数据清洗脚本:修正原始数据中的问题""" + +from pathlib import Path + +import numpy as np +import pandas as pd +import typer +from loguru import logger + +from lnp_ml.config import RAW_DATA_DIR, INTERIM_DATA_DIR + + +app = typer.Typer() + + +@app.command() +def main( + input_path: Path = RAW_DATA_DIR / "internal_deleted_uncorrected.xlsx", + output_path: Path = INTERIM_DATA_DIR / "internal_corrected.csv", +): + """ + 清洗原始数据,修正已知问题。 + + 修正内容: + 1. 修正肌肉注射组 Biodistribution_muscle=0.7745 的数据 + 2. 修复阳性对照组 (Amine="Crtl") 的数据 + 3. 按给药途径分组进行 z-score 标准化 + 4. 对 size 列取 log + """ + logger.info(f"Loading data from {input_path}") + df = pd.read_excel(input_path, header=2) + logger.info(f"Loaded {len(df)} samples") + + # 修正肌肉注射组 0.7745 的数据 + logger.info("Correcting Biodistribution_muscle=0.7745 rows...") + rows_to_correct = df[df["Biodistribution_muscle"] == 0.7745] + for index, row in rows_to_correct.iterrows(): + total_biodistribution = pd.to_numeric(row[[ + "Biodistribution_lymph_nodes", + "Biodistribution_heart", + "Biodistribution_liver", + "Biodistribution_spleen", + "Biodistribution_lung", + "Biodistribution_kidney", + "Biodistribution_muscle" + ]]).sum() + df.at[index, "Biodistribution_lymph_nodes"] = pd.to_numeric(row["Biodistribution_lymph_nodes"]) / total_biodistribution + df.at[index, "Biodistribution_heart"] = pd.to_numeric(row["Biodistribution_heart"]) / total_biodistribution + df.at[index, "Biodistribution_liver"] = pd.to_numeric(row["Biodistribution_liver"]) / total_biodistribution + df.at[index, "Biodistribution_spleen"] = pd.to_numeric(row["Biodistribution_spleen"]) / total_biodistribution + df.at[index, "Biodistribution_lung"] = pd.to_numeric(row["Biodistribution_lung"]) / total_biodistribution + df.at[index, "Biodistribution_kidney"] = pd.to_numeric(row["Biodistribution_kidney"]) / total_biodistribution + df.at[index, "Biodistribution_muscle"] = pd.to_numeric(row["Biodistribution_muscle"]) / total_biodistribution + df.at[index, "quantified_total_luminescence"] = pd.to_numeric(row["quantified_total_luminescence"]) / (1 - 0.7745) + df.at[index, "unnormalized_delivery"] = df.at[index, "quantified_total_luminescence"] + logger.info(f" Corrected {len(rows_to_correct)} rows") + + # 修复阳性对照组的数据 + logger.info("Fixing control group (Amine='Crtl')...") + rows_to_override = df["Amine"] == "Crtl" + df.loc[rows_to_override, "quantified_total_luminescence"] = 1 + df.loc[rows_to_override, "unnormalized_delivery"] = 1 + logger.info(f" Fixed {rows_to_override.sum()} rows") + + # 分别对肌肉注射组和静脉注射组重新进行 z-score 标准化 + logger.info("Z-score normalizing delivery by Route_of_administration...") + df["unnormalized_delivery"] = pd.to_numeric(df["unnormalized_delivery"], errors="coerce") + df["quantified_delivery"] = ( + df.groupby("Route_of_administration")["unnormalized_delivery"] + .transform(lambda x: (x - x.mean()) / x.std()) + ) + + # 对 size 列取 log + logger.info("Log-transforming size column...") + df["size"] = pd.to_numeric(df["size"], errors="coerce") + df["size"] = np.log(df["size"].replace(0, np.nan)) # 避免 log(0) + + # 保存 + output_path.parent.mkdir(parents=True, exist_ok=True) + df.to_csv(output_path, index=False) + logger.success(f"Saved cleaned data to {output_path}") + + +if __name__ == "__main__": + app() diff --git a/scripts/process_data.py b/scripts/process_data.py new file mode 100644 index 0000000..afae6b3 --- /dev/null +++ b/scripts/process_data.py @@ -0,0 +1,158 @@ +"""数据处理脚本:将原始数据转换为模型可用的格式""" + +from pathlib import Path + +import pandas as pd +import typer +from loguru import logger + +from lnp_ml.config import INTERIM_DATA_DIR, PROCESSED_DATA_DIR +from lnp_ml.dataset import ( + process_dataframe, + SMILES_COL, + COMP_COLS, + HELP_COLS, + TARGET_REGRESSION, + TARGET_CLASSIFICATION_PDI, + TARGET_CLASSIFICATION_EE, + TARGET_TOXIC, + TARGET_BIODIST, + get_phys_cols, + get_exp_cols, +) + +app = typer.Typer() + + +@app.command() +def main( + input_path: Path = INTERIM_DATA_DIR / "internal_corrected.csv", + output_dir: Path = PROCESSED_DATA_DIR, + train_ratio: float = 0.56, + val_ratio: float = 0.14, + seed: int = 42, +): + """ + 处理原始数据并划分训练/验证/测试集。 + + 输出文件: + - train.parquet: 训练集 + - val.parquet: 验证集 + - test.parquet: 测试集 + - feature_columns.txt: 特征列名配置 + """ + logger.info(f"Loading data from {input_path}") + df = pd.read_csv(input_path) + logger.info(f"Loaded {len(df)} samples") + + # 处理数据 + logger.info("Processing dataframe...") + df = process_dataframe(df) + + # 定义要保留的列 + phys_cols = get_phys_cols() + exp_cols = get_exp_cols() + + keep_cols = ( + [SMILES_COL] + + COMP_COLS + + phys_cols + + HELP_COLS + + exp_cols + + TARGET_REGRESSION + + TARGET_CLASSIFICATION_PDI + + TARGET_CLASSIFICATION_EE + + [TARGET_TOXIC] + + TARGET_BIODIST + ) + + # 只保留存在的列 + keep_cols = [c for c in keep_cols if c in df.columns] + df = df[keep_cols] + + # 随机打乱并划分 + logger.info("Splitting dataset...") + df = df.sample(frac=1, random_state=seed).reset_index(drop=True) + + n = len(df) + n_train = int(n * train_ratio) + n_val = int(n * val_ratio) + + train_df = df.iloc[:n_train] + val_df = df.iloc[n_train:n_train + n_val] + test_df = df.iloc[n_train + n_val:] + + logger.info(f"Train: {len(train_df)}, Val: {len(val_df)}, Test: {len(test_df)}") + + # 保存 + output_dir.mkdir(parents=True, exist_ok=True) + + train_path = output_dir / "train.parquet" + val_path = output_dir / "val.parquet" + test_path = output_dir / "test.parquet" + + train_df.to_parquet(train_path, index=False) + val_df.to_parquet(val_path, index=False) + test_df.to_parquet(test_path, index=False) + + logger.success(f"Saved train to {train_path}") + logger.success(f"Saved val to {val_path}") + logger.success(f"Saved test to {test_path}") + + # 保存列名配置 + config_path = output_dir / "feature_columns.txt" + with open(config_path, "w") as f: + f.write("# Feature columns configuration\n\n") + f.write(f"# SMILES\n{SMILES_COL}\n\n") + f.write(f"# comp token [{len(COMP_COLS)}]\n") + f.write("\n".join(COMP_COLS) + "\n\n") + f.write(f"# phys token [{len(phys_cols)}]\n") + f.write("\n".join(phys_cols) + "\n\n") + f.write(f"# help token [{len(HELP_COLS)}]\n") + f.write("\n".join(HELP_COLS) + "\n\n") + f.write(f"# exp token [{len(exp_cols)}]\n") + f.write("\n".join(exp_cols) + "\n\n") + f.write("# Targets\n") + f.write("## Regression\n") + f.write("\n".join(TARGET_REGRESSION) + "\n") + f.write("## PDI classification\n") + f.write("\n".join(TARGET_CLASSIFICATION_PDI) + "\n") + f.write("## EE classification\n") + f.write("\n".join(TARGET_CLASSIFICATION_EE) + "\n") + f.write("## Toxic\n") + f.write(f"{TARGET_TOXIC}\n") + f.write("## Biodistribution\n") + f.write("\n".join(TARGET_BIODIST) + "\n") + + logger.success(f"Saved feature config to {config_path}") + + # 打印统计信息 + logger.info("\n=== Dataset Statistics ===") + logger.info(f"Total samples: {n}") + logger.info(f"SMILES unique: {df[SMILES_COL].nunique()}") + + # 缺失值统计 + logger.info("\nMissing values in targets:") + for col in TARGET_REGRESSION + [TARGET_TOXIC]: + if col in df.columns: + missing = df[col].isna().sum() + logger.info(f" {col}: {missing} ({100*missing/n:.1f}%)") + + # PDI 分布 + if all(c in df.columns for c in TARGET_CLASSIFICATION_PDI): + pdi_sum = df[TARGET_CLASSIFICATION_PDI].sum() + logger.info(f"\nPDI distribution:") + for col, count in pdi_sum.items(): + logger.info(f" {col}: {int(count)}") + + # EE 分布 + if all(c in df.columns for c in TARGET_CLASSIFICATION_EE): + ee_sum = df[TARGET_CLASSIFICATION_EE].sum() + logger.info(f"\nEE distribution:") + for col, count in ee_sum.items(): + logger.info(f" {col}: {int(count)}") + + +if __name__ == "__main__": + app() +