-
Notifications
You must be signed in to change notification settings - Fork 25
/
nxtree.py
185 lines (148 loc) · 5.67 KB
/
nxtree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python
#Copyright (C) 2006-2012 by Glenn Hickey
#
#Released under the MIT license, see LICENSE.txt
#!/usr/bin/env python
"""A more general (ie arbitrary degree) tree to replace the BinaryTree
in sonLib.tree.py. Implemented as a lightweight wrapper over a
networkx digraph. nodes are accessed using unique ids.
for now, there is no interface (beyond directly working the
networkx graph or reading from newick) to modify the structure of the tree.
update -- added function to remove leaves (and their parents if deg. 2)
"""
import sys
import os
import networkx as NX
from optparse import OptionParser
class NXTree(object):
def __init__(self, nxDg = None):
self.nxDg = nxDg
if self.nxDg is None:
self.nxDg = NX.DiGraph()
self.isTree()
self.rootId = None
self.computeRootId()
def isTree(self):
assert NX.is_directed_acyclic_graph(self.nxDg)
for node in self.nxDg.nodes():
assert len(self.nxDg.in_edges(node)) < 2
def computeRootId(self):
self.rootId = None
for node in self.nxDg.nodes():
if len(self.nxDg.in_edges(node)) == 0:
self.rootId = node
break
def loadNetworkXTree(self, nxDg):
self.nxDg = nxDg
self.isTree()
self.computeRootId()
def getChildren(self, id):
assert id in self.nxDg
return sorted([x[1] for x in self.nxDg.out_edges(id)])
def isLeaf(self, id):
return len(self.getChildren(id)) == 0
def getLeaves(self):
leaves = []
for i in self.breadthFirstTraversal():
if self.isLeaf(i):
leaves.append(i)
return leaves
def hasParent(self, id):
return self.getParent(id) is not None
def getParent(self, id):
assert id in self.nxDg
edges = self.nxDg.in_edges(id)
assert len(edges) < 2
if len(edges) == 0:
return None
else:
return edges[0][0]
def getName(self, id):
assert id in self.nxDg
node = self.nxDg.node[id]
if 'name' in node:
return node['name']
return ""
def setName(self, id, name):
assert id in self.nxDg
self.nxDg.node[id]['name'] = name
def hasName(self, id):
assert id in self.nxDg
node = self.nxDg.node[id]
return 'name' in node
def getWeight(self, parentId, childId, defaultValue=None):
assert parentId in self.nxDg and childId in self.nxDg
edge = self.nxDg[parentId][childId]
if 'weight' in edge:
return edge['weight']
return defaultValue
def setWeight(self, parentId, childId, weight):
assert parentId in self.nxDg and childId in self.nxDg
self.nxDg[parentId][childId]['weight'] = float(weight)
def getRootId(self):
return self.rootId
def getRootName(self):
return self.getName(self.getRootId())
def preOrderTraversal(self, root = None):
if root is None:
root = self.rootId
yield root
for child in self.getChildren(root):
for node in self.preOrderTraversal(child):
yield node
def postOrderTraversal(self, root = None):
if root is None:
root = self.rootId
for child in self.getChildren(root):
for node in self.postOrderTraversal(child):
yield node
yield root
def breadthFirstTraversal(self, root = None):
if root is None:
root = self.rootId
bfQueue = [root]
while len(bfQueue) > 0:
node = bfQueue.pop(0)
yield node
for child in self.getChildren(node):
bfQueue.append(child)
def removeDegree2Vertex(self, nodeId):
if self.hasParent(nodeId) is True:
parentId = self.getParent(nodeId)
topWeight = self.getWeight(parentId, nodeId)
children = self.getChildren(nodeId)
if len(children) == 1:
childId = children[0]
botWeight = self.getWeight(nodeId, childId)
if topWeight is None and botWeight is None:
newWeight = None
elif topWeight is None:
newWeight = botWeight
elif botWeight is None:
newWeight = topWeight
else:
newWeight = topWeight + botWeight
self.nxDg.remove_node(nodeId)
self.nxDg.add_edge(parentId, childId)
self.nxDg[parentId][childId]['weight'] = float(newWeight)
def removeLeaf(self, leafId):
assert self.isLeaf(leafId)
parentId = self.getParent(leafId)
self.nxDg.remove_node(leafId)
if len(self.getChildren(parentId)) == 1 and \
self.hasParent(parentId) is True:
self.removeDegree2Vertex(parentId)
def removeEdge(self, parentId, childId):
self.nxDg.remove_edge(parentId, childId)
def reroot(self, newRootId):
flipEdges = []
node = newRootId
while self.hasParent(node) is True:
parent = self.getParent(node)
flipEdges.append((parent, node))
node = parent
for edge in flipEdges:
self.nxDg.add_edge(edge[1], edge[0])
self.nxDg[edge[1]][edge[0]] = self.nxDg[edge[0]][edge[1]]
self.nxDg.remove_edge(edge[0], edge[1])
self.rootId = newRootId