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.
- checksums.yaml +5 -5
- data/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
- data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +21 -11
- data/.github/ISSUE_TEMPLATE.md +15 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +10 -0
- data/.github/workflows/ci.yml +17 -0
- data/.github/workflows/doc.yml +23 -0
- data/.github/workflows/gem-push.yml +45 -0
- data/.rspec +0 -1
- data/.rubocop.yml +56 -63
- data/.yardopts +0 -1
- data/README.md +27 -27
- data/Rakefile +2 -3
- data/lib/networkx/auxillary_functions/cliques.rb +62 -0
- data/lib/networkx/auxillary_functions/cycles.rb +114 -0
- data/lib/networkx/auxillary_functions/dag.rb +59 -0
- data/lib/networkx/auxillary_functions/eccentricity.rb +37 -0
- data/lib/networkx/auxillary_functions/mis.rb +23 -0
- data/lib/networkx/auxillary_functions/mst.rb +33 -0
- data/lib/networkx/auxillary_functions/union_find.rb +104 -0
- data/lib/networkx/auxillary_functions/vitality.rb +13 -0
- data/lib/networkx/auxillary_functions/wiener.rb +13 -0
- data/lib/networkx/converters/to_csv.rb +45 -0
- data/lib/networkx/converters/to_json.rb +37 -0
- data/lib/networkx/digraph.rb +234 -0
- data/lib/networkx/flow/capacityscaling.rb +249 -0
- data/lib/networkx/flow/edmondskarp.rb +115 -0
- data/lib/networkx/flow/preflowpush.rb +249 -0
- data/lib/networkx/flow/shortestaugmentingpath.rb +154 -0
- data/lib/networkx/flow/utils.rb +139 -0
- data/lib/networkx/graph.rb +448 -0
- data/lib/networkx/link_analysis/hits.rb +59 -0
- data/lib/networkx/link_analysis/pagerank.rb +89 -0
- data/lib/networkx/multidigraph.rb +249 -0
- data/lib/networkx/multigraph.rb +199 -0
- data/lib/networkx/operators/all.rb +65 -0
- data/lib/networkx/operators/binary.rb +222 -0
- data/lib/networkx/operators/product.rb +201 -0
- data/lib/networkx/operators/unary.rb +17 -0
- 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 +73 -0
- data/lib/networkx/shortest_path/dense.rb +29 -0
- data/lib/networkx/shortest_path/unweighted.rb +136 -0
- data/lib/networkx/shortest_path/weighted.rb +417 -0
- data/lib/networkx/to_matrix.rb +51 -0
- data/lib/networkx/traversals/bfs.rb +110 -0
- data/lib/networkx/traversals/dfs.rb +135 -0
- data/lib/networkx/traversals/edge_dfs.rb +114 -0
- data/lib/networkx/version.rb +1 -1
- data/lib/networkx.rb +43 -1
- data/networkx.gemspec +14 -12
- metadata +118 -62
- data/.rspec_formatter.rb +0 -24
- data/.travis.yml +0 -18
- 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
|