#!/usr/bin/python """Tree improver that conducts sequential alignment and then tree improvement using other CIPRES services.""" import os import copy from PIPRes.util.server_import import * from PIPRes.cipres_types import * from PIPRes.wrap.external_program import mapTreeToLeafSubset from PIPRes.util.cipres_registry_from_xml import composeRegistryQuery from PIPRes.util.io import cipresGetLogger _LOG = cipresGetLogger('ciprespy.services.realigning_tree_improver.py') def toRecursiveScriptable(o): return o._narrow(CipresIDL_api1__POA.RecursiveScriptable) def tryRemove(o, loggerObj): """Calls remove on o. (exceptions are caught and logged).""" if o is None: return try: o.remove() except: logException(loggerObj) class RealigningTreeImprover(object, CipresIDL_api1__POA.DelegatingTreeImprove, SimpleServer): defaultAlignerEntry = composeRegistryQuery(interface='MatrixAlign', applicationName='clustalW') defaultTreeSearcherEntry = composeRegistryQuery(interface='TreeImprove', applicationName='paup') defaultTreeScorerEntry = composeRegistryQuery(interface='TreeEvaluate', applicationName='paup') debugging = False def __init__(self, registry): self.debugging = RealigningTreeImprover.debugging SimpleServer.__init__(self, registry) self.registry = registry # Note, _localMatrixRows stores references to all of the rows # in self.unalignedMat # self.unalignedMat is sometimes just a subset of the rows from the # matrix sent into setMatrix self.unalignedMat = None self._localMatrixRows = None self._localAlignedMatrixRows = None self.origTree = None self.alignerObjRef = None self.alignerEntry = copy.copy(RealigningTreeImprover.defaultAlignerEntry) self.alignerCmds = '' self.treeSearcherObjRef = None self.treeSearcherEntry = copy.copy(RealigningTreeImprover.defaultTreeSearcherEntry) self.treeSearcherCmds = '' self.treeScorerObjRef = None self.treeScorerEntry = copy.copy(RealigningTreeImprover.defaultTreeScorerEntry) self.treeScorerCmds = '' self.saveAlignment = True def setAlignedMatrix(self, mat): self.alignedMat = CipresDiscreteMatrix(mat) self._localAlignedMatrixRows = [i for i in self.alignedMat.m_matrix] def setMatrix(self, mat): _LOG.debug('setMatrix called\n') # _LOG.debug('matrix = %s\n' % mat) self.setAlignedMatrix(mat) self.unalignedMat = self.alignedMat.getUngappedVersion() #@TODO the clustal wrapper does not remove gaps or tell clustal to ignore gaps # shallow matrix copy so we don't copy row data self._localMatrixRows = [i for i in self.unalignedMat.m_matrix] def setTree(self, tree): _LOG.debug('setTree called. Converting arg to CipresTree\n') self.origTree = CipresTree(tree) _LOG.debug('setTree called with arg:\n') _LOG.debug(self.origTree.m_newick) def executeRecursive(self, commandTree): _LOG.debug("Entering executeRecursive") resultStrList = [] try: for cmd in commandTree.commands: succeeded, result = self.execute(cmd) if not succeeded: return False, result resultStrList.append(result) for child in commandTree.children: target = self.getScriptableObjectRef(child.commandTarget) if target is None: return False, "Target of commands (%s) is not recognized" % child.commandTarget recursiveTarget = toRecursiveScriptable(target) if recursiveTarget is None: for cmd in child.commands: succeeded, result = target.execute(cmd) if not succeeded: return False, result resultStrList.append(result) else: succeeded, result = recursiveTarget.executeRecursive(child) if not succeeded: return False, result resultStrList.append(result) return True, '\n'.join(resultStrList) except: logException(_LOG) return False, "Configuration failed unexpectedly" def getScriptableObjectRef(self, name): if name.upper() == 'ALIGNER': return self.alignerObjRef is None and self.incarnateAligner() or self.alignerObjRef elif name.upper() == 'TREESEARCHER': return self.treeSearcherObjRef is None and self.incarnateTreeSearcher() or self.treeSearcherObjRef elif name.upper() == 'TREESCORER': return self.treeScorerObjRef is None and self.incarnateTreeScorer() or self.treeScorerObjRef return None def removeServants(self): self.removeAligner() self.removeTreeSearcher() self.removeTreeScorer() def removeAligner(self): tryRemove(self.alignerObjRef, _LOG) self.alignerObjRef = None def removeTreeSearcher(self): tryRemove(self.treeSearcherObjRef, _LOG) self.treeSearcherObjRef = None def removeTreeScorer(self): tryRemove(self.treeScorerObjRef, _LOG) self.treeScorerObjRef = None def incarnateServants(self, forceReplace=False): if forceReplace or self.alignerObjRef is None: self.incarnateAligner() if forceReplace or self.treeSearcherObjRef is None: self.incarnateTreeSearcher() if forceReplace or self.treeScorerObjRef is None: self.incarnateTreeScorer() def incarnateAligner(self): self.removeAligner() self.alignerObjRef = self.registry.getServiceOrThrow(CipresIDL_api1.MatrixAlign, registryEntry=self.alignerEntry) return self.alignerObjRef def incarnateTreeSearcher(self): self.removeTreeSearcher() self.treeSearcherObjRef = self.registry.getServiceOrThrow(CipresIDL_api1.TreeImprove, registryEntry=self.treeSearcherEntry) return self.treeSearcherObjRef def incarnateTreeScorer(self): self.removeTreeScorer() self.treeScorerObjRef = self.registry.getServiceOrThrow(CipresIDL_api1.TreeEvaluate, registryEntry=self.treeScorerEntry) return self.treeScorerObjRef def remove(self): self.removeServants() SimpleServer.remove(self) def _copyRows(self, mat, dataRows, rowIndices): """Sets self.unalignedMat to hold only the rows specified (rows is a list of row indices). Note: the data for the slices is coming from self._localMatrixRows not the self.unalignedMat itself self._localMatrixRows is a copy of the original self.unalignedMat.m_matrix """ self.unalignedMat.m_matrix *= 0 try: self.unalignedMat.m_matrix.extend([self._localMatrixRows[i] for i in rows]) except IndexError: for i in rows: if i > len(nRowsInMatrix): raise ValueError, 'Leaf number (%d) exceeds the number of taxa in the matrix (%d)' %(i+1, nRowsInMatrix) raise self.unalignedMat.notifyResized() def _sliceMatWithExternalRowData(mat, rowData, rowIndices): mat *= 0 try: mat.extend([rowData[i] for i in rowIndices]) except IndexError: for i in rows: if i > len(rowData): raise ValueError, 'Leaf number (%d) exceeds the number of taxa in the matrix (%d)' %(i+1, maxInd) raise _sliceMatWithExternalRowData = staticmethod(_sliceMatWithExternalRowData) def _sliceUnalignedMatrix(self, rows): """Sets self.unalignedMat to hold only the rows specified (rows is a list of row indices). Note: the data for the slices is coming from self._localMatrixRows not the self.unalignedMat itself self._localMatrixRows is a copy of the original self.unalignedMat.m_matrix """ RealigningTreeImprover._sliceMatWithExternalRowData(self.unalignedMat.m_matrix, self._localMatrixRows, rows) self.unalignedMat.notifyResized() def _sliceAlignedMatrix(self, rows): """Sets self.unalignedMat to hold only the rows specified (rows is a list of row indices). Note: the data for the slices is coming from self._localMatrixRows not the self.unalignedMat itself self._localMatrixRows is a copy of the original self.unalignedMat.m_matrix """ assert(self.__dict__.get('alignedMat') is not None) RealigningTreeImprover._sliceMatWithExternalRowData(self.alignedMat.m_matrix, self._localAlignedMatrixRows, rows) self.alignedMat.notifyResized() def improveTree(self, proxyConsumer): try: assert(self.origTree is not None) assert(self.unalignedMat is not None) assert(self._localMatrixRows is not None) self.incarnateServants() #should be getting the real taxa names in this function (if the IDL for a tree improver were correct!) nLeavesInTree = self.origTree.n_taxa nRowsInMatrix = len(self._localMatrixRows) if nLeavesInTree != nRowsInMatrix: assert(nLeavesInTree < nRowsInMatrix) mappedTree, taxa, rows, toExternalTranslation = mapTreeToLeafSubset(self.origTree) self._sliceUnalignedMatrix(rows) else: mappedTree = self.origTree toExternalTranslation = None taxa = [('tax%d' % (i + 1)) for i in range(nLeavesInTree)] rows = None _LOG.debug('sliced taxa = %s' % ', '.join(taxa)) _LOG.debug('mapped tree= %s' % str(mappedTree)) _LOG.debug('mapped tree.m_newick= %s' % str(mappedTree.m_newick)) _LOG.debug('mapped tree.m_leafSet= %s' % str(mappedTree.m_leafSet)) self.alignerObjRef.setTaxa(taxa) self.alignerObjRef.setInputMatrix(self.unalignedMat) if not mappedTree.has_edge_len: if rows is not None: self._sliceAlignedMatrix(rows) self.treeScorerObjRef.setMatrix(self.alignedMat) self.treeScorerObjRef.setTree(mappedTree) mappedTree = CipresTree(self.treeScorerObjRef.evaluateTree(proxyConsumer)) self.alignerObjRef.setGuideTree(mappedTree) self.alignerObjRef.execute('') # This is workaround to allow self.alignedMat = self.alignerObjRef.getAlignedMatrix() self.treeSearcherObjRef.setMatrix(self.alignedMat) # we'll get rid of alignedMat ASAP to save on memory (we cannot delete # the unaligned data because we might be asked to align again). if not self.saveAlignment: del self.alignedMat self.treeSearcherObjRef.setTree(mappedTree) treeToReturn = CipresTree(self.treeSearcherObjRef.improveTree(proxyConsumer)) _LOG.debug(str(treeToReturn)) if toExternalTranslation is not None: treeToReturn.map_leaves(toExternalTranslation) _LOG.debug(str(treeToReturn)) return treeToReturn except: logException(_LOG) return nilTree() def createRealigningImproverWrap(registry, argv): return RealigningTreeImprover(registry) if __name__=='__main__': try: interfaceDict = {} for k in ['DelegatingTreeImprove' ]: ####, 'TreeInfer']: interfaceDict[k] = RealigningTreeImprover cipresServe(sys.argv, interfaceDict) except: logException(_LOG)