"""
Implementation of the bidirectional search algorithm.
"""
import time
from molpher.core.ExplorationTree import ExplorationTree as ETree
from molpher.core.operations import *
[docs]def timeit(func):
milliseconds = 1000 * time.clock()
func()
return 1000 * time.clock() - milliseconds
[docs]class FindClosest:
def __init__(self):
self.closest = None
def __call__(self, morph):
if not self.closest:
self.closest = morph.copy()
return
current_dist = self.closest.getDistToTarget()
morph_dist = morph.getDistToTarget()
if morph_dist < current_dist:
self.closest = morph.copy()
[docs]class BidirectionalPathFinder:
def __init__(self, source, target):
options = {
'fingerprint' : 'ATOM_PAIRS'
}
self.source_target = ETree.create(source=source, target=target)
self.target_source = ETree.create(source=target, target=source)
self.source_target.params = options
self.target_source.params = options
self.source_target_min = FindClosest()
self.target_source_min = FindClosest()
self.ITERATION = [
GenerateMorphsOper()
, SortMorphsOper()
, FilterMorphsOper()
, ExtendTreeOper()
, PruneTreeOper()
]
self.path = []
def _find_path(self, tree, connecting_mol):
path = []
current = tree.fetchMol(connecting_mol)
path.append(current.getSMILES())
while current != '':
current = current.getParentSMILES()
if current:
current = tree.fetchMol(current)
path.append(current.getSMILES())
path.reverse()
return path
def __call__(self):
counter = 0
connecting_molecule = None
while True:
counter+=1
print('Iteration {0}:'.format(counter))
for oper in self.ITERATION:
print('Execution times ({0}):'.format(type(oper).__name__))
source_target_time = timeit(lambda : self.source_target.runOperation(oper))
print('\tsource -> target: {0}'.format(source_target_time))
target_source_time = timeit(lambda : self.target_source.runOperation(oper))
print('\ttarget -> source: {0}'.format(target_source_time))
print('\ttotal time: {0}'.format(source_target_time + target_source_time))
print('Traversal times:')
source_target_time = timeit(lambda : self.source_target.traverse(self.source_target_min))
print('\tsource -> target: {0}'.format(source_target_time))
target_source_time = timeit(lambda : self.target_source.traverse(self.target_source_min))
print('\ttarget -> source: {0}'.format(target_source_time))
print('\ttotal time: {0}'.format(source_target_time + target_source_time))
print('Current Targets:')
print('\tsource to target:', self.source_target.params['target'])
print('\ttarget to source:', self.target_source.params['target'])
print('Current Minima:')
print('\tsource to target:', self.source_target_min.closest.getSMILES(), self.source_target_min.closest.getDistToTarget())
print('\ttarget to source:', self.target_source_min.closest.getSMILES(), self.target_source_min.closest.getDistToTarget())
self.source_target.params = {
'target' : self.target_source_min.closest.getSMILES()
}
self.target_source.params = {
'target' : self.source_target_min.closest.getSMILES()
}
print('New Targets:')
print('\tsource to target:', self.source_target.params['target'])
print('\ttarget to source:', self.target_source.params['target'])
if self.source_target.path_found:
print('Path Found in tree going from source to target:')
connecting_molecule = self.source_target.params['target']
print('Connecting molecule:', connecting_molecule)
assert self.source_target.hasMol(connecting_molecule)
assert self.target_source.hasMol(connecting_molecule)
break
if self.target_source.path_found:
print('Path Found in tree going from target to source:')
connecting_molecule = self.target_source.params['target']
print('Connecting molecule:', connecting_molecule)
assert self.target_source.hasMol(connecting_molecule)
assert self.source_target.hasMol(connecting_molecule)
break
source_target_path = self._find_path(self.source_target, connecting_molecule)
target_source_path = self._find_path(self.target_source, connecting_molecule)
assert source_target_path.pop(-1) == connecting_molecule
target_source_path.reverse()
source_target_path.extend(target_source_path)
self.path = source_target_path
[docs]def main():
milliseconds_now = 1000 * time.clock()
cocaine = 'CN1[C@H]2CC[C@@H]1[C@@H](C(=O)OC)[C@@H](OC(=O)c1ccccc1)C2'
procaine = 'O=C(OCCN(CC)CC)c1ccc(N)cc1'
pathfinder = BidirectionalPathFinder(cocaine, procaine)
pathfinder()
print(pathfinder.path)
print('Total Execution Time: {0}'.format(1000 * time.clock() - milliseconds_now))
if __name__ == "__main__":
exit(main())