Source code for mordred.RingCount

import networkx
from rdkit import Chem

from ._base import Descriptor

__all__ = ("RingCount",)


class RingCountBase(Descriptor):
    __slots__ = ()
    explicit_hydrogens = False

    def parameters(self):
        return ()


class Rings(RingCountBase):
    __slots__ = ()

    def calculate(self):
        return [frozenset(s) for s in Chem.GetSymmSSSR(self.mol)]


class FusedRings(RingCountBase):
    __slots__ = ()

    def dependencies(self):
        return {"Rings": Rings()}

    def calculate(self, Rings):
        if len(Rings) < 2:
            return []

        G = networkx.Graph()

        l = len(Rings)
        for i, j in ((i, j) for i in range(l) for j in range(i + 1, l)):
            if len(Rings[i] & Rings[j]) >= 2:
                G.add_edge(i, j)

        return [
            frozenset(j for i in ring_ids for j in Rings[i])
            for ring_ids in networkx.connected_components(G)
        ]


[docs]class RingCount(RingCountBase): r"""ring count descriptor. :type order: int or None :param order: number of bonds in ring :type greater: bool :param greater: count length or greater rings :type fused: bool :param fused: count fused rings :type aromatic: bool or None :param aromatic: * True: count aromatic rings * False: count non-aromatic rings * None: count any rings :type hetero: bool or None :param hetero: * True: count hetero rings * False: count carbon rings * None: count any rings """ __slots__ = ("_order", "_greater", "_fused", "_aromatic", "_hetero",)
[docs] def description(self): if self._order is None: o = "" elif self._greater: o = "{}-or-greater-membered ".format(self._order) else: o = "{}-membered ".format(self._order) if self._aromatic is None: a = "" elif self._aromatic is True: a = "aromatic " else: a = "aliphatic " return "{}{}{}{}ring count".format( o, a, "fused " if self._fused else "", "hetero " if self._hetero else "", )
@classmethod def preset(cls): for fused in [False, True]: for arom in [None, True, False]: for hetero in [None, True]: yield cls(None, False, fused, arom, hetero) for n in range(4 if fused else 3, 13): yield cls(n, False, fused, arom, hetero) yield cls(12, True, fused, arom, hetero) def __str__(self): attrs = [] if self._greater: attrs.append("G") if self._order is not None: attrs.append(str(self._order)) if self._fused: attrs.append("F") if self._aromatic is True: attrs.append("a") elif self._aromatic is False: attrs.append("A") if self._hetero is True: attrs.append("H") elif self._hetero is False: attrs.append("C") return "n{}Ring".format("".join(attrs)) def parameters(self): return self._order, self._greater, self._fused, self._aromatic, self._hetero def __init__(self, order=None, greater=False, fused=False, aromatic=None, hetero=None): self._order = order self._greater = greater self._fused = fused self._aromatic = aromatic self._hetero = hetero def dependencies(self): return { "Rs": (FusedRings if self._fused else Rings)(), } def _check_order(self, R): if self._order is None: return True if self._greater: return len(R) >= self._order else: return len(R) == self._order def _check_arom(self, R): if self._aromatic is None: return True is_arom = all(self.mol.GetAtomWithIdx(i).GetIsAromatic() for i in R) if self._aromatic: return is_arom return not is_arom def _check_hetero(self, R): if self._hetero is None: return True has_hetero = any(self.mol.GetAtomWithIdx(i).GetAtomicNum() != 6 for i in R) if self._hetero: return has_hetero return not has_hetero def calculate(self, Rs): return sum( 1 for R in Rs if self._check_order(R) and self._check_arom(R) and self._check_hetero(R) ) rtype = int