Gilmullin Almaz
Refactor code structure for improved readability and maintainability
72a3513
"""Module containing classes and functions for manipulating reactions and reaction
rules."""
from typing import Any, Iterator, List, Optional
from CGRtools.containers import MoleculeContainer, ReactionContainer
from CGRtools.exceptions import InvalidAromaticRing
from CGRtools.reactor import Reactor
class Reaction(ReactionContainer):
"""Reaction class used for a general representation of reaction."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def add_small_mols(
big_mol: MoleculeContainer, small_molecules: Optional[Any] = None
) -> List[MoleculeContainer]:
"""Takes a molecule and returns a list of modified molecules where each small
molecule has been added to the big molecule.
:param big_mol: A molecule.
:param small_molecules: A list of small molecules that need to be added to the
molecule.
:return: Returns a list of molecules.
"""
if small_molecules:
tmp_mol = big_mol.copy()
transition_mapping = {}
for small_mol in small_molecules:
for n, atom in small_mol.atoms():
new_number = tmp_mol.add_atom(atom.atomic_symbol)
transition_mapping[n] = new_number
for atom, neighbor, bond in small_mol.bonds():
tmp_mol.add_bond(
transition_mapping[atom], transition_mapping[neighbor], bond
)
transition_mapping = {}
return tmp_mol.split()
return [big_mol]
def apply_reaction_rule(
molecule: MoleculeContainer,
reaction_rule: Reactor,
sort_reactions: bool = False,
top_reactions_num: int = 3,
validate_products: bool = True,
rebuild_with_cgr: bool = False,
) -> Iterator[List[MoleculeContainer,]]:
"""Applies a reaction rule to a given molecule.
:param molecule: A molecule to which reaction rule will be applied.
:param reaction_rule: A reaction rule to be applied.
:param sort_reactions:
:param top_reactions_num: The maximum amount of reactions after the application of
reaction rule.
:param validate_products: If True, validates the final products.
:param rebuild_with_cgr: If True, the products are extracted from CGR decomposition.
:return: An iterator yielding the products of reaction rule application.
"""
reactants = add_small_mols(molecule, small_molecules=False)
try:
if sort_reactions:
unsorted_reactions = list(reaction_rule(reactants))
sorted_reactions = sorted(
unsorted_reactions,
key=lambda react: len(
list(filter(lambda mol: len(mol) > 6, react.products))
),
reverse=True,
)
# take top-N reactions from reactor
reactions = sorted_reactions[:top_reactions_num]
else:
reactions = []
for reaction in reaction_rule(reactants):
reactions.append(reaction)
if len(reactions) == top_reactions_num:
break
except IndexError:
reactions = []
for reaction in reactions:
# temporary solution - incorrect leaving groups
reactant_atom_nums = []
for i in reaction.reactants:
reactant_atom_nums.extend(i.atoms_numbers)
product_atom_nums = []
for i in reaction.products:
product_atom_nums.extend(i.atoms_numbers)
leaving_atom_nums = set(reactant_atom_nums) - set(product_atom_nums)
if len(leaving_atom_nums) > len(product_atom_nums):
continue
# check reaction
if rebuild_with_cgr:
cgr = reaction.compose()
reactants = cgr.decompose()[1].split()
else:
reactants = reaction.products # reactants are products in retro reaction
reactants = [mol for mol in reactants if len(mol) > 0]
# validate products
if validate_products:
for mol in reactants:
try:
mol.kekule()
if mol.check_valence():
yield None
mol.thiele()
except InvalidAromaticRing:
yield None
yield reactants