Source code for qsarkit.representation.fingerprints._pharmacophore

"""2D pharmacophore fingerprints (Gobbi & Poppinger feature definitions)."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Optional

import numpy as np

from qsarkit.representation.fingerprints._base import (
    BaseFingerprintTransformer,
    fold_on_bits,
)

if TYPE_CHECKING:  # pragma: no cover
    from rdkit.Chem import Mol


[docs] class PharmacophoreFingerprint(BaseFingerprintTransformer): """Gobbi 2D pharmacophore fingerprint. Atoms are typed into pharmacophoric classes (hydrogen-bond donor, acceptor, positive/negative ionizable, aromatic, lipophilic) using the Gobbi & Poppinger SMARTS definitions shipped with RDKit (``rdkit.Chem.Pharm2D.Gobbi_Pharm2D``). Every 2- and 3-point combination of typed atoms, binned by topological distance, becomes one bit of a very sparse 39 972-bit vector. Parameters ---------- n_bits : int, optional, default 2048 If given, the sparse key space is modulo-folded onto ``n_bits`` columns, which keeps the output dense and machine-learning ready. Pass ``None`` to return the full, unfolded 39 972-bit vector. Notes ----- Deviation from the original specification: the Gobbi signature is defined over 39 972 keys, which is impractical as a dense design matrix for typical QSAR datasets. By default this transformer therefore folds the set bits modulo ``n_bits`` (the standard RDKit folding strategy), accepting bit collisions in exchange for a compact representation. Set ``n_bits=None`` to recover the exact, unfolded key vector. Examples -------- >>> from rdkit import Chem >>> from qsarkit.representation.fingerprints import PharmacophoreFingerprint >>> fp = PharmacophoreFingerprint(n_bits=256) >>> fp.fit_transform([Chem.MolFromSmiles("CC(=O)Oc1ccccc1C(=O)O")]).shape (1, 256) References ---------- - Gobbi, A. & Poppinger, D. (1998). "Genetic Optimization of Combinatorial Libraries." Biotechnol. Bioeng., 61(1), 47-54. https://doi.org/10.1002/(SICI)1097-0290(199824)61:1<47::AID-BIT9>3.0.CO;2-Z - RDKit ``rdkit.Chem.Pharm2D`` documentation: https://www.rdkit.org/docs/source/rdkit.Chem.Pharm2D.html - RDKit Book, "2D pharmacophore fingerprints": https://www.rdkit.org/docs/RDKit_Book.html """ _feature_prefix = "Pharm2D" def __init__(self, n_bits: Optional[int] = 2048): self.n_bits = n_bits @property def n_features_out(self) -> int: if self.n_bits is not None: return int(self.n_bits) from rdkit.Chem.Pharm2D import Gobbi_Pharm2D return int(Gobbi_Pharm2D.factory.GetSigSize()) def _fingerprint(self, mol: "Mol") -> np.ndarray: from rdkit.Chem.Pharm2D import Generate, Gobbi_Pharm2D signature = Generate.Gen2DFingerprint(mol, Gobbi_Pharm2D.factory) on_bits = list(signature.GetOnBits()) if self.n_bits is None: array = np.zeros(signature.GetNumBits(), dtype=np.uint8) array[on_bits] = 1 return array return fold_on_bits(on_bits, int(self.n_bits))