Exemple #1
0
    def apply(self, mol: RWMol) -> RWMol:
        num_atoms = mol.GetNumAtoms()
        if self.detach:
            for i, a in enumerate(mol.GetAtoms()):
                m = a.GetAtomMapNum()
                if m == self.atom_map2:
                    for bond in a.GetBonds():
                        mol.RemoveBond(bond.GetBeginAtomIdx(),
                                       bond.GetEndAtomIdx())
                    mol.RemoveAtom(i)
                    num_atoms -= 1
                    break

        atom_ind = get_atom_ind(mol, self.atom_map1)
        b_type = rdchem.BondType.values[self.bond_type]
        b_stereo = rdchem.BondStereo.values[self.bond_stereo]

        old_atom = mol.GetAtomWithIdx(atom_ind)
        if old_atom.HasProp('in_reactant'):
            self.new_a.SetBoolProp('in_reactant',
                                   old_atom.GetBoolProp('in_reactant'))
        if old_atom.HasProp('mol_id'):
            self.new_a.SetIntProp('mol_id', old_atom.GetIntProp('mol_id'))

        mol.AddAtom(self.new_a)
        new_atom_ind = num_atoms

        bond_ind = mol.AddBond(atom_ind, new_atom_ind, order=b_type) - 1
        new_bond = mol.GetBondWithIdx(bond_ind)
        new_bond.SetStereo(b_stereo)
        new_bond.SetBoolProp('is_edited', True)

        return mol
Exemple #2
0
 def _delete_marked(self, mol: Chem.RWMol):
     morituri = list(
         reversed(
             mol.GetAtomsMatchingQuery(
                 Chem.rdqueries.HasPropQueryAtom('DELETE'))))
     for atom in morituri:
         mol.RemoveAtom(atom.GetIdx())
def remove_exocyclic_attachments(mol):
    """
    Remove exocyclic and exolinker attachments from
    a molecule.

    Parameters
    ----------
    mol : rdkit.Chem.rdchem.Mol

    Returns
    -------
    rdkit.Chem.rdchem.Mol
        Molecule with exocyclic/exolinker attachments
        removed.

    """
    edit = RWMol(mol)
    remove_atoms = set()
    for atom in edit.GetAtoms():
        degree = atom.GetDegree()
        if degree == 1:
            bond = atom.GetBonds()[0]
            if bond.GetBondTypeAsDouble() == 2.0:
                nbr = bond.GetOtherAtom(atom)
                hcount = nbr.GetTotalNumHs()
                nbr.SetNumExplicitHs(hcount + 2)
                nbr.SetNoImplicit(True)
                remove_atoms.add(atom.GetIdx())
    for aix in sorted(remove_atoms, reverse=True):
        edit.RemoveAtom(aix)
    rdmolops.AssignRadicals(edit)
    GetSymmSSSR(edit)
    return edit.GetMol()
Exemple #4
0
def _GetSMILES(mol, idxlist):
    tmol = mol.__copy__()  #(t)emporary
    tmol = RWMol(tmol)
    for AtomIdx in xrange(tmol.GetNumAtoms() - 1, -1, -1):
        if AtomIdx not in idxlist:
            tmol.RemoveAtom(AtomIdx)
    return Chem.MolToSmiles(tmol)
Exemple #5
0
    def depict(self, filename=None, ipython=False):
        from rdkit.Chem.Draw import IPythonConsole
        from rdkit.Chem.Draw import MolToImage
        from rdkit.Chem.Draw import rdMolDraw2D
        from rdkit.Chem.AllChem import EmbedMolecule
        from IPython.display import SVG
        from rdkit.Chem import RWMol, MolFromSmiles, Atom, BondType, ChiralType

        _ = MolFromSmiles('C')
        rmol = RWMol(_)

        dict_old_new_idx = {}
        n = 1
        for a in self.atoms:
            old_idx = a.GetIdx()
            rmol.AddAtom(a)
            dict_old_new_idx[old_idx] = n
            n += 1

        for a in self.enviroments:
            old_idx = a.GetIdx()
            a.SetChiralTag(ChiralType.CHI_UNSPECIFIED)
            a.SetIsAromatic(0)
            rmol.AddAtom(a)
            dict_old_new_idx[old_idx] = n
            n += 1

        for b in self.Bonds:
            rmol.AddBond(dict_old_new_idx[b.GetBeginAtomIdx()],
                         dict_old_new_idx[b.GetEndAtomIdx()], b.GetBondType())
        for b in self.bondsenvironments:
            rmol.AddBond(dict_old_new_idx[b.GetBeginAtomIdx()],
                         dict_old_new_idx[b.GetEndAtomIdx()], b.GetBondType())

        rmol.RemoveAtom(0)

        EmbedMolecule(rmol)
        drawer = rdMolDraw2D.MolDraw2DSVG(400, 200)

        drawer.DrawMolecule(rmol)

        drawer.FinishDrawing()
        svg = drawer.GetDrawingText()

        if filename != None:
            f = open(filename, 'w')
            f.write(svg)
            f.close()

        if ipython:
            svg = svg.replace('svg:', '')
            return SVG(svg)
        else:
            return None
Exemple #6
0
 def _prevent_two_bonds_on_dummy(self, mol: Chem.RWMol):
     for atom in mol.GetAtoms():
         if atom.GetSymbol() != '*':
             pass
         elif len(atom.GetNeighbors()) <= 1:
             pass
         elif len(atom.GetNeighbors()) >= 2:
             neighs = atom.GetNeighbors()
             for second in neighs[1:]:
                 self._absorb(mol, atom.GetIdx(), second.GetIdx())
                 mol.RemoveAtom(second.GetIdx())
                 self._prevent_two_bonds_on_dummy(mol)
                 break
 def _prevent_bridge_ring(self, mol: Chem.RWMol, examplar: Tuple[int]):
     ## This is really
     # examplar is ring
     ringatoms = self._get_ring_info(mol) #GetRingInfo().AtomRings()
     ringatoms = [ring for ring in ringatoms if set(ring).intersection(examplar)]
     ring_idx = list(range(len(ringatoms)))
     shared_count = {}
     for ra, rb in itertools.combinations(ring_idx, r=2):
         shared_count[(ra, rb)] = len(set(ringatoms[ra]).intersection(set(ringatoms[rb])))
     if len(shared_count) == 0:
         return mol
     ra, rb = list(shared_count.keys())[0]
     shared = list(set(ringatoms[ra]).intersection(ringatoms[rb]))
     pairs = [(a, b) for a, b in itertools.combinations(shared, r=2) if mol.GetBondBetweenAtoms(a, b) is not None]
     c = Counter([i for pair in pairs for i in pair])
     ring_A, ring_B = ringatoms[ra], ringatoms[rb]
     small, big = sorted([ring_A, ring_B], key=lambda ring: len(ring))
     inners = [i for i in c if c[i] > 1]
     x = list(set(shared).difference(inners))
     if len(x) != 2:
         log.critical(f'This is impossible. {ringatoms} share {shared} with {inners} in the inside and {x} on the edge?')
         return mol
     a, b = x
     if len(big) > 6:
         log.warning(f'Removing {len(inners)} bridging atoms and replacing with fused ring')
         # bond the vertices
         bt = Chem.BondType.SINGLE # ???
         if mol.GetBondBetweenAtoms(a, b) is None:
             mol.AddBond(a, b, bt)
         else:
             log.warning('This is really odd! Why is there a bond already??')
         # remove the middle atoms.
         for i in sorted(inners, reverse=True):
             mol.RemoveAtom(i)
     else:
         log.warning(f'Shriking the smaller ring to change from bridged to fused.')
         # get the neighbour in the small atom to a vertex.
         neighs = [neigh for neigh in mol.GetAtomWithIdx(a).GetNeighbors() if
                   neigh.GetIdx() not in shared and neigh.GetIdx() in small]
         neigh = sorted(neighs, key=lambda atom: atom.GetSymbol() != 'C')[0]
         bt = mol.GetBondBetweenAtoms(a, neigh.GetIdx()).GetBondType()
         mol.RemoveBond(a, neigh.GetIdx())
         new_neigh = [neigh for neigh in mol.GetAtomWithIdx(a).GetNeighbors() if neigh.GetIdx() in shared][0]
         mol.AddBond(neigh.GetIdx(), new_neigh.GetIdx(), bt)
         neigh.SetBoolProp('_Novel', True)
         new_neigh.SetBoolProp('_Novel', True)
         mol.GetAtomWithIdx(a).SetBoolProp('_Novel', True)
     return mol
Exemple #8
0
 def complement_reaction(rxn_template):
     if rxn_template.GetNumProductTemplates() != 1:
         print("[ERROR] A reaction template has only one product template.")
         sys.exit(1)
     pro = rxn_template.GetProductTemplate(0)
     rw_pro = RWMol(pro)
     amaps_pro = {a.GetAtomMapNum() for a in pro.GetAtoms()}
     amaps_rcts = {a.GetAtomMapNum() for rct in rxn_template.GetReactants() for a in rct.GetAtoms()}
     amaps_not_in_rcts = amaps_pro.intersection(amaps_rcts)
     for amap in amaps_not_in_rcts:
         aidx = [a.GetIdx() for a in rw_pro.GetAtoms() if a.GetAtomMapNum() == amap][0]
         rw_pro.RemoveAtom(aidx)
     m = rw_pro.GetMol()
     if '.' in Chem.MolToSmarts(m):
         return
     if (m.GetNumAtoms() == 0) or (m.GetNumAtoms() == 1 and m.GetAtomWithIdx(0).GetSymbol() in {"*", None}):
         return
     rxn_template.AddReactantTemplate(m)
Exemple #9
0
    def fragment(self, scaffold):
        """Fragment a scaffold into its next set of Murcko fragments.

        Parameters
        ----------
        scaffold : scaffoldgraph.core.Scaffold
            Child scaffold to be fragmented.

        Returns
        -------
        list
            A list of parent scaffolds representing the next hierarchy.

        """
        parents = []
        rings = scaffold.ring_systems  # ring system information
        info = scaffold.rings.info

        if rings.count == 1:
            return []
        for rix, ring in enumerate(rings):
            edit = RWMol(scaffold.mol)
            remove_atoms = set()
            for index, atom in zip(ring.aix, ring.atoms):
                if info.NumAtomRings(index) == 1 or any(
                    [not b.IsInRing() for b in atom.GetBonds()]):
                    if atom.GetDegree() > 2:  # Evoke linker collection
                        collect_linker_atoms(edit.GetAtomWithIdx(index),
                                             remove_atoms)
                    else:
                        remove_atoms.add(index)
                else:
                    remove_atoms.add(index)

            for aix in sorted(remove_atoms, reverse=True):
                edit.RemoveAtom(aix)

            for parent in get_scaffold_frags(edit):
                if parent.ring_systems.count == len(rings) - 1:
                    parent.removed_ring_idx = rix
                    parents.append(parent)

        return parents
def _minimize_rings(mol):
    """Private: Minimize rings in a scaffold.

    In this process, all remaining vertices/atoms of degree two are
    removed by performing an edge merging operation. The only
    exception being when both vertices neighbours are connected
    (i.e. we have a triangle), when edge merging would lead to the
    loss of a cycle. The result is a minimum cycle topological
    representation of the original molecule. This function is used
    in the computation of ring topology scaffolds (Oprea).

    If a ring contains a non-carbon atom, this atom is maintained.
    Neighbouring ring atoms which are of the same type are merged
    together into a single atom of the corresponding type.

    Parameters
    ----------
    mol : rdkit.Chem.rdchem.Mol

    Returns
    -------
    rdkit.Chem.rdchem.RWMol
        Minimum cycle topological graph.

    """
    edit = RWMol(mol)
    remove_atoms = set()
    for atom in edit.GetAtoms():
        if atom.GetDegree() == 2:
            n1, n2 = atom.GetNeighbors()
            n1_idx, n2_idx = n1.GetIdx(), n2.GetIdx()
            connected = edit.GetBondBetweenAtoms(n1_idx, n2_idx)
            if not connected and (n1.GetAtomicNum() == atom.GetAtomicNum()
                                  or n2.GetAtomicNum() == atom.GetAtomicNum()):
                a_idx = atom.GetIdx()
                edit.RemoveBond(n1_idx, a_idx)
                edit.RemoveBond(n2_idx, a_idx)
                edit.AddBond(n1_idx, n2_idx, BondType.SINGLE)
                remove_atoms.add(a_idx)
    for a_idx in sorted(remove_atoms, reverse=True):
        edit.RemoveAtom(a_idx)
    return edit
Exemple #11
0
    def fragment(self, scaffold):
        """Fragment a scaffold into its next set of murcko fragments.

        This fragmenter will not dissect fused ring systems.

        Parameters
        ----------
        scaffold (sg.core.Scaffold): scaffold to be fragmented.

        Returns
        -------
        parents (list): a list of the next scaffold parents.
        """

        parents = []
        rings = scaffold.ring_systems  # ring system information
        info = scaffold.rings.info

        if rings.count == 1:
            return []
        for rix, ring in enumerate(rings):
            edit = RWMol(scaffold.mol)
            remove_atoms = set()
            for index, atom in zip(ring.aix, ring.atoms):
                if info.NumAtomRings(index) == 1:
                    if atom.GetDegree() > 2:  # Evoke linker collection
                        collect_linker_atoms(edit.GetAtomWithIdx(index),
                                             remove_atoms)
                    else:
                        remove_atoms.add(index)
                else:
                    remove_atoms.add(index)

            for aix in sorted(remove_atoms, reverse=True):
                edit.RemoveAtom(aix)

            for parent in get_scaffold_frags(edit):
                if parent.ring_systems.count == len(rings) - 1:
                    parent.removed_ring_idx = rix
                    parents.append(parent)

        return parents
 def _delete_collapsed(self, mol: Chem.RWMol):
     for a in reversed(range(mol.GetNumAtoms())):
         if mol.GetAtomWithIdx(a).GetIntProp('_ori_i') == -1:
             mol.RemoveAtom(a)
Exemple #13
0
    def fragment(self, scaffold):
        """Fragment a scaffold into its next set of Murcko fragments.

        Parameters
        ----------
        scaffold : scaffoldgraph.core.Scaffold
            Child scaffold to be fragmented.

        Returns
        -------
        list
            A list of parent scaffolds representing the next hierarchy.

        """
        parents = []  # container for parent scaffolds
        rings = scaffold.rings  # ring information

        for rix, ring in enumerate(rings):  # Loop through all rings and remove
            edit = RWMol(scaffold.mol)  # Editable molecule

            # Collect all removable atoms in the molecule
            remove_atoms = set()
            for index, atom in zip(ring.aix, ring.atoms):
                if rings.info.NumAtomRings(index) == 1:
                    if atom.GetDegree() > 2:  # Evoke linker collection
                        collect_linker_atoms(edit.GetAtomWithIdx(index),
                                             remove_atoms)
                    else:  # Add ring atom to removable set
                        remove_atoms.add(index)
                else:  # Atom is shared between multiple rings
                    correct_atom_props(edit.GetAtomWithIdx(index))

            # Collect removable bonds (this needs to be done to prevent the case where when deleting
            # a ring two atoms belonging to the same bond are also part of separate other rings.
            # This bond must be broken to prevent an incorrect output)
            remove_bonds = set()
            for bix in {
                    x
                    for x in ring.bix if rings.info.NumBondRings(x) == 1
            }:
                bond = edit.GetBondWithIdx(bix)
                b_x, b_y = bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()
                if b_x not in remove_atoms and b_y not in remove_atoms:
                    remove_bonds.add((b_x, b_y))
                    correct_atom_props(edit.GetAtomWithIdx(b_x))
                    correct_atom_props(edit.GetAtomWithIdx(b_y))

            # Scheme 4 (scaffold tree rule)
            if self.use_scheme_4 is not False and len(ring) == 3:
                atomic_nums = [a.GetAtomicNum() for a in ring.atoms]
                if len([a for a in atomic_nums if a != 1 and a != 6]) == 1:
                    shared = {
                        x
                        for x in ring.bix if rings.info.NumBondRings(x) > 1
                    }
                    if len(shared) == 1:
                        bond = edit.GetBondWithIdx(shared.pop())
                        bond.SetBondType(BondType.DOUBLE)

            # Remove collected atoms and bonds
            for bix in remove_bonds:
                edit.RemoveBond(*bix)
            for aix in sorted(remove_atoms, reverse=True):
                edit.RemoveAtom(aix)

            # Add new parent scaffolds to parent list
            for parent in get_scaffold_frags(edit):
                if parent.rings.count == len(rings) - 1:
                    parent.removed_ring_idx = rix
                    parents.append(parent)

        return parents