Source code for molpher.core.MolpherMol


# Copyright (c) 2016 Martin Sicho
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import warnings
from io import StringIO

import molpher.swig_wrappers.core as wrappers
import molpher.core.ExplorationTree
from molpher.core.MolpherAtom import MolpherAtom
from molpher.core._utils import shorten_repr

from rdkit import Chem
from rdkit.Chem import AllChem


[docs]class MolpherMol(wrappers.MolpherMol): """ :param str_repr: smiles of the molecule that is to be created or a path to an SDF file (only the first molecule is read) :type str_repr: `str` :param other: another instance, the new instance will be its copy (tree ownership is not transferred onto the copy) :type other: `molpher.swig_wrappers.core.MolpherMol` or its derived class This is a specialized version of the `molpher.swig_wrappers.core.MolpherMol` proxy class. It implements some additional functionality for ease of use from Python. .. seealso:: `molpher.swig_wrappers.core.MolpherMol` """ def __init__(self, str_repr=None, other=None): if str_repr and other: raise Exception("Can't specify both string representation and other instance to initialize.") if other and isinstance(other, wrappers.MolpherMol): super(MolpherMol, self).__init__(other) elif other and isinstance(other, Chem.Mol): # FIXME: this should be handled through a SWIG typemap from the C++ side or something similar (http://rdkit-discuss.narkive.com/edZG2UM5/pyobject-rdkit-romol) other_mmol = MolpherMol.fromMolBlock(Chem.MolToMolBlock(other)) placed_locks = other.GetPropsAsDict() for key in placed_locks: lock_name = key.replace("MOLPHER_", "") locked_indices = [int(idx)-1 for idx in placed_locks[key].split(',')] for idx in locked_indices: atom = other_mmol.atoms[idx] atom.locking_mask = atom.locking_mask | getattr(MolpherAtom, lock_name) super(MolpherMol, self).__init__(other_mmol) elif str_repr and type(str_repr) == str: super(MolpherMol, self).__init__(str_repr) elif not str_repr and not other: super(MolpherMol, self).__init__() else: raise AttributeError('Invalid arguments supplied to the constructor.') def __repr__(self): return shorten_repr(MolpherMol, self) def __lt__(self, other): return self.smiles < other.smiles
[docs] @staticmethod def fromMolBlock(block): ret = wrappers.MolpherMol.fromMolBlock(block) ret.__class__ = MolpherMol return ret
[docs] def copy(self): """ Returns a copy of this instance. If this instance has a `tree` assigned, the returned will have `None` instead. :return: a copy of this instance :rtype: """ copy = super(MolpherMol, self).copy() copy.__class__ = MolpherMol return copy
[docs] def asRDMol(self, include_locks = True): # FIXME: this should be handled through a SWIG typemap from the C++ side or something similar (http://rdkit-discuss.narkive.com/edZG2UM5/pyobject-rdkit-romol) ret = Chem.MolFromMolBlock(self.asMolBlock()) AllChem.Compute2DCoords(ret) if include_locks: locks_map = dict() for idx, atm in enumerate(self.atoms): if atm.is_locked: lock_info = atm.lock_info for key in lock_info: if key != 'UNLOCKED' and lock_info[key]: if key not in locks_map: locks_map[key] = [] locks_map[key].append(str(idx+1)) for key in locks_map: ret.SetProp("MOLPHER_{0}".format(key), ",".join(locks_map[key])) return ret
@property def atoms(self): """ Atoms of this molecule represented as MolpherAtom instances. :return: `tuple` """ atoms = self.getAtoms() for x in atoms: x.__class__ = MolpherAtom return atoms
[docs] def getAtom(self, idx): ret = super(MolpherMol, self).getAtom(idx) ret.__class__ = MolpherAtom return ret
@property def tree(self): """ A reference to the tree this instance is currently in. If the molecule is not present in any tree, this value is `None`. :return: reference to the tree this instance is currently in :rtype: :class:`~molpher.core.ExplorationTree.ExplorationTree` or `None` """ ret = self.getTree() if ret: ret.__class__ = molpher.core.ExplorationTree return ret @property def smiles(self): """ :return: canonical SMILES string of this molecule :rtype: `str` """ return self.getSMILES() @smiles.setter def smiles(self, val): self.setSMILES(val) @property def parent_smiles(self): """ Canonical SMILES string of the parent molecule in the tree. Can be an empty `str`, if the molecule is a root of the tree or is not associated with any. :return: canonical SMILES string of the parent molecule in the tree :rtype: `str` """ return self.getParentSMILES() @property def parent_operator(self): """ The name of the :term:`chemical operator <chemical operators>` :term:`selector <selectors>` that lead to the creation of this molecule. :return: name of the parent chemical operator :rtype: `str` """ return self.getParentOper() @parent_operator.setter def parent_operator(self, val): self.setParentOper(val) @property def dist_to_target(self): """ The value of the objective function. In the original implementation, this is the structural distance to the target molecule using a `similarity measure`. This value can be changed. :return: value of the objective function :rtype: `float` """ return self.getDistToTarget() @dist_to_target.setter def dist_to_target(self, dist): self.setDistToTarget(dist) @property def sascore(self): """ The synthetic feasibility score of the molecule according to Ertl. .. todo:: add reference This value can be changed. :return: synthetic feasibility score :rtype: `float` """ return self.getSAScore() @sascore.setter def sascore(self, val): self.setSAScore(val) @property def historic_descendents(self): """ Canonical SMILES strings of all molecules derived from this compound. :return: :rtype: `str` """ return self.getHistoricDescendants() @property def descendents(self): """ Canonical SMILES strings of all molecules derived from this compound that are currently present in the tree. :return: :rtype: `str` """ return self.getDescendants() @property def gens_without_improvement(self): """ Number of morph generations derived from this molecule that did not contain any morphs with an improvement in the objective function from the target molecule. This value can be changed. :return: number of non-producing generations :rtype: `int` """ return self.getItersWithoutDistImprovement() @gens_without_improvement.setter def gens_without_improvement(self, value): self.setItersWithoutDistImprovement(value)