Spaces:
Sleeping
Sleeping
| from rdkit import Chem | |
| from rdkit.Chem import Crippen, Descriptors, QED | |
| def _calc_props(smiles: str) -> dict | None: | |
| mol = Chem.MolFromSmiles(smiles) | |
| if mol is None: | |
| return None | |
| mw = Descriptors.MolWt(mol) | |
| logp = Crippen.MolLogP(mol) | |
| hbd = Descriptors.NumHDonors(mol) | |
| hba = Descriptors.NumHAcceptors(mol) | |
| rot = Descriptors.NumRotatableBonds(mol) | |
| tpsa = Descriptors.TPSA(mol) | |
| qed = float(QED.qed(mol)) | |
| return { | |
| "mw": float(mw), | |
| "logp": float(logp), | |
| "hbd": int(hbd), | |
| "hba": int(hba), | |
| "rotatable_bonds": int(rot), | |
| "tpsa": float(tpsa), | |
| "qed": float(qed), | |
| } | |
| def filter_molecules(candidates: list[dict]) -> list[dict]: | |
| """Apply basic drug-likeness heuristics (Lipinski-ish + QED).""" | |
| filtered: list[dict] = [] | |
| for c in candidates: | |
| smiles = (c or {}).get("smiles") | |
| if not smiles or not isinstance(smiles, str): | |
| continue | |
| props = _calc_props(smiles) | |
| if props is None: | |
| out = dict(c) | |
| out["passes_filter"] = False | |
| out["parse_error"] = True | |
| filtered.append(out) | |
| continue | |
| passes = True | |
| if not (150 <= props["mw"] <= 500): | |
| passes = False | |
| if props["logp"] > 5: | |
| passes = False | |
| if props["hbd"] > 5: | |
| passes = False | |
| if props["hba"] > 10: | |
| passes = False | |
| if props["qed"] < 0.3: | |
| passes = False | |
| out = dict(c) | |
| out.update(props) | |
| out["passes_filter"] = passes | |
| filtered.append(out) | |
| filtered.sort(key=lambda x: (x.get("score") is None, -(x.get("score") or 0), -x.get("qed", 0))) | |
| return filtered | |