-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFindBoroughs.py
201 lines (171 loc) · 7.01 KB
/
FindBoroughs.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 23 12:05:17 2013
@author: Steven
"""
import os.path
import sys
import networkx as nx
import cPickle as pickle
import time
from collections import defaultdict
import argparse
def boroughs_via_cycles(G, minlen=3, maxlen=5):
"""Find simple cycles (elementary circuits) of at least length minlen
and at most length maxlen of a graph
An simple cycle, or elementary circuit, is a closed path where no
node appears twice, except that the first and last node are the same.
Two elementary circuits are distinct if they are not cyclic permutations
of each other.
Parameters
----------
G : NetworkX Graph
A graph
minlen : int
The minimum length of a cycle
maxlen : int
The maximum length of a cycle
Returns
-------
A list of circuits, where each circuit is a list of nodes, with the first
and last node being the same.
Example:
>>> G = nx.DiGraph([(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)])
>>> nx.simple_cycles(G)
[[0, 0], [0, 1, 2, 0], [0, 2, 0], [1, 2, 1], [2, 2]]
See Also
--------
cycle_basis (for undirected graphs)
Notes
-----
The implementation follows pp. 79-80 in [1]_.
The time complexity is O((n+e)(c+1)) for n nodes, e edges and c
elementary circuits.
References
----------
.. [1] Finding all the elementary circuits of a directed graph.
D. B. Johnson, SIAM Journal on Computing 4, no. 1, 77-84, 1975.
http://dx.doi.org/10.1137/0204007
See Also
--------
cycle_basis
"""
# Jon Olav Vik, 2010-08-09
# Edited by Steven Laan, 2013-01-23
def _unblock(thisnode):
"""Recursively unblock and remove nodes from B[thisnode]."""
if blocked[thisnode]:
blocked[thisnode] = False
while B[thisnode]:
_unblock(B[thisnode].pop())
def circuit(thisnode, startnode, component):
closed = False # set to True if elementary path is closed
path.append(thisnode)
if len(path) <= maxlen:
blocked[thisnode] = True
for nextnode in component[thisnode]: # direct successors of thisnode
if nextnode == startnode:
if len(path) >= minlen:
add_cycle(path + [startnode])
closed = True
elif not blocked[nextnode]:
if circuit(nextnode, startnode, component):
closed = True
if closed:
_unblock(thisnode)
else:
for nextnode in component[thisnode]:
if thisnode not in B[nextnode]:
B[nextnode].append(thisnode)
path.pop() # remove thisnode from path
return closed
def add_cycle(cycle):
my_matches = []
cycle_graph = nx.Graph()
cycle_graph.add_path(cycle)
edges_set = set(cycle_graph.edges())
for borough in boroughs:
for e in edges_set:
if e in borough or (e[1], e[0]) in borough:
my_matches.append(borough)
break
for match in my_matches:
edges_set = edges_set | match
boroughs.remove(match)
if edges_set not in boroughs:
boroughs.append(edges_set)
path = [] # stack of nodes in current path
blocked = defaultdict(bool) # vertex: blocked from search?
B = defaultdict(list) # graph portions that yield no elementary circuit
boroughs = [] # list to accumulate the circuits found
borough_graphs = [] # list to build networkx graphs
# Johnson's algorithm requires some ordering of the nodes.
# They might not be sortable so we assign an arbitrary ordering.
ordering = dict(zip(G, range(len(G))))
for s in ordering:
# Build the subgraph induced by s and following nodes in the ordering
subgraph = G.subgraph(node for node in G
if ordering[node] >= ordering[s])
# Find the strongly connected component in the subgraph
# that contains the least node according to the ordering
strongcomp = nx.connected_components(subgraph)
mincomp = min( strongcomp,
key = lambda nodes: min(ordering[n] for n in nodes))
component = G.subgraph(mincomp)
if component:
# smallest node in the component according to the ordering
startnode = min(component, key = ordering.__getitem__)
for node in component:
blocked[node] = False
B[node][:] = []
dummy = circuit(startnode, startnode, component)
return sorted(boroughs, key=len, reverse=True)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('graph', help='The graph to find the boroughs of.')
parser.add_argument('-o', '--output', help='The file to put the results in.', default='boroughs_result.pickle')
parser.add_argument('-t', '--type', help='The type of the output.', default='pickle')
parser.add_argument('-g', '--graphics', help='Visualize the boroughs.', action='store_true')
parser.add_argument('-i', '--individual', help='Visualize individual boroughs', action='store_true')
parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args()
G = nx.read_graphml(args.graph)
if args.verbose:
print 'starting search (%d nodes)' % len(G.nodes())
start = time.time()
boroughs = boroughs_via_cycles(G)
if args.verbose:
print 'took', time.time() - start, 'seconds'
num_boroughs = len(boroughs)
if args.verbose:
print 'Boroughs found:', num_boroughs
if args.type == 'graphml':
extension = os.path.basename(args.output).split(os.path.extsep)[-1]
index = len(extension) + len(os.path.extsep)
filename = os.path.basename(args.output)[:-index]
for i, borough in enumerate(boroughs):
H = nx.Graph()
H.add_edges_from(borough)
writer = nx.GraphMLWriter(graph=H)
f = open(filename + str(i) + os.path.extsep + extension, 'w')
writer.dump(f)
f.close()
else:
f = open(args.output, 'w')
pickle.dump(boroughs, f)
f.close()
if args.graphics:
import matplotlib.pyplot as plt
pos = nx.spring_layout(G)
for i, borough in enumerate(boroughs):
H = nx.Graph()
H.add_edges_from(borough)
nx.draw_networkx_edges(H, pos)
c = [i] * nx.number_of_nodes(H)
nx.draw_networkx_nodes(H, pos, node_color=c, vmin=0, vmax=num_boroughs, cmap=plt.cm.hsv)
nx.draw_networkx_labels(H, pos)
if args.individual:
plt.show()
if not args.individual:
plt.show()