Spaces:
Paused
Paused
Download src/analogs.py from VSRAJHAVEL/BioGraph-AI: direct link, hf CLI and curl.
- Browser
- Download file 5.72 kB
-
https://huggingface.co/spaces/VSRAJHAVEL/BioGraph-AI/resolve/main/src/analogs.py
- Command line
-
hf download hf://spaces/VSRAJHAVEL/BioGraph-AI/src/analogs.py
-
curl -L -o analogs.py https://huggingface.co/spaces/VSRAJHAVEL/BioGraph-AI/resolve/main/src/analogs.py
5.72 kB
| """ | |
| In Silico Lead Optimization & Molecular Analog Generator. | |
| Systematically generates bioisosteric and R-group substituted chemical analogs, | |
| evaluates property shifts (Delta logS, Delta BBBP, Delta Tox), and discovers Pareto-optimal leads. | |
| """ | |
| from typing import List, Dict, Any, Optional | |
| from rdkit import Chem, RDLogger | |
| from rdkit.Chem import AllChem, Descriptors, QED | |
| from src.admet import compute_admet_profile | |
| from src.ensemble import predict_ensemble | |
| RDLogger.DisableLog('rdApp.*') | |
| # Common medicinal chemistry transformation rules | |
| MEDCHEM_RULES = [ | |
| # Aromatic functional group replacements | |
| {"name": "+Fluorine (-F)", "smarts": "[c:1][H]", "repl": "[c:1]F", "rationale": "Improves metabolic stability & lipophilicity"}, | |
| {"name": "+Chlorine (-Cl)", "smarts": "[c:1][H]", "repl": "[c:1]Cl", "rationale": "Enhances binding cavity packing"}, | |
| {"name": "+Methyl (-CH3)", "smarts": "[c:1][H]", "repl": "[c:1]C", "rationale": "Fills hydrophobic subpocket"}, | |
| {"name": "+Trifluoromethyl (-CF3)", "smarts": "[c:1][H]", "repl": "[c:1]C(F)(F)F", "rationale": "Strongly enhances lipophilic binding"}, | |
| {"name": "+Hydroxyl (-OH)", "smarts": "[c:1][H]", "repl": "[c:1]O", "rationale": "Improves aqueous solubility (H-bond donor)"}, | |
| {"name": "+Methoxy (-OCH3)", "smarts": "[c:1][H]", "repl": "[c:1]OC", "rationale": "H-bond acceptor without acidic proton"}, | |
| {"name": "+Amino (-NH2)", "smarts": "[c:1][H]", "repl": "[c:1]N", "rationale": "Basic center, enhances ionic interactions"}, | |
| {"name": "+Cyano (-C#N)", "smarts": "[c:1][H]", "repl": "[c:1]C#N", "rationale": "Linear polar group, bioisosteric to halide"}, | |
| {"name": "+Methylsulfonyl (-SO2CH3)", "smarts": "[c:1][H]", "repl": "[c:1]S(=O)(=O)C", "rationale": "High polarity, decreases lipophilicity"}, | |
| # Functional group bioisosteres | |
| {"name": "Ester -> Amide", "smarts": "[C:1](=O)[O:2][C:3]", "repl": "[C:1](=O)[N:2][C:3]", "rationale": "Improves hydrolytic stability in plasma"}, | |
| {"name": "Acid -> Tetrazole (Bioisostere)", "smarts": "[C:1](=O)[OH]", "repl": "[C:1]c1nnnn1", "rationale": "Non-classical carboxylic acid bioisostere"}, | |
| {"name": "Phenol -> Fluoro-benzene", "smarts": "[c:1][OH]", "repl": "[c:1]F", "rationale": "Blocks Phase-II glucuronidation metabolism"} | |
| ] | |
| def generate_analogs(parent_smiles: str, max_analogs: int = 15) -> List[Dict[str, Any]]: | |
| """ | |
| Generate chemically plausible analogs of the parent molecule. | |
| Args: | |
| parent_smiles: SMILES string of the lead compound. | |
| max_analogs: Maximum number of distinct valid analogs to generate. | |
| Returns: | |
| List of generated analog dictionaries with SMILES and transformation details. | |
| """ | |
| mol = Chem.MolFromSmiles(parent_smiles) | |
| if mol is None: | |
| return [] | |
| # Explicit hydrogens for aromatic replacement rules | |
| mol_with_h = Chem.AddHs(mol) | |
| generated = {} | |
| for rule in MEDCHEM_RULES: | |
| rxn_smarts = f"({rule['smarts']})>>({rule['repl']})" | |
| try: | |
| rxn = AllChem.ReactionFromSmarts(rxn_smarts) | |
| # Try on mol or mol_with_h | |
| reactants = (mol_with_h,) if "[H]" in rule['smarts'] else (mol,) | |
| products = rxn.RunReactants(reactants) | |
| for prod_tuple in products: | |
| prod = prod_tuple[0] | |
| try: | |
| prod = Chem.RemoveHs(prod) | |
| Chem.SanitizeMol(prod) | |
| smi = Chem.MolToSmiles(prod) | |
| # Filter out identity or duplicates | |
| if smi and smi != parent_smiles and smi not in generated: | |
| generated[smi] = { | |
| "smiles": smi, | |
| "transformation": rule["name"], | |
| "rationale": rule["rationale"] | |
| } | |
| if len(generated) >= max_analogs: | |
| break | |
| except Exception: | |
| continue | |
| except Exception: | |
| continue | |
| if len(generated) >= max_analogs: | |
| break | |
| return list(generated.values()) | |
| def evaluate_lead_optimization( | |
| parent_smiles: str, | |
| target_dataset: str = "esol", | |
| max_analogs: int = 12 | |
| ) -> Dict[str, Any]: | |
| """ | |
| Generate analogs and evaluate property shifts against the parent compound. | |
| """ | |
| parent_admet = compute_admet_profile(parent_smiles) | |
| parent_pred = predict_ensemble(parent_smiles, target_dataset) | |
| parent_val = float(parent_pred["mean"][0]) | |
| analogs = generate_analogs(parent_smiles, max_analogs=max_analogs) | |
| analog_results = [] | |
| for item in analogs: | |
| smi = item["smiles"] | |
| try: | |
| admet = compute_admet_profile(smi) | |
| pred = predict_ensemble(smi, target_dataset) | |
| val = float(pred["mean"][0]) | |
| delta = val - parent_val | |
| analog_results.append({ | |
| "SMILES": smi, | |
| "Transformation": item["transformation"], | |
| "Rationale": item["rationale"], | |
| "Predicted Value": val, | |
| "Delta": delta, | |
| "QED": admet["descriptors"]["QED"], | |
| "MW": admet["descriptors"]["Molecular Weight"], | |
| "LogP": admet["descriptors"]["LogP"], | |
| "TPSA": admet["descriptors"]["TPSA"], | |
| "Lipinski Pass": admet["filters"]["Lipinski"]["passed"] | |
| }) | |
| except Exception: | |
| continue | |
| return { | |
| "parent_smiles": parent_smiles, | |
| "parent_val": parent_val, | |
| "parent_qed": parent_admet["descriptors"]["QED"], | |
| "target_dataset": target_dataset, | |
| "analogs": analog_results | |
| } | |