Source code for molpher.core.tests.test_api


# 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 os
import unittest

from pkg_resources import resource_filename

from molpher.core import ExplorationTree
from molpher.core import MolpherMol
from molpher.core.operations import *
from molpher.core.selectors import *
from molpher.core import ExplorationData

[docs]class TestPythonAPI(unittest.TestCase):
[docs] def setUp(self): self.test_source = 'CCO' self.test_target = 'C1=COC=C1' self.test_dir = os.path.abspath(resource_filename('molpher.core.tests', 'test_files/')) self.test_template_path = os.path.join(self.test_dir, 'test-template.xml')
[docs] def tearDown(self): pass
[docs] def testMolpherMol(self): mol = MolpherMol(self.test_target) mol.smiles = 'CCC' self.assertTrue('CCC') # mol.historic_descendents = ('CCC', 'CCCC') # self.assertEqual(('CCC', 'CCCC'), mol.historic_descendents) copy = mol.copy() copy.sascore = 0.54 self.assertEqual(0.54, copy.sascore) self.assertEqual(0, mol.sascore) tree = ExplorationTree.create(source=mol.smiles, target='CCCNCCC') tree = ExplorationTree.create(source=mol, target='CCCNCCC') tree = ExplorationTree.create(source=mol, target=MolpherMol('CCCNCCC')) self.assertTrue(tree.hasMol(mol)) def assign(x): tree.fetchMol(mol.smiles).smiles = x self.assertRaises(RuntimeError, assign, 'CCO')
[docs] def testExplorationData(self): params = ExplorationData( source=self.test_source , target=self.test_target ) params.operators = set((OP_ADD_BOND, 'OP_REMOVE_BOND')) self.assertEqual(params.operators, ('OP_ADD_BOND', 'OP_REMOVE_BOND')) params.fingerprint = FP_EXT_ATOM_PAIRS self.assertEqual(params.fingerprint, 'FP_EXT_ATOM_PAIRS') params.fingerprint = 'FP_TOPOLOGICAL_LAYERED_2' self.assertEqual(params.fingerprint, 'FP_TOPOLOGICAL_LAYERED_2') params.similarity = 'SC_COSINE' self.assertEqual(params.similarity, 'SC_COSINE') params.similarity = SC_KULCZYNSKI self.assertEqual(params.similarity, 'SC_KULCZYNSKI') self.assertEqual(params.source.smiles, self.test_source) self.assertEqual(params.target.smiles, self.test_target) params.param_dict = { 'target' : self.test_source , 'operators' : params.param_dict['operators'][:1] } self.assertEqual(params.target.smiles, self.test_source) self.assertEqual(params.operators, ('OP_ADD_BOND',)) params_from_temp = ExplorationData.load(self.test_template_path) self.assertRaises(RuntimeError, lambda : ExplorationData.load('not/a/valid/path')) self.assertEqual(777, params_from_temp.far_produce)
[docs] def testTree(self): mol1 = self.test_source mol2 = self.test_target params_dict = { 'source' : mol1 , 'target' : mol2 , 'operators' : (OP_ADD_BOND, OP_REMOVE_BOND, OP_MUTATE_ATOM) } params = ExplorationData(**params_dict) self.assertRaises(AttributeError, lambda : ExplorationTree()) tree_from_dict = ExplorationTree.create(tree_data=params_dict) tree_from_params = ExplorationTree.create(tree_data=params) tree_from_SMILES = ExplorationTree.create(source=mol1, target=mol2) def test_tree(tree): self.assertEqual(tree.params['source'], mol1) self.assertEqual(tree.params['target'], mol2) test_tree(tree_from_dict) test_tree(tree_from_params) test_tree(tree_from_SMILES) tree = tree_from_params def func(): tree.params = {'source' : ''} self.assertRaises(RuntimeError, func) tree.params = { 'source' : mol2 # if we try to set source for non-empty tree, it shouldn't be changed , 'target' : 'C' } self.assertEqual(tree.params['source'], mol1) self.assertEqual(tree.params['target'], 'C') self.assertEqual(tree.params['operators'], params.param_dict['operators']) # we should still have the same opers set tree.params = params # assign the original parameters back self.assertEqual(tree.params['source'], mol1) self.assertEqual(tree.params['target'], mol2) self.assertEqual(tree.params['operators'], params.param_dict['operators']) leaf = tree.leaves[0] self.assertRaises(RuntimeError, lambda : leaf.setSMILES('CCCC')) self.assertTrue(tree.hasMol(leaf)) # self.assertEqual(tree, leaf.tree) # FIXME: add a reliable operator for comparison between trees leaf.setDistToTarget(0.5) self.assertEqual(tree.leaves[0].getDistToTarget(), 0.5) leaf_copy = tree.leaves[0].copy() # self.assertFalse(tree.hasMol(leaf_copy)) # FIXME: add a reliable operator for comparison between trees (this should check both the SMILES and the tree ownership) self.assertEqual(leaf_copy.getDistToTarget(), 0.5) leaf_copy.setDistToTarget(0.7) self.assertEqual(leaf.getDistToTarget(), 0.5) self.assertEqual(tree.leaves[0].getDistToTarget(), 0.5) self.assertEqual(leaf_copy.getDistToTarget(), 0.7)
[docs] def testOperations(self): tree = ExplorationTree.create(tree_data={ 'source' : self.test_source , 'target' : self.test_target }) iteration = [ GenerateMorphsOper() , SortMorphsOper() , FilterMorphsOper() , ExtendTreeOper() , PruneTreeOper() ] for oper in iteration: self.assertRaises(RuntimeError, lambda : oper()) fl = FindLeavesOper() for oper in iteration: tree.runOperation(oper) tree.runOperation(fl) for leaf1, leaf2, leaf3 in zip(sorted(fl.leaves), sorted(fl.tree.leaves), sorted(tree.leaves)): self.assertTrue(leaf1.smiles == leaf2.smiles == leaf3.smiles)
[docs] def testMorphing(self): def callback(morph): callback.morphs_in_tree += 1 self.assertTrue(morph) self.assertTrue(morph.tree) if morph.getItersWithoutDistImprovement() > 3: print('Callback output:') print(morph.getSMILES(), morph.getItersWithoutDistImprovement(), morph.getDistToTarget()) if not callback.closest_mol: callback.closest_mol = morph current_dist = morph.getDistToTarget() min_dist = callback.closest_mol.getDistToTarget() if min_dist > current_dist: callback.closest_mol = morph callback.morphs_in_tree = 0 callback.closest_mol = None class MorphingIteration(TreeOperation): parent = self def __init__(self, tree): super(MorphingIteration, self).__init__() self._tree = tree def __call__(self): print('Iteration: ', self._tree.getGenerationCount() + 1) self._tree.generateMorphs() for mol in self._tree.candidates: self.parent.assertEqual(None, mol.tree) self._tree.filterMorphs() self._tree.extend() self._tree.prune() callback.morphs_in_tree = 0 self._tree.traverse(callback) print('Number of morphs in the tree: ', callback.morphs_in_tree) print('Closest molecule to target: {0} -- distance: {1}'.format( callback.closest_mol.getSMILES() , callback.closest_mol.getDistToTarget() )) def getTree(self): return self._tree def setTree(self, tree): self._tree = tree tree = ExplorationTree.create(tree_data={ 'source' : self.test_source , 'target' : self.test_target }) iterate = MorphingIteration(tree) counter = 0 while counter < 5: iterate() counter += 1 child = tree.leaves[0] self.assertTrue(child.tree) self.assertTrue(tree.hasMol(child)) parent = child.getParentSMILES() tree.deleteSubtree(parent) self.assertFalse(tree.hasMol(parent)) self.assertFalse(tree.hasMol(child)) self.assertEqual(None, child.tree) self.assertEqual(parent, child.getParentSMILES()) # check descendents def check_descs(morph): for desc_smiles in morph.descendents: desc = tree.fetchMol(desc_smiles) self.assertTrue(desc.tree) tree.traverse(check_descs)