networkx 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
- data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +20 -10
- data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +1 -1
- data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +2 -4
- data/.github/workflows/ci.yml +17 -0
- data/.github/workflows/doc.yml +23 -0
- data/.rspec +0 -1
- data/.rubocop.yml +57 -71
- data/.yardopts +0 -1
- data/README.md +32 -34
- data/Rakefile +2 -3
- data/lib/networkx/auxillary_functions/cliques.rb +9 -12
- data/lib/networkx/auxillary_functions/cycles.rb +17 -7
- data/lib/networkx/auxillary_functions/dag.rb +10 -5
- data/lib/networkx/auxillary_functions/eccentricity.rb +2 -1
- data/lib/networkx/auxillary_functions/mis.rb +2 -2
- data/lib/networkx/auxillary_functions/mst.rb +1 -3
- data/lib/networkx/auxillary_functions/union_find.rb +92 -12
- data/lib/networkx/auxillary_functions/wiener.rb +1 -1
- data/lib/networkx/converters/to_csv.rb +1 -3
- data/lib/networkx/converters/to_json.rb +0 -2
- data/lib/networkx/digraph.rb +55 -49
- data/lib/networkx/flow/capacityscaling.rb +29 -35
- data/lib/networkx/flow/edmondskarp.rb +17 -15
- data/lib/networkx/flow/preflowpush.rb +29 -32
- data/lib/networkx/flow/shortestaugmentingpath.rb +17 -20
- data/lib/networkx/flow/utils.rb +6 -27
- data/lib/networkx/graph.rb +179 -72
- data/lib/networkx/link_analysis/hits.rb +9 -9
- data/lib/networkx/link_analysis/pagerank.rb +29 -31
- data/lib/networkx/multidigraph.rb +90 -81
- data/lib/networkx/multigraph.rb +91 -63
- data/lib/networkx/operators/all.rb +8 -4
- data/lib/networkx/operators/binary.rb +106 -128
- data/lib/networkx/operators/product.rb +61 -64
- data/lib/networkx/operators/unary.rb +1 -1
- data/lib/networkx/others/bridges.rb +30 -0
- data/lib/networkx/others/generators.rb +237 -0
- data/lib/networkx/others/grid_2d_graph.rb +38 -0
- data/lib/networkx/others/info.rb +11 -0
- data/lib/networkx/others/number_connected_components.rb +17 -0
- data/lib/networkx/others/reads.rb +52 -0
- data/lib/networkx/shortest_path/astar.rb +10 -8
- data/lib/networkx/shortest_path/dense.rb +1 -3
- data/lib/networkx/shortest_path/unweighted.rb +13 -16
- data/lib/networkx/shortest_path/weighted.rb +51 -42
- data/lib/networkx/to_matrix.rb +2 -3
- data/lib/networkx/traversals/bfs.rb +54 -2
- data/lib/networkx/traversals/dfs.rb +62 -6
- data/lib/networkx/traversals/edge_dfs.rb +36 -12
- data/lib/networkx/version.rb +1 -1
- data/lib/networkx.rb +7 -1
- data/networkx.gemspec +17 -14
- metadata +74 -84
- data/.rspec_formatter.rb +0 -24
- data/.travis.yml +0 -18
- data/Guardfile +0 -7
- data/RELEASE_POLICY.md +0 -20
@@ -1,19 +1,15 @@
|
|
1
|
-
# TODO: Reduce module length
|
2
|
-
|
3
1
|
module NetworkX
|
4
2
|
# Helper function to extract weight from a adjecency hash
|
5
3
|
def self.get_weight(graph)
|
6
|
-
|
4
|
+
lambda do |_, _, attrs|
|
7
5
|
return attrs[:weight] || 1 unless graph.multigraph?
|
6
|
+
|
8
7
|
attrs.group_by { |_k, vals| vals[:weight] || 1 }.keys.max
|
9
8
|
end
|
10
|
-
weight_get
|
11
9
|
end
|
12
10
|
|
13
|
-
# TODO: Reduce method length and method complexity
|
14
|
-
|
15
11
|
# Helper function for multisource dijkstra
|
16
|
-
def self.help_multisource_dijkstra(graph, sources, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
|
12
|
+
def self.help_multisource_dijkstra(graph, sources, weight, pred = nil, paths = nil, cutoff = nil, target = nil)
|
17
13
|
count = ->(i) { i + 1 }
|
18
14
|
i = -1
|
19
15
|
dist = {}
|
@@ -26,17 +22,21 @@ module NetworkX
|
|
26
22
|
|
27
23
|
until fringe.empty?
|
28
24
|
d, _, v = fringe.pop
|
29
|
-
next if dist.
|
25
|
+
next if dist.has_key?(v)
|
26
|
+
|
30
27
|
dist[v] = d
|
31
28
|
break if v == target
|
29
|
+
|
32
30
|
graph.adj[v].each do |u, attrs|
|
33
31
|
cost = weight.call(v, u, attrs)
|
34
32
|
next if cost.nil?
|
33
|
+
|
35
34
|
vu_dist = dist[v] + cost
|
36
35
|
next if !cutoff.nil? && vu_dist > cutoff
|
37
|
-
|
36
|
+
|
37
|
+
if dist.has_key?(u)
|
38
38
|
raise ValueError, 'Contradictory weights found!' if vu_dist < dist[u]
|
39
|
-
elsif !seen.
|
39
|
+
elsif !seen.has_key?(u) || vu_dist < seen[u]
|
40
40
|
seen[u] = vu_dist
|
41
41
|
fringe << [vu_dist, count.call(i), u]
|
42
42
|
paths[u] = paths[v] + [u] unless paths.nil?
|
@@ -50,7 +50,7 @@ module NetworkX
|
|
50
50
|
end
|
51
51
|
|
52
52
|
# Helper function for single source dijkstra
|
53
|
-
def self.help_dijkstra(graph, source, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
|
53
|
+
def self.help_dijkstra(graph, source, weight, pred = nil, paths = nil, cutoff = nil, target = nil)
|
54
54
|
help_multisource_dijkstra(graph, [source], weight, pred, paths, cutoff, target)
|
55
55
|
end
|
56
56
|
|
@@ -62,15 +62,17 @@ module NetworkX
|
|
62
62
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
63
63
|
#
|
64
64
|
# @return [Numeric, Array<Object>] path lengths for all nodes
|
65
|
-
def self.multisource_dijkstra(graph, sources, target=nil, cutoff=nil)
|
65
|
+
def self.multisource_dijkstra(graph, sources, target = nil, cutoff = nil)
|
66
66
|
raise ValueError, 'Sources cannot be empty' if sources.empty?
|
67
67
|
return [0, [target]] if sources.include?(target)
|
68
|
+
|
68
69
|
paths = {}
|
69
70
|
weight = get_weight(graph)
|
70
71
|
sources.each { |source| paths[source] = [source] }
|
71
72
|
dist = help_multisource_dijkstra(graph, sources, weight, nil, paths, cutoff, target)
|
72
73
|
return [dist, paths] if target.nil?
|
73
|
-
raise KeyError, "No path to #{target}!" unless dist.
|
74
|
+
raise KeyError, "No path to #{target}!" unless dist.has_key?(target)
|
75
|
+
|
74
76
|
[dist[target], paths[target]]
|
75
77
|
end
|
76
78
|
|
@@ -81,8 +83,9 @@ module NetworkX
|
|
81
83
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
82
84
|
#
|
83
85
|
# @return [Hash{ Object => Numeric }] path lengths for any nodes from given nodes
|
84
|
-
def self.multisource_dijkstra_path_length(graph, sources, cutoff=nil)
|
86
|
+
def self.multisource_dijkstra_path_length(graph, sources, cutoff = nil)
|
85
87
|
raise ValueError, 'Sources cannot be empty' if sources.empty?
|
88
|
+
|
86
89
|
weight = get_weight(graph)
|
87
90
|
help_multisource_dijkstra(graph, sources, weight, nil, nil, cutoff)
|
88
91
|
end
|
@@ -94,7 +97,7 @@ module NetworkX
|
|
94
97
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
95
98
|
#
|
96
99
|
# @return [Hash{ Object => Array<Object> }] paths for any nodes from given nodes
|
97
|
-
def self.multisource_dijkstra_path(graph, sources, cutoff=nil)
|
100
|
+
def self.multisource_dijkstra_path(graph, sources, cutoff = nil)
|
98
101
|
_, path = multisource_dijkstra(graph, sources, nil, cutoff)
|
99
102
|
path
|
100
103
|
end
|
@@ -107,7 +110,7 @@ module NetworkX
|
|
107
110
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
108
111
|
#
|
109
112
|
# @return [Hash{ Object => Array<Object> }, Array<Object>] paths for all nodes/target node from given node
|
110
|
-
def self.singlesource_dijkstra(graph, source, target=nil, cutoff=nil)
|
113
|
+
def self.singlesource_dijkstra(graph, source, target = nil, cutoff = nil)
|
111
114
|
multisource_dijkstra(graph, [source], target, cutoff)
|
112
115
|
end
|
113
116
|
|
@@ -118,7 +121,7 @@ module NetworkX
|
|
118
121
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
119
122
|
#
|
120
123
|
# @return [Hash{ Object => Numeric }] path lengths for all nodes from given node
|
121
|
-
def self.singlesource_dijkstra_path_length(graph, source, cutoff=nil)
|
124
|
+
def self.singlesource_dijkstra_path_length(graph, source, cutoff = nil)
|
122
125
|
multisource_dijkstra_path_length(graph, [source], cutoff)
|
123
126
|
end
|
124
127
|
|
@@ -129,7 +132,7 @@ module NetworkX
|
|
129
132
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
130
133
|
#
|
131
134
|
# @return [Hash{ Object => Array<Object> }] paths for all nodes from given node
|
132
|
-
def self.singlesource_dijkstra_path(graph, source, cutoff=nil)
|
135
|
+
def self.singlesource_dijkstra_path(graph, source, cutoff = nil)
|
133
136
|
multisource_dijkstra_path(graph, [source], cutoff)
|
134
137
|
end
|
135
138
|
|
@@ -142,9 +145,11 @@ module NetworkX
|
|
142
145
|
# @return [Numeric] path length for target node from given node
|
143
146
|
def self.dijkstra_path_length(graph, source, target)
|
144
147
|
return 0 if source == target
|
148
|
+
|
145
149
|
weight = get_weight(graph)
|
146
150
|
length = help_dijkstra(graph, source, weight, nil, nil, nil, target)
|
147
|
-
raise KeyError, 'Node not reachable!' unless length.
|
151
|
+
raise KeyError, 'Node not reachable!' unless length.has_key?(target)
|
152
|
+
|
148
153
|
length[target]
|
149
154
|
end
|
150
155
|
|
@@ -168,7 +173,7 @@ module NetworkX
|
|
168
173
|
#
|
169
174
|
# @return [<Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>]
|
170
175
|
# predcessor hash and distance hash
|
171
|
-
def self.dijkstra_predecessor_distance(graph, source, cutoff=nil)
|
176
|
+
def self.dijkstra_predecessor_distance(graph, source, cutoff = nil)
|
172
177
|
weight = get_weight(graph)
|
173
178
|
pred = {source => []}
|
174
179
|
[pred, help_dijkstra(graph, source, weight, pred, nil, cutoff)]
|
@@ -181,7 +186,7 @@ module NetworkX
|
|
181
186
|
#
|
182
187
|
# @return [Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>]
|
183
188
|
# paths and path lengths between all nodes
|
184
|
-
def self.all_pairs_dijkstra(graph, cutoff=nil)
|
189
|
+
def self.all_pairs_dijkstra(graph, cutoff = nil)
|
185
190
|
path = []
|
186
191
|
graph.nodes.each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
|
187
192
|
path
|
@@ -193,7 +198,7 @@ module NetworkX
|
|
193
198
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
194
199
|
#
|
195
200
|
# @return [Array<Object, Hash{ Object => Numeric }>] path lengths between all nodes
|
196
|
-
def self.all_pairs_dijkstra_path_length(graph, cutoff=nil)
|
201
|
+
def self.all_pairs_dijkstra_path_length(graph, cutoff = nil)
|
197
202
|
path_lengths = []
|
198
203
|
graph.nodes.each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
|
199
204
|
path_lengths
|
@@ -205,16 +210,14 @@ module NetworkX
|
|
205
210
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
206
211
|
#
|
207
212
|
# @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths between all nodes
|
208
|
-
def self.all_pairs_dijkstra_path(graph, cutoff=nil)
|
213
|
+
def self.all_pairs_dijkstra_path(graph, cutoff = nil)
|
209
214
|
paths = []
|
210
215
|
graph.nodes.each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
|
211
216
|
paths
|
212
217
|
end
|
213
218
|
|
214
|
-
# TODO: Reduce method length and method complexity
|
215
|
-
|
216
219
|
# Helper function for bellman ford
|
217
|
-
def self.help_bellman_ford(graph, sources, weight, pred=nil, paths=nil, dist=nil, cutoff=nil, target=nil)
|
220
|
+
def self.help_bellman_ford(graph, sources, weight, pred = nil, paths = nil, dist = nil, cutoff = nil, target = nil)
|
218
221
|
pred = sources.product([[]]).to_h if pred.nil?
|
219
222
|
dist = sources.product([0]).to_h if dist.nil?
|
220
223
|
|
@@ -225,28 +228,31 @@ module NetworkX
|
|
225
228
|
skip = false
|
226
229
|
pred[u].each { |k| skip = true if in_q.include?(k) }
|
227
230
|
next if skip
|
231
|
+
|
228
232
|
dist_u = dist[u]
|
229
233
|
graph.adj[u].each do |v, e|
|
230
234
|
dist_v = dist_u + weight.call(u, v, e)
|
231
235
|
next if !cutoff.nil? && dist_v > cutoff
|
232
236
|
next if !target.nil? && dist_v > (dist[target] || inf)
|
237
|
+
|
233
238
|
if dist_v < (dist[v] || inf)
|
234
239
|
unless in_q.include?(v)
|
235
240
|
q << v
|
236
241
|
in_q.add(v)
|
237
242
|
count_v = (count[v] || 0) + 1
|
238
243
|
raise ArgumentError, 'Negative edge cycle detected!' if count_v == n
|
244
|
+
|
239
245
|
count[v] = count_v
|
240
246
|
end
|
241
247
|
dist[v] = dist_v
|
242
248
|
pred[v] = [u]
|
243
|
-
elsif dist.
|
249
|
+
elsif dist.has_key?(v) && dist_v == dist[v]
|
244
250
|
pred[v] << u
|
245
251
|
end
|
246
252
|
end
|
247
253
|
end
|
248
254
|
unless paths.nil?
|
249
|
-
dsts =
|
255
|
+
dsts = pred
|
250
256
|
dsts.each_key do |dst|
|
251
257
|
path, cur = [dst], dst
|
252
258
|
until pred[cur][0].nil?
|
@@ -268,24 +274,28 @@ module NetworkX
|
|
268
274
|
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
269
275
|
#
|
270
276
|
# @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predecessors and distances
|
271
|
-
def self.bellmanford_predecesor_distance(graph, source, target=nil, cutoff=nil)
|
277
|
+
def self.bellmanford_predecesor_distance(graph, source, target = nil, cutoff = nil)
|
272
278
|
raise ArgumentError, 'Node not found!' unless graph.node?(source)
|
279
|
+
|
273
280
|
weight = get_weight(graph)
|
274
281
|
# TODO: Detection of selfloop edges
|
275
282
|
dist = {source => 0}
|
276
283
|
pred = {source => []}
|
277
284
|
return [pred, dist] if graph.nodes.length == 1
|
285
|
+
|
278
286
|
dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target)
|
279
287
|
[pred, dist]
|
280
288
|
end
|
281
289
|
|
282
|
-
def self.singlesource_bellmanford(graph, source, target=nil, cutoff=nil)
|
290
|
+
def self.singlesource_bellmanford(graph, source, target = nil, cutoff = nil)
|
283
291
|
return [0, [source]] if source == target
|
292
|
+
|
284
293
|
weight = get_weight(graph)
|
285
294
|
paths = {source => [source]}
|
286
295
|
dist = help_bellman_ford(graph, [source], weight, nil, paths, nil, cutoff, target)
|
287
296
|
return [dist, paths] if target.nil?
|
288
|
-
raise ArgumentError, 'Node not reachable!' unless dist.
|
297
|
+
raise ArgumentError, 'Node not reachable!' unless dist.has_key?(target)
|
298
|
+
|
289
299
|
[dist[target], paths[target]]
|
290
300
|
end
|
291
301
|
|
@@ -298,9 +308,11 @@ module NetworkX
|
|
298
308
|
# @return [Numeric] distance between source and target
|
299
309
|
def self.bellmanford_path_length(graph, source, target)
|
300
310
|
return 0 if source == target
|
311
|
+
|
301
312
|
weight = get_weight(graph)
|
302
|
-
length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target
|
303
|
-
raise ArgumentError, 'Node not reachable!' unless length.
|
313
|
+
length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target)
|
314
|
+
raise ArgumentError, 'Node not reachable!' unless length.has_key?(target)
|
315
|
+
|
304
316
|
length[target]
|
305
317
|
end
|
306
318
|
|
@@ -323,7 +335,7 @@ module NetworkX
|
|
323
335
|
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
324
336
|
#
|
325
337
|
# @return [Hash{ Object => Array<Object> }] path from source to all nodes
|
326
|
-
def self.singlesource_bellmanford_path(graph, source, cutoff=nil)
|
338
|
+
def self.singlesource_bellmanford_path(graph, source, cutoff = nil)
|
327
339
|
_, path = singlesource_bellmanford(graph, source, cutoff)
|
328
340
|
path
|
329
341
|
end
|
@@ -335,7 +347,7 @@ module NetworkX
|
|
335
347
|
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
336
348
|
#
|
337
349
|
# @return [Hash{ Object => Numeric }] path lengths from source to all nodes
|
338
|
-
def self.singlesource_bellmanford_path_length(graph, source, cutoff=nil)
|
350
|
+
def self.singlesource_bellmanford_path_length(graph, source, cutoff = nil)
|
339
351
|
weight = get_weight(graph)
|
340
352
|
help_bellman_ford(graph, [source], weight, nil, nil, nil, cutoff)
|
341
353
|
end
|
@@ -346,7 +358,7 @@ module NetworkX
|
|
346
358
|
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
347
359
|
#
|
348
360
|
# @return [Array<Object, Hash{ Object => Numeric }>] path lengths from source to all nodes
|
349
|
-
def self.allpairs_bellmanford_path_length(graph, cutoff=nil)
|
361
|
+
def self.allpairs_bellmanford_path_length(graph, cutoff = nil)
|
350
362
|
path_lengths = []
|
351
363
|
graph.nodes.each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
|
352
364
|
path_lengths
|
@@ -358,7 +370,7 @@ module NetworkX
|
|
358
370
|
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
359
371
|
#
|
360
372
|
# @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths from source to all nodes
|
361
|
-
def self.allpairs_bellmanford_path(graph, cutoff=nil)
|
373
|
+
def self.allpairs_bellmanford_path(graph, cutoff = nil)
|
362
374
|
paths = []
|
363
375
|
graph.nodes.each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
|
364
376
|
paths
|
@@ -385,8 +397,6 @@ module NetworkX
|
|
385
397
|
path_lengths
|
386
398
|
end
|
387
399
|
|
388
|
-
# TODO: Reduce method length and method complexity
|
389
|
-
|
390
400
|
# Returns shortest path between all pairs of nodes
|
391
401
|
#
|
392
402
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
@@ -399,10 +409,9 @@ module NetworkX
|
|
399
409
|
dist[n], pred[n] = 0, []
|
400
410
|
end
|
401
411
|
weight = get_weight(graph)
|
402
|
-
dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist
|
412
|
+
dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist)
|
403
413
|
new_weight = ->(u, v, d) { weight.call(u, v, d) + dist_bellman[u] - dist_bellman[v] }
|
404
414
|
dist_path = dist_path_lambda(graph, new_weight)
|
405
|
-
|
406
|
-
path_lengths
|
415
|
+
set_path_lengths_johnson(graph, dist_path, new_weight)
|
407
416
|
end
|
408
417
|
end
|
data/lib/networkx/to_matrix.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
module NetworkX
|
2
|
-
|
3
|
-
def self.to_matrix(graph, val, multigraph_weight='sum')
|
2
|
+
def self.to_matrix(graph, val, multigraph_weight = 'sum')
|
4
3
|
is_undirected = !graph.directed?
|
5
4
|
is_multigraph = graph.multigraph?
|
6
5
|
nodelen = graph.nodes.length
|
7
6
|
|
8
|
-
m =
|
7
|
+
m = Matrix.build(nodelen) { val }
|
9
8
|
index = {}
|
10
9
|
inv_index = {}
|
11
10
|
ind = 0
|
@@ -1,6 +1,4 @@
|
|
1
1
|
module NetworkX
|
2
|
-
# TODO: Reduce method length
|
3
|
-
|
4
2
|
# Returns edges of the graph travelled in breadth first fashion
|
5
3
|
#
|
6
4
|
# @example
|
@@ -10,6 +8,7 @@ module NetworkX
|
|
10
8
|
# @param source [Object] node to start bfs from
|
11
9
|
def self.bfs_edges(graph, source)
|
12
10
|
raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source)
|
11
|
+
|
13
12
|
bfs_edges = []
|
14
13
|
visited = [source]
|
15
14
|
queue = Queue.new.push([source, graph.neighbours(source)])
|
@@ -17,6 +16,7 @@ module NetworkX
|
|
17
16
|
parent, children = queue.pop
|
18
17
|
children.each_key do |child|
|
19
18
|
next if visited.include?(child)
|
19
|
+
|
20
20
|
bfs_edges << [parent, child]
|
21
21
|
visited << child
|
22
22
|
queue.push([child, graph.neighbours(child)])
|
@@ -55,4 +55,56 @@ module NetworkX
|
|
55
55
|
bfs_edges.each { |u, v| predecessors[v] = u }
|
56
56
|
predecessors
|
57
57
|
end
|
58
|
+
|
59
|
+
class Graph
|
60
|
+
def bfs_nodes(root)
|
61
|
+
each_bfs_node(root).to_a
|
62
|
+
end
|
63
|
+
|
64
|
+
def each_bfs_node(root)
|
65
|
+
return enum_for(:each_bfs_node, root) unless block_given?
|
66
|
+
|
67
|
+
queue = [root]
|
68
|
+
dist = {root => 0}
|
69
|
+
while (v = queue.shift)
|
70
|
+
yield v
|
71
|
+
d = dist[v]
|
72
|
+
@adj[v].each do |u, _data|
|
73
|
+
next if dist[u]
|
74
|
+
|
75
|
+
dist[u] = d + 1
|
76
|
+
queue << u
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# [EXPERIMENTAL]
|
82
|
+
#
|
83
|
+
# @param [Object] node which is root, start ,source
|
84
|
+
def bfs_edges(node)
|
85
|
+
each_bfs_edge(node).to_a
|
86
|
+
end
|
87
|
+
|
88
|
+
# [EXPERIMENTAL]
|
89
|
+
#
|
90
|
+
# @param [Object] node which is root, start ,source
|
91
|
+
def each_bfs_edge(node)
|
92
|
+
return enum_for(:each_bfs_edge, node) unless block_given?
|
93
|
+
|
94
|
+
que = [node]
|
95
|
+
used = {node => true}
|
96
|
+
while que[0]
|
97
|
+
node = que.shift
|
98
|
+
|
99
|
+
@adj[node].each do |v, _data|
|
100
|
+
next if used[v]
|
101
|
+
|
102
|
+
used[v] = true
|
103
|
+
|
104
|
+
yield(node, v)
|
105
|
+
que << v
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
58
110
|
end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
module NetworkX
|
2
|
-
# TODO: Reduce method complexity and method length
|
3
|
-
|
4
2
|
# Returns edges of the graph travelled in depth first fashion
|
5
3
|
#
|
6
4
|
# @example
|
@@ -9,8 +7,10 @@ module NetworkX
|
|
9
7
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
10
8
|
# @param source [Object] node to start dfs from
|
11
9
|
# @param depth_limit [Integer, nil] the depth limit of dfs
|
12
|
-
def self.dfs_edges(graph, source, depth_limit=nil)
|
10
|
+
def self.dfs_edges(graph, source, depth_limit = nil)
|
13
11
|
raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source)
|
12
|
+
|
13
|
+
depth_limit += 1 if depth_limit
|
14
14
|
depth_limit = graph.nodes.length if depth_limit.nil?
|
15
15
|
dfs_edges = []
|
16
16
|
visited = [source]
|
@@ -37,7 +37,7 @@ module NetworkX
|
|
37
37
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
38
38
|
# @param source [Object] node to start dfs from
|
39
39
|
# @param depth_limit [Integer, nil] the depth limit of dfs
|
40
|
-
def self.dfs_tree(graph, source, depth_limit=nil)
|
40
|
+
def self.dfs_tree(graph, source, depth_limit = nil)
|
41
41
|
t = NetworkX::DiGraph.new
|
42
42
|
t.add_node(source)
|
43
43
|
t.add_edges_from(dfs_edges(graph, source, depth_limit))
|
@@ -52,7 +52,7 @@ module NetworkX
|
|
52
52
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
53
53
|
# @param source [Object] node to start dfs from
|
54
54
|
# @param depth_limit [Integer, nil] the depth limit of dfs
|
55
|
-
def self.dfs_successors(graph, source, depth_limit=nil)
|
55
|
+
def self.dfs_successors(graph, source, depth_limit = nil)
|
56
56
|
dfs_edges = dfs_edges(graph, source, depth_limit)
|
57
57
|
successors = {}
|
58
58
|
dfs_edges.each do |u, v|
|
@@ -70,10 +70,66 @@ module NetworkX
|
|
70
70
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
71
71
|
# @param source [Object] node to start dfs from
|
72
72
|
# @param depth_limit [Integer, nil] the depth limit of dfs
|
73
|
-
def self.dfs_predecessors(graph, source, depth_limit=nil)
|
73
|
+
def self.dfs_predecessors(graph, source, depth_limit = nil)
|
74
74
|
dfs_edges = dfs_edges(graph, source, depth_limit)
|
75
75
|
predecessors = {}
|
76
76
|
dfs_edges.each { |u, v| predecessors[v] = u }
|
77
77
|
predecessors
|
78
78
|
end
|
79
|
+
|
80
|
+
class Graph
|
81
|
+
# [EXPERIMENTAL]
|
82
|
+
#
|
83
|
+
# @param root [Object] node which is root, start, source
|
84
|
+
#
|
85
|
+
# @return [Array[Object]] nodes
|
86
|
+
def dfs_preorder_nodes(root)
|
87
|
+
each_dfs_preorder_node(root).to_a
|
88
|
+
end
|
89
|
+
|
90
|
+
# [EXPERIMENTAL]
|
91
|
+
#
|
92
|
+
# @param root [Object] node which is root, start, source
|
93
|
+
def each_dfs_preorder_node(root)
|
94
|
+
return enum_for(:each_dfs_preorder_node, root) unless block_given?
|
95
|
+
|
96
|
+
st = [root]
|
97
|
+
used = {root => true}
|
98
|
+
while st[-1]
|
99
|
+
node = st.pop
|
100
|
+
yield(node)
|
101
|
+
@adj[node].reverse_each do |v, _data|
|
102
|
+
next if used[v]
|
103
|
+
|
104
|
+
used[v] = node
|
105
|
+
st << v
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# [EXPERIMENTAL]
|
111
|
+
#
|
112
|
+
# @param root [Object] node which is root, start, source
|
113
|
+
#
|
114
|
+
# @return [Array[Object]] array of dfs postorder nodes
|
115
|
+
def dfs_postorder_nodes(root, used = {root => true})
|
116
|
+
res = []
|
117
|
+
@adj[root].each do |v, _data|
|
118
|
+
next if used[v]
|
119
|
+
|
120
|
+
used[v] = true
|
121
|
+
res.concat dfs_postorder_nodes(v, used)
|
122
|
+
end
|
123
|
+
|
124
|
+
res << root
|
125
|
+
res
|
126
|
+
end
|
127
|
+
|
128
|
+
# @param root [Object] node which is root, start, source
|
129
|
+
def each_dfs_postorder_node(root, &block)
|
130
|
+
return enum_for(:each_dfs_postorder_node, root) unless block_given?
|
131
|
+
|
132
|
+
dfs_postorder_nodes(root).each(&block)
|
133
|
+
end
|
134
|
+
end
|
79
135
|
end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
module NetworkX
|
2
|
-
# TODO: Reduce method complexity and method length
|
3
|
-
|
4
2
|
# Helper function for edge_dfs
|
5
3
|
#
|
6
4
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
@@ -11,7 +9,7 @@ module NetworkX
|
|
11
9
|
case graph.class.name
|
12
10
|
when 'NetworkX::Graph', 'NetworkX::DiGraph'
|
13
11
|
graph.adj[node].each do |v, _|
|
14
|
-
if graph.
|
12
|
+
if graph.instance_of?(::NetworkX::DiGraph) || visited[[v, node]].nil?
|
15
13
|
visited[[node, v]] = true
|
16
14
|
edges << [node, v]
|
17
15
|
end
|
@@ -19,7 +17,7 @@ module NetworkX
|
|
19
17
|
else
|
20
18
|
graph.adj[node].each do |v, uv_keys|
|
21
19
|
uv_keys.each_key do |k|
|
22
|
-
if graph.
|
20
|
+
if graph.instance_of?(::NetworkX::MultiDiGraph) || visited[[v, node, k]].nil?
|
23
21
|
visited[[node, v, k]] = true
|
24
22
|
edges << [node, v, k]
|
25
23
|
end
|
@@ -32,12 +30,11 @@ module NetworkX
|
|
32
30
|
# Helper function of edge_dfs
|
33
31
|
def self.edge_id(graph, edge)
|
34
32
|
return edge if graph.directed?
|
35
|
-
return Set.new([edge, (edge[0..1].reverse
|
33
|
+
return Set.new([edge, (edge[0..1].reverse << edge[2])]) if graph.multigraph?
|
34
|
+
|
36
35
|
Set.new([edge, edge.reverse])
|
37
36
|
end
|
38
37
|
|
39
|
-
# TODO: Reduce method complexity and method length
|
40
|
-
|
41
38
|
# Performs edge dfs on the graph
|
42
39
|
# Orientation :ignore, directed edges can be
|
43
40
|
# travelled in both fashions
|
@@ -49,15 +46,15 @@ module NetworkX
|
|
49
46
|
# NetworkX.edge_dfs(graph, source, 'ignore')
|
50
47
|
#
|
51
48
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
52
|
-
# @param
|
49
|
+
# @param start [Object] node to start dfs from
|
53
50
|
# @param orientation [:ignore, :reverse', nil] the orientation of edges of graph
|
54
|
-
def self.edge_dfs(graph, start, orientation=nil)
|
51
|
+
def self.edge_dfs(graph, start, orientation = nil)
|
55
52
|
case orientation
|
56
53
|
when :reverse
|
57
|
-
graph = graph.reverse if graph.
|
54
|
+
graph = graph.reverse if graph.instance_of?(::NetworkX::DiGraph) || graph.instance_of?(::NetworkX::MultiDiGraph)
|
58
55
|
when :ignore
|
59
|
-
graph = graph.to_undirected if graph.
|
60
|
-
graph = graph.to_multigraph if graph.
|
56
|
+
graph = graph.to_undirected if graph.instance_of?(::NetworkX::DiGraph)
|
57
|
+
graph = graph.to_multigraph if graph.instance_of?(::NetworkX::MultiDiGraph)
|
61
58
|
end
|
62
59
|
|
63
60
|
visited_edges = []
|
@@ -87,4 +84,31 @@ module NetworkX
|
|
87
84
|
end
|
88
85
|
e.take(graph.number_of_edges)
|
89
86
|
end
|
87
|
+
|
88
|
+
class Graph
|
89
|
+
def dfs_edges(node)
|
90
|
+
each_dfs_edge(node).to_a
|
91
|
+
end
|
92
|
+
|
93
|
+
def each_dfs_edge(node)
|
94
|
+
return enum_for(:each_dfs_edge, node) unless block_given?
|
95
|
+
|
96
|
+
st = [node]
|
97
|
+
used = {}
|
98
|
+
parents = {}
|
99
|
+
while st[-1]
|
100
|
+
node = st.pop
|
101
|
+
|
102
|
+
yield(parents[node], node) if parents[node]
|
103
|
+
|
104
|
+
used[node] = true
|
105
|
+
@adj[node].reverse_each do |v, _data|
|
106
|
+
next if used[v]
|
107
|
+
|
108
|
+
parents[v] = node
|
109
|
+
st << v unless used[v]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
90
114
|
end
|
data/lib/networkx/version.rb
CHANGED
data/lib/networkx.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
-
require '
|
2
|
+
require 'matrix'
|
3
3
|
require 'rb_heap'
|
4
4
|
require 'securerandom'
|
5
5
|
require_relative 'networkx/version'
|
@@ -35,3 +35,9 @@ require_relative 'networkx/auxillary_functions/wiener'
|
|
35
35
|
require_relative 'networkx/auxillary_functions/cycles'
|
36
36
|
require_relative 'networkx/link_analysis/hits'
|
37
37
|
require_relative 'networkx/link_analysis/pagerank'
|
38
|
+
require_relative 'networkx/others/bridges'
|
39
|
+
require_relative 'networkx/others/generators'
|
40
|
+
require_relative 'networkx/others/grid_2d_graph'
|
41
|
+
require_relative 'networkx/others/info'
|
42
|
+
require_relative 'networkx/others/number_connected_components'
|
43
|
+
require_relative 'networkx/others/reads'
|
data/networkx.gemspec
CHANGED
@@ -13,26 +13,29 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.email = ['athityakumar@gmail.com']
|
14
14
|
spec.summary = 'A Ruby implemenation of the well-known graph library called networkx.'
|
15
15
|
spec.description = NetworkX::DESCRIPTION
|
16
|
-
spec.homepage = 'https://github.com/
|
16
|
+
spec.homepage = 'https://github.com/SciRuby/networkx.rb'
|
17
17
|
spec.license = 'MIT'
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
19
|
spec.bindir = 'bin'
|
20
20
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ['lib']
|
22
22
|
|
23
|
-
spec.
|
24
|
-
|
25
|
-
spec.add_development_dependency '
|
23
|
+
spec.required_ruby_version = '>= 2.7'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
26
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
26
27
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
-
spec.add_development_dependency 'rspec-its'
|
28
|
-
spec.add_development_dependency 'rubocop', '
|
29
|
-
spec.add_development_dependency 'rubocop-
|
30
|
-
spec.add_development_dependency '
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency '
|
33
|
-
spec.add_development_dependency '
|
34
|
-
spec.
|
35
|
-
|
28
|
+
spec.add_development_dependency 'rspec-its', '~> 1.0'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 1.0'
|
30
|
+
spec.add_development_dependency 'rubocop-rake', '~> 0.6'
|
31
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.0'
|
32
|
+
spec.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
33
|
+
spec.add_development_dependency 'saharspec', '~> 0.0'
|
34
|
+
spec.add_development_dependency 'simplecov', '~> 0.21'
|
35
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
36
|
+
|
37
|
+
spec.add_runtime_dependency 'matrix', '~> 0.4'
|
38
|
+
spec.add_runtime_dependency 'rb_heap', '~> 1.0'
|
36
39
|
|
37
|
-
spec.
|
40
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
38
41
|
end
|