networkx 0.1.0 → 0.2.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.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +21 -11
  4. data/.github/ISSUE_TEMPLATE.md +15 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  6. data/.github/workflows/ci.yml +17 -0
  7. data/.github/workflows/doc.yml +23 -0
  8. data/.github/workflows/gem-push.yml +45 -0
  9. data/.rspec +0 -1
  10. data/.rubocop.yml +56 -63
  11. data/.yardopts +0 -1
  12. data/README.md +27 -27
  13. data/Rakefile +2 -3
  14. data/lib/networkx/auxillary_functions/cliques.rb +62 -0
  15. data/lib/networkx/auxillary_functions/cycles.rb +114 -0
  16. data/lib/networkx/auxillary_functions/dag.rb +59 -0
  17. data/lib/networkx/auxillary_functions/eccentricity.rb +37 -0
  18. data/lib/networkx/auxillary_functions/mis.rb +23 -0
  19. data/lib/networkx/auxillary_functions/mst.rb +33 -0
  20. data/lib/networkx/auxillary_functions/union_find.rb +104 -0
  21. data/lib/networkx/auxillary_functions/vitality.rb +13 -0
  22. data/lib/networkx/auxillary_functions/wiener.rb +13 -0
  23. data/lib/networkx/converters/to_csv.rb +45 -0
  24. data/lib/networkx/converters/to_json.rb +37 -0
  25. data/lib/networkx/digraph.rb +234 -0
  26. data/lib/networkx/flow/capacityscaling.rb +249 -0
  27. data/lib/networkx/flow/edmondskarp.rb +115 -0
  28. data/lib/networkx/flow/preflowpush.rb +249 -0
  29. data/lib/networkx/flow/shortestaugmentingpath.rb +154 -0
  30. data/lib/networkx/flow/utils.rb +139 -0
  31. data/lib/networkx/graph.rb +448 -0
  32. data/lib/networkx/link_analysis/hits.rb +59 -0
  33. data/lib/networkx/link_analysis/pagerank.rb +89 -0
  34. data/lib/networkx/multidigraph.rb +249 -0
  35. data/lib/networkx/multigraph.rb +199 -0
  36. data/lib/networkx/operators/all.rb +65 -0
  37. data/lib/networkx/operators/binary.rb +222 -0
  38. data/lib/networkx/operators/product.rb +201 -0
  39. data/lib/networkx/operators/unary.rb +17 -0
  40. data/lib/networkx/others/bridges.rb +30 -0
  41. data/lib/networkx/others/generators.rb +237 -0
  42. data/lib/networkx/others/grid_2d_graph.rb +38 -0
  43. data/lib/networkx/others/info.rb +11 -0
  44. data/lib/networkx/others/number_connected_components.rb +17 -0
  45. data/lib/networkx/others/reads.rb +52 -0
  46. data/lib/networkx/shortest_path/astar.rb +73 -0
  47. data/lib/networkx/shortest_path/dense.rb +29 -0
  48. data/lib/networkx/shortest_path/unweighted.rb +136 -0
  49. data/lib/networkx/shortest_path/weighted.rb +417 -0
  50. data/lib/networkx/to_matrix.rb +51 -0
  51. data/lib/networkx/traversals/bfs.rb +110 -0
  52. data/lib/networkx/traversals/dfs.rb +135 -0
  53. data/lib/networkx/traversals/edge_dfs.rb +114 -0
  54. data/lib/networkx/version.rb +1 -1
  55. data/lib/networkx.rb +43 -1
  56. data/networkx.gemspec +14 -12
  57. metadata +118 -62
  58. data/.rspec_formatter.rb +0 -24
  59. data/.travis.yml +0 -18
  60. data/Guardfile +0 -7
@@ -0,0 +1,417 @@
1
+ module NetworkX
2
+ # Helper function to extract weight from a adjecency hash
3
+ def self.get_weight(graph)
4
+ lambda do |_, _, attrs|
5
+ return attrs[:weight] || 1 unless graph.multigraph?
6
+
7
+ attrs.group_by { |_k, vals| vals[:weight] || 1 }.keys.max
8
+ end
9
+ end
10
+
11
+ # Helper function for multisource dijkstra
12
+ def self.help_multisource_dijkstra(graph, sources, weight, pred = nil, paths = nil, cutoff = nil, target = nil)
13
+ count = ->(i) { i + 1 }
14
+ i = -1
15
+ dist = {}
16
+ seen = {}
17
+ fringe = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) }
18
+ sources.each do |s|
19
+ seen[s] = 0
20
+ fringe << [0, count.call(i), s]
21
+ end
22
+
23
+ until fringe.empty?
24
+ d, _, v = fringe.pop
25
+ next if dist.has_key?(v)
26
+
27
+ dist[v] = d
28
+ break if v == target
29
+
30
+ graph.adj[v].each do |u, attrs|
31
+ cost = weight.call(v, u, attrs)
32
+ next if cost.nil?
33
+
34
+ vu_dist = dist[v] + cost
35
+ next if !cutoff.nil? && vu_dist > cutoff
36
+
37
+ if dist.has_key?(u)
38
+ raise ValueError, 'Contradictory weights found!' if vu_dist < dist[u]
39
+ elsif !seen.has_key?(u) || vu_dist < seen[u]
40
+ seen[u] = vu_dist
41
+ fringe << [vu_dist, count.call(i), u]
42
+ paths[u] = paths[v] + [u] unless paths.nil?
43
+ pred[u] = [v] unless pred.nil?
44
+ elsif vu_dist == seen[u]
45
+ pred[u] << v unless pred.nil?
46
+ end
47
+ end
48
+ end
49
+ dist
50
+ end
51
+
52
+ # Helper function for single source dijkstra
53
+ def self.help_dijkstra(graph, source, weight, pred = nil, paths = nil, cutoff = nil, target = nil)
54
+ help_multisource_dijkstra(graph, [source], weight, pred, paths, cutoff, target)
55
+ end
56
+
57
+ # Computes shortest paths and path lengths to a target from one of the nodes
58
+ #
59
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
60
+ # @param sources [Array<Object>] Array of sources
61
+ # @param target [Object, nil] target node for the dijkstra algorithm
62
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
63
+ #
64
+ # @return [Numeric, Array<Object>] path lengths for all nodes
65
+ def self.multisource_dijkstra(graph, sources, target = nil, cutoff = nil)
66
+ raise ValueError, 'Sources cannot be empty' if sources.empty?
67
+ return [0, [target]] if sources.include?(target)
68
+
69
+ paths = {}
70
+ weight = get_weight(graph)
71
+ sources.each { |source| paths[source] = [source] }
72
+ dist = help_multisource_dijkstra(graph, sources, weight, nil, paths, cutoff, target)
73
+ return [dist, paths] if target.nil?
74
+ raise KeyError, "No path to #{target}!" unless dist.has_key?(target)
75
+
76
+ [dist[target], paths[target]]
77
+ end
78
+
79
+ # Computes shortest path lengths to any from the given nodes
80
+ #
81
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
82
+ # @param sources [Array<Object>] Array of sources
83
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
84
+ #
85
+ # @return [Hash{ Object => Numeric }] path lengths for any nodes from given nodes
86
+ def self.multisource_dijkstra_path_length(graph, sources, cutoff = nil)
87
+ raise ValueError, 'Sources cannot be empty' if sources.empty?
88
+
89
+ weight = get_weight(graph)
90
+ help_multisource_dijkstra(graph, sources, weight, nil, nil, cutoff)
91
+ end
92
+
93
+ # Computes shortest paths to any from the given nodes
94
+ #
95
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
96
+ # @param sources [Array<Object>] Array of sources
97
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
98
+ #
99
+ # @return [Hash{ Object => Array<Object> }] paths for any nodes from given nodes
100
+ def self.multisource_dijkstra_path(graph, sources, cutoff = nil)
101
+ _, path = multisource_dijkstra(graph, sources, nil, cutoff)
102
+ path
103
+ end
104
+
105
+ # Computes shortest paths and path distances to all nodes/target from the given node
106
+ #
107
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
108
+ # @param source [Object] source
109
+ # @param target [Object] target
110
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
111
+ #
112
+ # @return [Hash{ Object => Array<Object> }, Array<Object>] paths for all nodes/target node from given node
113
+ def self.singlesource_dijkstra(graph, source, target = nil, cutoff = nil)
114
+ multisource_dijkstra(graph, [source], target, cutoff)
115
+ end
116
+
117
+ # Computes shortest path lengths to all nodes from the given node
118
+ #
119
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
120
+ # @param source [Object] source
121
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
122
+ #
123
+ # @return [Hash{ Object => Numeric }] path lengths for all nodes from given node
124
+ def self.singlesource_dijkstra_path_length(graph, source, cutoff = nil)
125
+ multisource_dijkstra_path_length(graph, [source], cutoff)
126
+ end
127
+
128
+ # Computes shortest paths to all nodes from the given node
129
+ #
130
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
131
+ # @param source [Object] source
132
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
133
+ #
134
+ # @return [Hash{ Object => Array<Object> }] paths for all nodes from given node
135
+ def self.singlesource_dijkstra_path(graph, source, cutoff = nil)
136
+ multisource_dijkstra_path(graph, [source], cutoff)
137
+ end
138
+
139
+ # Computes shortest path length to target from the given node
140
+ #
141
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
142
+ # @param source [Object] source
143
+ # @param target [Object] target
144
+ #
145
+ # @return [Numeric] path length for target node from given node
146
+ def self.dijkstra_path_length(graph, source, target)
147
+ return 0 if source == target
148
+
149
+ weight = get_weight(graph)
150
+ length = help_dijkstra(graph, source, weight, nil, nil, nil, target)
151
+ raise KeyError, 'Node not reachable!' unless length.has_key?(target)
152
+
153
+ length[target]
154
+ end
155
+
156
+ # Computes shortest path to target from the given node
157
+ #
158
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
159
+ # @param source [Object] source
160
+ # @param target [Object] target
161
+ #
162
+ # @return [Numeric] path for target node from given node
163
+ def self.dijkstra_path(graph, source, target)
164
+ _, path = singlesource_dijkstra(graph, source, target)
165
+ path
166
+ end
167
+
168
+ # Computes weighted shortest path length and predecessors
169
+ #
170
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
171
+ # @param source [Object] source
172
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
173
+ #
174
+ # @return [<Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>]
175
+ # predcessor hash and distance hash
176
+ def self.dijkstra_predecessor_distance(graph, source, cutoff = nil)
177
+ weight = get_weight(graph)
178
+ pred = {source => []}
179
+ [pred, help_dijkstra(graph, source, weight, pred, nil, cutoff)]
180
+ end
181
+
182
+ # Finds shortest weighted paths and lengths between all nodes
183
+ #
184
+ # @param graph [Graph, DiGrhelp_dijkaph, MultiGraph, MultiDiGraph] a graph
185
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
186
+ #
187
+ # @return [Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>]
188
+ # paths and path lengths between all nodes
189
+ def self.all_pairs_dijkstra(graph, cutoff = nil)
190
+ path = []
191
+ graph.nodes.each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
192
+ path
193
+ end
194
+
195
+ # Finds shortest weighted path length between all nodes
196
+ #
197
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
198
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
199
+ #
200
+ # @return [Array<Object, Hash{ Object => Numeric }>] path lengths between all nodes
201
+ def self.all_pairs_dijkstra_path_length(graph, cutoff = nil)
202
+ path_lengths = []
203
+ graph.nodes.each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
204
+ path_lengths
205
+ end
206
+
207
+ # Finds shortest weighted paths between all nodes
208
+ #
209
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
210
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
211
+ #
212
+ # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths between all nodes
213
+ def self.all_pairs_dijkstra_path(graph, cutoff = nil)
214
+ paths = []
215
+ graph.nodes.each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
216
+ paths
217
+ end
218
+
219
+ # Helper function for bellman ford
220
+ def self.help_bellman_ford(graph, sources, weight, pred = nil, paths = nil, dist = nil, cutoff = nil, target = nil)
221
+ pred = sources.product([[]]).to_h if pred.nil?
222
+ dist = sources.product([0]).to_h if dist.nil?
223
+
224
+ inf, n, count, q, in_q = Float::INFINITY, graph.nodes.length, {}, sources.clone, Set.new(sources)
225
+ until q.empty?
226
+ u = q.shift
227
+ in_q.delete(u)
228
+ skip = false
229
+ pred[u].each { |k| skip = true if in_q.include?(k) }
230
+ next if skip
231
+
232
+ dist_u = dist[u]
233
+ graph.adj[u].each do |v, e|
234
+ dist_v = dist_u + weight.call(u, v, e)
235
+ next if !cutoff.nil? && dist_v > cutoff
236
+ next if !target.nil? && dist_v > (dist[target] || inf)
237
+
238
+ if dist_v < (dist[v] || inf)
239
+ unless in_q.include?(v)
240
+ q << v
241
+ in_q.add(v)
242
+ count_v = (count[v] || 0) + 1
243
+ raise ArgumentError, 'Negative edge cycle detected!' if count_v == n
244
+
245
+ count[v] = count_v
246
+ end
247
+ dist[v] = dist_v
248
+ pred[v] = [u]
249
+ elsif dist.has_key?(v) && dist_v == dist[v]
250
+ pred[v] << u
251
+ end
252
+ end
253
+ end
254
+ unless paths.nil?
255
+ dsts = pred
256
+ dsts.each_key do |dst|
257
+ path, cur = [dst], dst
258
+ until pred[cur][0].nil?
259
+ cur = pred[cur][0]
260
+ path << cur
261
+ end
262
+ path.reverse
263
+ paths[dst] = path
264
+ end
265
+ end
266
+ dist
267
+ end
268
+
269
+ # Finds shortest weighted path lengths and predecessors on shortest paths
270
+ #
271
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
272
+ # @param source [Object] source
273
+ # @param target [Object, nil] target
274
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
275
+ #
276
+ # @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predecessors and distances
277
+ def self.bellmanford_predecesor_distance(graph, source, target = nil, cutoff = nil)
278
+ raise ArgumentError, 'Node not found!' unless graph.node?(source)
279
+
280
+ weight = get_weight(graph)
281
+ # TODO: Detection of selfloop edges
282
+ dist = {source => 0}
283
+ pred = {source => []}
284
+ return [pred, dist] if graph.nodes.length == 1
285
+
286
+ dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target)
287
+ [pred, dist]
288
+ end
289
+
290
+ def self.singlesource_bellmanford(graph, source, target = nil, cutoff = nil)
291
+ return [0, [source]] if source == target
292
+
293
+ weight = get_weight(graph)
294
+ paths = {source => [source]}
295
+ dist = help_bellman_ford(graph, [source], weight, nil, paths, nil, cutoff, target)
296
+ return [dist, paths] if target.nil?
297
+ raise ArgumentError, 'Node not reachable!' unless dist.has_key?(target)
298
+
299
+ [dist[target], paths[target]]
300
+ end
301
+
302
+ # Length of shortest path from source to target using Bellman Ford algorithm
303
+ #
304
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
305
+ # @param source [Object] source
306
+ # @param target [Object] target
307
+ #
308
+ # @return [Numeric] distance between source and target
309
+ def self.bellmanford_path_length(graph, source, target)
310
+ return 0 if source == target
311
+
312
+ weight = get_weight(graph)
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
+
316
+ length[target]
317
+ end
318
+
319
+ # Shortest path from source to target using Bellman Ford algorithm
320
+ #
321
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
322
+ # @param source [Object] source
323
+ # @param target [Object] target
324
+ #
325
+ # @return [Array<Object>] path from source to target
326
+ def self.bellmanford_path(graph, source, target)
327
+ _, path = singlesource_bellmanford(graph, source, target)
328
+ path
329
+ end
330
+
331
+ # Shortest path from source to all nodes using Bellman Ford algorithm
332
+ #
333
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
334
+ # @param source [Object] source
335
+ # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
336
+ #
337
+ # @return [Hash{ Object => Array<Object> }] path from source to all nodes
338
+ def self.singlesource_bellmanford_path(graph, source, cutoff = nil)
339
+ _, path = singlesource_bellmanford(graph, source, cutoff)
340
+ path
341
+ end
342
+
343
+ # Shortest path length from source to all nodes using Bellman Ford algorithm
344
+ #
345
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
346
+ # @param source [Object] source
347
+ # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
348
+ #
349
+ # @return [Hash{ Object => Numeric }] path lengths from source to all nodes
350
+ def self.singlesource_bellmanford_path_length(graph, source, cutoff = nil)
351
+ weight = get_weight(graph)
352
+ help_bellman_ford(graph, [source], weight, nil, nil, nil, cutoff)
353
+ end
354
+
355
+ # Shortest path lengths between all nodes using Bellman Ford algorithm
356
+ #
357
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
358
+ # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
359
+ #
360
+ # @return [Array<Object, Hash{ Object => Numeric }>] path lengths from source to all nodes
361
+ def self.allpairs_bellmanford_path_length(graph, cutoff = nil)
362
+ path_lengths = []
363
+ graph.nodes.each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
364
+ path_lengths
365
+ end
366
+
367
+ # Shortest paths between all nodes using Bellman Ford algorithm
368
+ #
369
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
370
+ # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
371
+ #
372
+ # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths from source to all nodes
373
+ def self.allpairs_bellmanford_path(graph, cutoff = nil)
374
+ paths = []
375
+ graph.nodes.each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
376
+ paths
377
+ end
378
+
379
+ # Helper function to get sources
380
+ def self.get_sources(graph)
381
+ graph.nodes.collect { |k, _v| k }
382
+ end
383
+
384
+ # Helper function to get distances
385
+ def self.dist_path_lambda(_graph, _new_weight)
386
+ lambda do |graph, v, new_weight|
387
+ paths = {v => [v]}
388
+ _ = help_dijkstra(graph, v, new_weight, nil, paths)
389
+ paths
390
+ end
391
+ end
392
+
393
+ # Helper function to set path lengths for Johnson algorithm
394
+ def self.set_path_lengths_johnson(graph, dist_path, new_weight)
395
+ path_lengths = []
396
+ graph.nodes.each_key { |n| path_lengths << [n, dist_path.call(graph, n, new_weight)] }
397
+ path_lengths
398
+ end
399
+
400
+ # Returns shortest path between all pairs of nodes
401
+ #
402
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
403
+ #
404
+ # @return [Array<Object, Hash { Object => Array<Object> }] shortest paths between all pairs of nodes
405
+ def self.johnson(graph)
406
+ dist, pred = {}, {}
407
+ sources = get_sources(graph)
408
+ graph.nodes.each_key do |n|
409
+ dist[n], pred[n] = 0, []
410
+ end
411
+ weight = get_weight(graph)
412
+ dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist)
413
+ new_weight = ->(u, v, d) { weight.call(u, v, d) + dist_bellman[u] - dist_bellman[v] }
414
+ dist_path = dist_path_lambda(graph, new_weight)
415
+ set_path_lengths_johnson(graph, dist_path, new_weight)
416
+ end
417
+ end
@@ -0,0 +1,51 @@
1
+ module NetworkX
2
+ def self.to_matrix(graph, val, multigraph_weight = 'sum')
3
+ is_undirected = !graph.directed?
4
+ is_multigraph = graph.multigraph?
5
+ nodelen = graph.nodes.length
6
+
7
+ m = Matrix.build(nodelen) { val }
8
+ index = {}
9
+ inv_index = {}
10
+ ind = 0
11
+
12
+ graph.nodes.each do |u, _|
13
+ index[u] = ind
14
+ inv_index[ind] = u
15
+ ind += 1
16
+ end
17
+
18
+ if is_multigraph
19
+ graph.adj.each do |u, edge_hash|
20
+ edge_hash.each do |v, keys|
21
+ all_weights = []
22
+ keys.each do |_key, attrs|
23
+ all_weights << attrs[:weight]
24
+ end
25
+
26
+ edge_attr = 0
27
+
28
+ case multigraph_weight
29
+ when 'sum'
30
+ edge_attr = all_weights.inject(0, :+)
31
+ when 'max'
32
+ edge_attr = all_weights.max
33
+ when 'min'
34
+ edge_attr = all_weights.min
35
+ end
36
+
37
+ m[index[u], index[v]] = edge_attr
38
+ m[index[v], index[u]] = edge_attr || 1 if is_undirected
39
+ end
40
+ end
41
+ else
42
+ graph.adj.each do |u, edge_hash|
43
+ edge_hash.each do |v, attrs|
44
+ m[index[u], index[v]] = (attrs[:weight] || 1)
45
+ m[index[v], index[u]] = (attrs[:weight] || 1) if is_undirected
46
+ end
47
+ end
48
+ end
49
+ [m, inv_index]
50
+ end
51
+ end
@@ -0,0 +1,110 @@
1
+ module NetworkX
2
+ # Returns edges of the graph travelled in breadth first fashion
3
+ #
4
+ # @example
5
+ # NetworkX.bfs_edges(graph, source)
6
+ #
7
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
8
+ # @param source [Object] node to start bfs from
9
+ def self.bfs_edges(graph, source)
10
+ raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source)
11
+
12
+ bfs_edges = []
13
+ visited = [source]
14
+ queue = Queue.new.push([source, graph.neighbours(source)])
15
+ until queue.empty?
16
+ parent, children = queue.pop
17
+ children.each_key do |child|
18
+ next if visited.include?(child)
19
+
20
+ bfs_edges << [parent, child]
21
+ visited << child
22
+ queue.push([child, graph.neighbours(child)])
23
+ end
24
+ end
25
+ bfs_edges
26
+ end
27
+
28
+ # Returns parent successor pair of the graph travelled in breadth first fashion
29
+ #
30
+ # @example
31
+ # NetworkX.bfs_successors(graph, source)
32
+ #
33
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
34
+ # @param source [Object] node to start bfs from
35
+ def self.bfs_successors(graph, source)
36
+ bfs_edges = bfs_edges(graph, source)
37
+ successors = {}
38
+ bfs_edges.each do |u, v|
39
+ successors[u] = [] if successors[u].nil?
40
+ successors[u] << v
41
+ end
42
+ successors
43
+ end
44
+
45
+ # Returns predecessor child pair of the graph travelled in breadth first fashion
46
+ #
47
+ # @example
48
+ # NetworkX.bfs_predecessors(graph, source)
49
+ #
50
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
51
+ # @param source [Object] node to start bfs from
52
+ def self.bfs_predecessors(graph, source)
53
+ bfs_edges = bfs_edges(graph, source)
54
+ predecessors = {}
55
+ bfs_edges.each { |u, v| predecessors[v] = u }
56
+ predecessors
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
110
+ end
@@ -0,0 +1,135 @@
1
+ module NetworkX
2
+ # Returns edges of the graph travelled in depth first fashion
3
+ #
4
+ # @example
5
+ # NetworkX.dfs_edges(graph, source)
6
+ #
7
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
8
+ # @param source [Object] node to start dfs from
9
+ # @param depth_limit [Integer, nil] the depth limit of dfs
10
+ def self.dfs_edges(graph, source, depth_limit = nil)
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
+ depth_limit = graph.nodes.length if depth_limit.nil?
15
+ dfs_edges = []
16
+ visited = [source]
17
+ stack = [[-1, source, depth_limit, graph.neighbours(source)]]
18
+ until stack.empty?
19
+ earlier_node, parent, depth_now, children = stack.pop
20
+ dfs_edges << [earlier_node, parent]
21
+ children.each_key do |child|
22
+ unless visited.include?(child)
23
+ visited << child
24
+ stack.push([parent, child, depth_now - 1, graph.neighbours(child)]) if depth_now > 1
25
+ end
26
+ end
27
+ end
28
+ dfs_edges.shift
29
+ dfs_edges
30
+ end
31
+
32
+ # Returns dfs tree of the graph
33
+ #
34
+ # @example
35
+ # NetworkX.dfs_tree(graph, source)
36
+ #
37
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
38
+ # @param source [Object] node to start dfs from
39
+ # @param depth_limit [Integer, nil] the depth limit of dfs
40
+ def self.dfs_tree(graph, source, depth_limit = nil)
41
+ t = NetworkX::DiGraph.new
42
+ t.add_node(source)
43
+ t.add_edges_from(dfs_edges(graph, source, depth_limit))
44
+ t
45
+ end
46
+
47
+ # Returns parent successor pair of the graph travelled in depth first fashion
48
+ #
49
+ # @example
50
+ # NetworkX.dfs_successors(graph, source)
51
+ #
52
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
53
+ # @param source [Object] node to start dfs from
54
+ # @param depth_limit [Integer, nil] the depth limit of dfs
55
+ def self.dfs_successors(graph, source, depth_limit = nil)
56
+ dfs_edges = dfs_edges(graph, source, depth_limit)
57
+ successors = {}
58
+ dfs_edges.each do |u, v|
59
+ successors[u] = [] if successors[u].nil?
60
+ successors[u] << v
61
+ end
62
+ successors
63
+ end
64
+
65
+ # Returns predecessor child pair of the graph travelled in depth first fashion
66
+ #
67
+ # @example
68
+ # NetworkX.dfs_predecessors(graph, source)
69
+ #
70
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
71
+ # @param source [Object] node to start dfs from
72
+ # @param depth_limit [Integer, nil] the depth limit of dfs
73
+ def self.dfs_predecessors(graph, source, depth_limit = nil)
74
+ dfs_edges = dfs_edges(graph, source, depth_limit)
75
+ predecessors = {}
76
+ dfs_edges.each { |u, v| predecessors[v] = u }
77
+ predecessors
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
135
+ end