Skip to content

Commit b66bdb3

Browse files
committed
fixes for sub graphs
1 parent aec5edc commit b66bdb3

1 file changed

Lines changed: 82 additions & 9 deletions

File tree

graphutil/graphutil.py

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
# --David Minor April 9, 2018 -from here on check git hub
1717
# --https://github.com/dahvid/graphutil.git
1818

19+
#this breaks subgraphs from version 2
20+
version = '3.0.1'
21+
1922
has_graphviz = True
2023
try:
2124
import graphviz as pgv
@@ -253,6 +256,11 @@ def clear(self):
253256
def set_attribute(self, name, value):
254257
self.graph_attributes[name] = value
255258

259+
def add_attributes(self, key_values):
260+
for k,v in key_values.items():
261+
self.graph_attributes[k] = v
262+
263+
256264
def get_attribute(self, name):
257265
return self.graph_attributes.get(name)
258266

@@ -288,11 +296,30 @@ def substitute(self, node, g):
288296
self.ccs_dirty = True
289297
self.topo_dirty = True
290298

299+
"""
300+
This inserts a node after the from_node, interrupting and re-routing all outputs
301+
through the after_node. attributes will be preserved until from_attr or to_attr are given
302+
assumes the after_node is already added to the graph
303+
"""
304+
def insert_node_after(self,from_node, after_node, from_attr=None, to_attr=None):
305+
from_arcs = self.out_arcs(from_node)
306+
if from_arcs:
307+
for f in from_arcs:
308+
to_node = self.tail(f)
309+
to_data = to_attr if to_attr else self.edge_data(f)[2]
310+
from_data = from_attr if from_attr else self.edge_data(f)[2]
311+
self.delete_edge(f)
312+
#duplicate insertion means nothing here
313+
self.add_edge(from_node,after_node,edge_data=from_data, no_except=True)
314+
self.add_edge(after_node, to_node, edge_data=to_data)
315+
else: #is leaf node, create an edge from scratch
316+
self.add_edge(from_node,after_node,edge_data=from_attr)
317+
291318

292319

293320

294321
#TODO this should use a dfs for effeciency, instead of duplicating edges
295-
def induce(self, nodes, label=None):
322+
def induce(self, nodes, label=None, attributes=None):
296323
"""
297324
creates an induced graph from the passed in set of tasks
298325
:return graph, dangling out edges, dangling in edges
@@ -321,6 +348,8 @@ def induce(self, nodes, label=None):
321348
dangling_out_edges += [(head,tail,data)]
322349
for (head,tail),data in edges.items():
323350
g.add_edge(head,tail,data)
351+
if attributes:
352+
g.add_attributes(attributes)
324353
return (g, dangling_in_edges, dangling_out_edges)
325354

326355

@@ -545,6 +574,10 @@ def add_edge(self, head_id, tail_id, edge_data=None, no_except=False):
545574
self.ccs_dirty = True
546575
return edge_id
547576

577+
def set_edge_data(self,edge_id, data):
578+
ed = self.edge_data(edge_id)
579+
self.edges[edge_id] = (ed[0],ed[1],data)
580+
548581

549582
# --Removes the edge from the normal graph, but does not delete
550583
# --its information. The edge is held in a separate structure
@@ -655,6 +688,9 @@ def get_edges(self, head_id, tail_id):
655688
return edges
656689

657690

691+
def get_edge_attributes(self,edge_id):
692+
d = self.edge_data(edge_id)
693+
return d[2]
658694
# print "WARNING: No edge to return."
659695

660696
def number_of_nodes(self):
@@ -740,6 +776,21 @@ def set_node_data(self, node_id, data):
740776
def edge_data(self, edge_id):
741777
return self.edges[edge_id]
742778

779+
def is_linear(self):
780+
return not self.has_splits() and not self.has_joins()
781+
782+
# --Returns true if graph has any splits
783+
def has_splits(self):
784+
for n in self.node_list():
785+
if len(self.out_arcs(n)) > 1:
786+
return True
787+
return False
788+
789+
def has_joins(self):
790+
for n in self.node_list():
791+
if len(self.in_arcs(n)) > 1:
792+
return True
793+
return False
743794

744795
# --Returns a reference to the head of the edge. (A reference to the head id)
745796
def head(self, edge):
@@ -766,7 +817,7 @@ def tail_data(self, edge):
766817
def out_arcs(self, node_id):
767818
if node_id not in self.nodes.keys():
768819
return []
769-
return self.nodes[node_id][1]
820+
return copy.copy(self.nodes[node_id][1])
770821

771822

772823
# --Returns a copy of the list of edge data of the node's out arcs.
@@ -778,7 +829,7 @@ def out_arcs_data(self, node_id):
778829
def in_arcs(self, node_id):
779830
if node_id not in self.nodes.keys():
780831
return []
781-
return self.nodes[node_id][0]
832+
return copy.copy(self.nodes[node_id][0])
782833

783834

784835
# --Returns list of adjacent nodes on input arcs
@@ -800,8 +851,6 @@ def out_adjacency_list(self):
800851
return dict([(node, self.out_adjacent(node)) for node in self.nodes])
801852

802853

803-
# return { node : self.out_adjacent(node) for node in self.nodes}
804-
805854
# --Similar to above.
806855
def in_arcs_data(self, node_id):
807856
return [self.edge_data(edge_id) for edge_id in self.in_arcs(node_id)]
@@ -833,7 +882,7 @@ def degree(self, node_id):
833882

834883
# --merges graph into this graph
835884
# --overwriting any shared nodes
836-
def merge(self, graph):
885+
def merge(self, graph, remove_old_edges=False):
837886
for node,data in graph.node_dict().items():
838887
if node in self.node_list():
839888
self.set_node_data(node,data)
@@ -843,6 +892,13 @@ def merge(self, graph):
843892
for edge,data in graph.edge_dict().items():
844893
if edge not in self.edge_dict():
845894
self.add_edge(edge[0],edge[1],data[2])
895+
if remove_old_edges:
896+
for edge, data in self.edge_dict().items():
897+
if edge[0] in graph.node_list() and (edge[1] in graph.node_list()):
898+
if edge not in graph.edge_dict():
899+
edges = self.get_edges(edge[0],edge[1])
900+
for e in edges:
901+
self.delete_edge(e)
846902

847903
# location of each node in topo list
848904
def make_topo_node_finder(self):
@@ -968,9 +1024,6 @@ def connected_components(self):
9681024
else:
9691025
return self.ccs
9701026

971-
972-
973-
9741027
# --Tells dfs to stop searching this branch and go on to the next one
9751028
class SkipBranch(Exception):
9761029
pass
@@ -1004,6 +1057,25 @@ def dfs(self, source_id, visitor=None, repeat=False):
10041057
nodes_already_stacked[self.tail(edge)] = 0
10051058
dfs_stack.push(self.tail(edge))
10061059
return dfs_list
1060+
"""
1061+
Combindes dfs with topo, will do dfs but insure topo order followed
1062+
"""
1063+
def dfs_topo_sort(self):
1064+
visited = { n : False for n in self.node_list()}
1065+
result = []
1066+
def DFS(node):
1067+
if visited[node]:
1068+
return
1069+
visited[node] = True
1070+
for adj in self.out_adjacent(node):
1071+
DFS(adj)
1072+
result.append(node)
1073+
1074+
for i in self.topological_sort():
1075+
DFS(i)
1076+
1077+
result.reverse()
1078+
return result
10071079

10081080

10091081
class Counter:
@@ -1286,3 +1358,4 @@ def robust_topological_sort(self):
12861358
sorted_components = robust_topological_sort(graph)
12871359
# print 'robust topo sort', sorted_components
12881360
return sorted_components
1361+

0 commit comments

Comments
 (0)