networkx 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +18 -11
  3. data/CONTRIBUTING.md +2 -2
  4. data/Guardfile +1 -1
  5. data/ISSUE_TEMPLATE.md +15 -0
  6. data/PULL_REQUEST_TEMPLATE.md +12 -0
  7. data/README.md +4 -4
  8. data/RELEASE_POLICY.md +20 -0
  9. data/lib/networkx.rb +37 -1
  10. data/lib/networkx/auxillary_functions/cliques.rb +65 -0
  11. data/lib/networkx/auxillary_functions/cycles.rb +104 -0
  12. data/lib/networkx/auxillary_functions/dag.rb +54 -0
  13. data/lib/networkx/auxillary_functions/eccentricity.rb +36 -0
  14. data/lib/networkx/auxillary_functions/mis.rb +23 -0
  15. data/lib/networkx/auxillary_functions/mst.rb +35 -0
  16. data/lib/networkx/auxillary_functions/union_find.rb +24 -0
  17. data/lib/networkx/auxillary_functions/vitality.rb +13 -0
  18. data/lib/networkx/auxillary_functions/wiener.rb +13 -0
  19. data/lib/networkx/converters/to_csv.rb +47 -0
  20. data/lib/networkx/converters/to_json.rb +39 -0
  21. data/lib/networkx/digraph.rb +228 -0
  22. data/lib/networkx/flow/capacityscaling.rb +255 -0
  23. data/lib/networkx/flow/edmondskarp.rb +113 -0
  24. data/lib/networkx/flow/preflowpush.rb +252 -0
  25. data/lib/networkx/flow/shortestaugmentingpath.rb +157 -0
  26. data/lib/networkx/flow/utils.rb +160 -0
  27. data/lib/networkx/graph.rb +341 -0
  28. data/lib/networkx/link_analysis/hits.rb +59 -0
  29. data/lib/networkx/link_analysis/pagerank.rb +47 -0
  30. data/lib/networkx/multidigraph.rb +240 -0
  31. data/lib/networkx/multigraph.rb +171 -0
  32. data/lib/networkx/operators/all.rb +61 -0
  33. data/lib/networkx/operators/binary.rb +244 -0
  34. data/lib/networkx/operators/product.rb +204 -0
  35. data/lib/networkx/operators/unary.rb +17 -0
  36. data/lib/networkx/shortest_path/astar.rb +71 -0
  37. data/lib/networkx/shortest_path/dense.rb +31 -0
  38. data/lib/networkx/shortest_path/unweighted.rb +139 -0
  39. data/lib/networkx/shortest_path/weighted.rb +408 -0
  40. data/lib/networkx/to_matrix.rb +52 -0
  41. data/lib/networkx/traversals/bfs.rb +58 -0
  42. data/lib/networkx/traversals/dfs.rb +79 -0
  43. data/lib/networkx/traversals/edge_dfs.rb +90 -0
  44. data/lib/networkx/version.rb +1 -1
  45. data/networkx.gemspec +4 -1
  46. metadata +70 -4
@@ -0,0 +1,17 @@
1
+ module NetworkX
2
+ # Performs the complement operation on the graph
3
+ #
4
+ # @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph
5
+ #
6
+ # @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the complement of the graph
7
+ def self.complement(graph)
8
+ result = Marshal.load(Marshal.dump(graph))
9
+ result.clear
10
+
11
+ result.add_nodes(graph.nodes.map { |u, attrs| [u, attrs] })
12
+ graph.adj.each do |u, u_edges|
13
+ graph.nodes.each { |v, attrs| result.add_edge(u, v, attrs) if !u_edges.key?(v) && u != v }
14
+ end
15
+ result
16
+ end
17
+ end
@@ -0,0 +1,71 @@
1
+ module NetworkX
2
+ # TODO: Reduce method complexity and method length
3
+
4
+ # Returns path using astar algorithm between source and target
5
+ #
6
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
7
+ # @param source [Object] a node to start astar from
8
+ # @param target [Object] a node to end astar
9
+ # @param heuristic [] a lambda to compute heuristic b/w two nodes
10
+ #
11
+ # @return [Array<Object>] an array of nodes forming a path between source
12
+ # and target
13
+ def self.astar_path(graph, source, target, heuristic=nil)
14
+ warn 'A* is not implemented for MultiGraph and MultiDiGraph'
15
+ raise ArgumentError, 'Either source or target is not in graph' unless graph.node?(source) && graph.node?(target)
16
+ count = ->(i) { i + 1 }
17
+ i = -1
18
+ heuristic ||= (->(_u, _v) { 0 })
19
+ heap = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) }
20
+ heap << [0, count.call(i), source, 0, nil]
21
+ enqueued, explored = {}, {}
22
+
23
+ until heap.empty?
24
+ _, _, curnode, dist, parent = heap.pop
25
+ if curnode == target
26
+ path = [curnode]
27
+ node = parent
28
+ until node.nil?
29
+ path << node
30
+ node = explored[node]
31
+ end
32
+ path.reverse
33
+ return path
34
+ end
35
+
36
+ next if explored.key?(curnode)
37
+
38
+ explored[curnode] = parent
39
+
40
+ graph.adj[curnode].each do |u, attrs|
41
+ next if explored.key?(u)
42
+ ncost = dist + (attrs[:weight] || 1)
43
+ if enqueued.key?(u)
44
+ qcost, = enqueued[u]
45
+ next if qcost <= ncost
46
+ else
47
+ h = heuristic.call(u, target)
48
+ enqueued[u] = ncost, h
49
+ heap << [ncost + h, count.call(i), u, ncost, curnode]
50
+ end
51
+ end
52
+ end
53
+ raise ArgumentError, 'Target not reachable!'
54
+ end
55
+
56
+ # Returns astar path length b/w source and target
57
+ #
58
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
59
+ # @param source [Object] a node to start astar from
60
+ # @param target [Object] a node to end astar
61
+ # @param heuristic [] a lambda to compute heuristic b/w two nodes
62
+ #
63
+ # @return [Numeric] the length of the path
64
+ def self.astar_path_length(graph, source, target, heuristic=nil)
65
+ raise ArgumentError, 'Either source or target is not in graph' unless graph.node?(source) && graph.node?(target)
66
+ path = astar_path(graph, source, target, heuristic)
67
+ path_length = 0
68
+ (1..(path.length - 1)).each { |i| path_length += (graph.adj[path[i - 1]][path[i]][:weight] || 1) }
69
+ path_length
70
+ end
71
+ end
@@ -0,0 +1,31 @@
1
+ module NetworkX
2
+ # TODO: Reduce method length and method complexity
3
+
4
+ # Returns the all pair distance between all the nodes
5
+ #
6
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
7
+ #
8
+ # @return [Hash{ Object => { Object => { Numeric }}}] a hash containing distances
9
+ # b/w all pairs of nodes
10
+ def self.floyd_warshall(graph)
11
+ a, index = to_matrix(graph, Float::INFINITY, 'min')
12
+ nodelen = graph.nodes.length
13
+ (0..(nodelen - 1)).each { |i| a[i, i] = 0 }
14
+ (0..(nodelen - 1)).each do |k|
15
+ (0..(nodelen - 1)).each do |i|
16
+ (0..(nodelen - 1)).each do |j|
17
+ a[i, j] = [a[i, j], a[i, k] + a[k, j]].min
18
+ end
19
+ end
20
+ end
21
+
22
+ as_hash = {}
23
+ (0..(nodelen - 1)).each do |i|
24
+ (0..(nodelen - 1)).each do |j|
25
+ as_hash[index[i]] = {} unless as_hash.key?(index[i])
26
+ as_hash[index[i]][index[j]] = a[i, j]
27
+ end
28
+ end
29
+ as_hash
30
+ end
31
+ end
@@ -0,0 +1,139 @@
1
+ module NetworkX
2
+ # TODO: Reduce method length
3
+
4
+ # Helper function for single source shortest path length
5
+ def self.help_single_shortest_path_length(adj, firstlevel, cutoff)
6
+ iterator = Enumerator.new do |e|
7
+ seen = {}
8
+ level = 0
9
+ nextlevel = firstlevel
10
+
11
+ while !nextlevel.empty? && cutoff >= level
12
+ thislevel = nextlevel
13
+ nextlevel = {}
14
+ thislevel.each do |u, _attrs|
15
+ next if seen.key?(u)
16
+ seen[u] = level
17
+ nextlevel.merge!(adj[u])
18
+ e.yield [u, level]
19
+ end
20
+ level += 1
21
+ end
22
+ seen.clear
23
+ end
24
+ iterator
25
+ end
26
+
27
+ # Computes shortest path values to all nodes from a given node
28
+ #
29
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
30
+ # @param source [Object] source to compute path length from
31
+ # @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
32
+ #
33
+ # @return [Array<Object, Numeric>] path lengths for all nodes
34
+ def self.single_source_shortest_path_length(graph, source, cutoff=nil)
35
+ raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source)
36
+ cutoff = Float::INFINITY if cutoff.nil?
37
+ nextlevel = {source => 1}
38
+ help_single_shortest_path_length(graph.adj, nextlevel, cutoff).take(graph.nodes.length)
39
+ end
40
+
41
+ # Computes shortest path values to all nodes from all nodes
42
+ #
43
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
44
+ # @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
45
+ #
46
+ # @return [Array<Object, Array<Object, Numeric>>] path lengths for all nodes from all nodes
47
+ def self.all_pairs_shortest_path_length(graph, cutoff=nil)
48
+ shortest_paths = []
49
+ graph.nodes.each_key { |n| shortest_paths << [n, single_source_shortest_path_length(graph, n, cutoff)] }
50
+ shortest_paths
51
+ end
52
+
53
+ # TODO: Reduce method length
54
+
55
+ # Helper function for finding single source shortest path
56
+ def self.help_single_shortest_path(adj, firstlevel, paths, cutoff)
57
+ level = 0
58
+ nextlevel = firstlevel
59
+ while !nextlevel.empty? && cutoff > level
60
+ thislevel = nextlevel
61
+ nextlevel = {}
62
+ thislevel.each_key do |u|
63
+ adj[u].each_key do |v|
64
+ unless paths.key?(v)
65
+ paths[v] = paths[u] + [v]
66
+ nextlevel[v] = 1
67
+ end
68
+ end
69
+ end
70
+ level += 1
71
+ end
72
+ paths
73
+ end
74
+
75
+ # Computes single source shortest path from a node to every other node
76
+ #
77
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
78
+ # @param source [Object] source from which shortest paths are needed
79
+ # @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
80
+ #
81
+ # @return [Array<Object, Array<Object, Array<Object>>>] path lengths for all nodes from all nodes
82
+ def self.single_source_shortest_path(graph, source, cutoff=nil)
83
+ raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source)
84
+ cutoff = Float::INFINITY if cutoff.nil?
85
+ nextlevel = {source => 1}
86
+ paths = {source => [source]}
87
+ help_single_shortest_path(graph.adj, nextlevel, paths, cutoff)
88
+ end
89
+
90
+ # Computes shortest paths to all nodes from all nodes
91
+ #
92
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
93
+ # @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
94
+ #
95
+ # @return [Array<Object, Hash {Object => Array<Object> }>] paths for all nodes from all nodes
96
+ def self.all_pairs_shortest_path(graph, cutoff=nil)
97
+ shortest_paths = []
98
+ graph.nodes.each_key { |n| shortest_paths << [n, single_source_shortest_path(graph, n, cutoff)] }
99
+ shortest_paths
100
+ end
101
+
102
+ # TODO: Reduce method length and method complexity
103
+
104
+ # Computes shortest paths to all nodes from all nodes
105
+ #
106
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
107
+ # @param source [Object] source for which predecessors are needed
108
+ # @param cutoff [Numeric, nil] cutoff for finding predecessor
109
+ # @param return_seen [Boolean] if true, returns seen dict
110
+ #
111
+ # @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>,
112
+ # Hash{ Object => Array<Object> }]
113
+ # predecessors of a given node and/or seen dict
114
+ def self.predecessor(graph, source, cutoff=nil, return_seen=false)
115
+ raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source)
116
+ level = 0
117
+ nextlevel = [source]
118
+ seen = {source => level}
119
+ pred = {source => []}
120
+ until nextlevel.empty?
121
+ level += 1
122
+ thislevel = nextlevel
123
+ nextlevel = []
124
+ thislevel.each do |u|
125
+ graph.adj[u].each_key do |v|
126
+ if !seen.key?(v)
127
+ pred[v] = [u]
128
+ seen[v] = level
129
+ nextlevel << v
130
+ elsif seen[v] == level
131
+ pred[v] << u
132
+ end
133
+ end
134
+ end
135
+ break if cutoff.nil? && cutoff <= level
136
+ end
137
+ return_seen ? [pred, seen] : pred
138
+ end
139
+ end
@@ -0,0 +1,408 @@
1
+ # TODO: Reduce module length
2
+
3
+ module NetworkX
4
+ # Helper function to extract weight from a adjecency hash
5
+ def self.get_weight(graph)
6
+ weight_get = lambda do |_, _, attrs|
7
+ return attrs[:weight] || 1 unless graph.multigraph?
8
+ attrs.group_by { |_k, vals| vals[:weight] || 1 }.keys.max
9
+ end
10
+ weight_get
11
+ end
12
+
13
+ # TODO: Reduce method length and method complexity
14
+
15
+ # Helper function for multisource dijkstra
16
+ def self.help_multisource_dijkstra(graph, sources, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
17
+ count = ->(i) { i + 1 }
18
+ i = -1
19
+ dist = {}
20
+ seen = {}
21
+ fringe = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) }
22
+ sources.each do |s|
23
+ seen[s] = 0
24
+ fringe << [0, count.call(i), s]
25
+ end
26
+
27
+ until fringe.empty?
28
+ d, _, v = fringe.pop
29
+ next if dist.key?(v)
30
+ dist[v] = d
31
+ break if v == target
32
+ graph.adj[v].each do |u, attrs|
33
+ cost = weight.call(v, u, attrs)
34
+ next if cost.nil?
35
+ vu_dist = dist[v] + cost
36
+ next if !cutoff.nil? && vu_dist > cutoff
37
+ if dist.key?(u)
38
+ raise ValueError, 'Contradictory weights found!' if vu_dist < dist[u]
39
+ elsif !seen.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
+ paths = {}
69
+ weight = get_weight(graph)
70
+ sources.each { |source| paths[source] = [source] }
71
+ dist = help_multisource_dijkstra(graph, sources, weight, nil, paths, cutoff, target)
72
+ return [dist, paths] if target.nil?
73
+ raise KeyError, "No path to #{target}!" unless dist.key?(target)
74
+ [dist[target], paths[target]]
75
+ end
76
+
77
+ # Computes shortest path lengths to any from the given nodes
78
+ #
79
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
80
+ # @param sources [Array<Object>] Array of sources
81
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
82
+ #
83
+ # @return [Hash{ Object => Numeric }] path lengths for any nodes from given nodes
84
+ def self.multisource_dijkstra_path_length(graph, sources, cutoff=nil)
85
+ raise ValueError, 'Sources cannot be empty' if sources.empty?
86
+ weight = get_weight(graph)
87
+ help_multisource_dijkstra(graph, sources, weight, nil, nil, cutoff)
88
+ end
89
+
90
+ # Computes shortest paths to any from the given nodes
91
+ #
92
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
93
+ # @param sources [Array<Object>] Array of sources
94
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
95
+ #
96
+ # @return [Hash{ Object => Array<Object> }] paths for any nodes from given nodes
97
+ def self.multisource_dijkstra_path(graph, sources, cutoff=nil)
98
+ _, path = multisource_dijkstra(graph, sources, nil, cutoff)
99
+ path
100
+ end
101
+
102
+ # Computes shortest paths and path distances to all nodes/target from the given node
103
+ #
104
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
105
+ # @param source [Object] source
106
+ # @param target [Object] target
107
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
108
+ #
109
+ # @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)
111
+ multisource_dijkstra(graph, [source], target, cutoff)
112
+ end
113
+
114
+ # Computes shortest path lengths to all nodes from the given node
115
+ #
116
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
117
+ # @param source [Object] source
118
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
119
+ #
120
+ # @return [Hash{ Object => Numeric }] path lengths for all nodes from given node
121
+ def self.singlesource_dijkstra_path_length(graph, source, cutoff=nil)
122
+ multisource_dijkstra_path_length(graph, [source], cutoff)
123
+ end
124
+
125
+ # Computes shortest paths to all nodes from the given node
126
+ #
127
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
128
+ # @param source [Object] source
129
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
130
+ #
131
+ # @return [Hash{ Object => Array<Object> }] paths for all nodes from given node
132
+ def self.singlesource_dijkstra_path(graph, source, cutoff=nil)
133
+ multisource_dijkstra_path(graph, [source], cutoff)
134
+ end
135
+
136
+ # Computes shortest path length to target from the given node
137
+ #
138
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
139
+ # @param source [Object] source
140
+ # @param target [Object] target
141
+ #
142
+ # @return [Numeric] path length for target node from given node
143
+ def self.dijkstra_path_length(graph, source, target)
144
+ return 0 if source == target
145
+ weight = get_weight(graph)
146
+ length = help_dijkstra(graph, source, weight, nil, nil, nil, target)
147
+ raise KeyError, 'Node not reachable!' unless length.key?(target)
148
+ length[target]
149
+ end
150
+
151
+ # Computes shortest path to target from the given node
152
+ #
153
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
154
+ # @param source [Object] source
155
+ # @param target [Object] target
156
+ #
157
+ # @return [Numeric] path for target node from given node
158
+ def self.dijkstra_path(graph, source, target)
159
+ _, path = singlesource_dijkstra(graph, source, target)
160
+ path
161
+ end
162
+
163
+ # Computes weighted shortest path length and predecessors
164
+ #
165
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
166
+ # @param source [Object] source
167
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
168
+ #
169
+ # @return [<Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>]
170
+ # predcessor hash and distance hash
171
+ def self.dijkstra_predecessor_distance(graph, source, cutoff=nil)
172
+ weight = get_weight(graph)
173
+ pred = {source => []}
174
+ [pred, help_dijkstra(graph, source, weight, pred, nil, cutoff)]
175
+ end
176
+
177
+ # Finds shortest weighted paths and lengths between all nodes
178
+ #
179
+ # @param graph [Graph, DiGrhelp_dijkaph, MultiGraph, MultiDiGraph] a graph
180
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
181
+ #
182
+ # @return [Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>]
183
+ # paths and path lengths between all nodes
184
+ def self.all_pairs_dijkstra(graph, cutoff=nil)
185
+ path = []
186
+ graph.nodes.each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
187
+ path
188
+ end
189
+
190
+ # Finds shortest weighted path length between all nodes
191
+ #
192
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
193
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
194
+ #
195
+ # @return [Array<Object, Hash{ Object => Numeric }>] path lengths between all nodes
196
+ def self.all_pairs_dijkstra_path_length(graph, cutoff=nil)
197
+ path_lengths = []
198
+ graph.nodes.each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
199
+ path_lengths
200
+ end
201
+
202
+ # Finds shortest weighted paths between all nodes
203
+ #
204
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
205
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
206
+ #
207
+ # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths between all nodes
208
+ def self.all_pairs_dijkstra_path(graph, cutoff=nil)
209
+ paths = []
210
+ graph.nodes.each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
211
+ paths
212
+ end
213
+
214
+ # TODO: Reduce method length and method complexity
215
+
216
+ # Helper function for bellman ford
217
+ def self.help_bellman_ford(graph, sources, weight, pred=nil, paths=nil, dist=nil, cutoff=nil, target=nil)
218
+ pred = sources.product([[]]).to_h if pred.nil?
219
+ dist = sources.product([0]).to_h if dist.nil?
220
+
221
+ inf, n, count, q, in_q = Float::INFINITY, graph.nodes.length, {}, sources.clone, Set.new(sources)
222
+ until q.empty?
223
+ u = q.shift
224
+ in_q.delete(u)
225
+ skip = false
226
+ pred[u].each { |k| skip = true if in_q.include?(k) }
227
+ next if skip
228
+ dist_u = dist[u]
229
+ graph.adj[u].each do |v, e|
230
+ dist_v = dist_u + weight.call(u, v, e)
231
+ next if !cutoff.nil? && dist_v > cutoff
232
+ next if !target.nil? && dist_v > (dist[target] || inf)
233
+ if dist_v < (dist[v] || inf)
234
+ unless in_q.include?(v)
235
+ q << v
236
+ in_q.add(v)
237
+ count_v = (count[v] || 0) + 1
238
+ raise ArgumentError, 'Negative edge cycle detected!' if count_v == n
239
+ count[v] = count_v
240
+ end
241
+ dist[v] = dist_v
242
+ pred[v] = [u]
243
+ elsif dist.key?(v) && dist_v == dist[v]
244
+ pred[v] << u
245
+ end
246
+ end
247
+ end
248
+ unless paths.nil?
249
+ dsts = target.nil? ? pred : [target]
250
+ dsts.each_key do |dst|
251
+ path, cur = [dst], dst
252
+ until pred[cur][0].nil?
253
+ cur = pred[cur][0]
254
+ path << cur
255
+ end
256
+ path.reverse
257
+ paths[dst] = path
258
+ end
259
+ end
260
+ dist
261
+ end
262
+
263
+ # Finds shortest weighted path lengths and predecessors on shortest paths
264
+ #
265
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
266
+ # @param source [Object] source
267
+ # @param target [Object, nil] target
268
+ # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
269
+ #
270
+ # @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predecessors and distances
271
+ def self.bellmanford_predecesor_distance(graph, source, target=nil, cutoff=nil)
272
+ raise ArgumentError, 'Node not found!' unless graph.node?(source)
273
+ weight = get_weight(graph)
274
+ # TODO: Detection of selfloop edges
275
+ dist = {source => 0}
276
+ pred = {source => []}
277
+ return [pred, dist] if graph.nodes.length == 1
278
+ dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target)
279
+ [pred, dist]
280
+ end
281
+
282
+ def self.singlesource_bellmanford(graph, source, target=nil, cutoff=nil)
283
+ return [0, [source]] if source == target
284
+ weight = get_weight(graph)
285
+ paths = {source => [source]}
286
+ dist = help_bellman_ford(graph, [source], weight, nil, paths, nil, cutoff, target)
287
+ return [dist, paths] if target.nil?
288
+ raise ArgumentError, 'Node not reachable!' unless dist.key?(target)
289
+ [dist[target], paths[target]]
290
+ end
291
+
292
+ # Length of shortest path from source to target using Bellman Ford algorithm
293
+ #
294
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
295
+ # @param source [Object] source
296
+ # @param target [Object] target
297
+ #
298
+ # @return [Numeric] distance between source and target
299
+ def self.bellmanford_path_length(graph, source, target)
300
+ return 0 if source == target
301
+ weight = get_weight(graph)
302
+ length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target=target)
303
+ raise ArgumentError, 'Node not reachable!' unless length.key?(target)
304
+ length[target]
305
+ end
306
+
307
+ # Shortest path from source to target using Bellman Ford algorithm
308
+ #
309
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
310
+ # @param source [Object] source
311
+ # @param target [Object] target
312
+ #
313
+ # @return [Array<Object>] path from source to target
314
+ def self.bellmanford_path(graph, source, target)
315
+ _, path = singlesource_bellmanford(graph, source, target)
316
+ path
317
+ end
318
+
319
+ # Shortest path from source to all nodes using Bellman Ford algorithm
320
+ #
321
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
322
+ # @param source [Object] source
323
+ # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
324
+ #
325
+ # @return [Hash{ Object => Array<Object> }] path from source to all nodes
326
+ def self.singlesource_bellmanford_path(graph, source, cutoff=nil)
327
+ _, path = singlesource_bellmanford(graph, source, cutoff)
328
+ path
329
+ end
330
+
331
+ # Shortest path length 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 => Numeric }] path lengths from source to all nodes
338
+ def self.singlesource_bellmanford_path_length(graph, source, cutoff=nil)
339
+ weight = get_weight(graph)
340
+ help_bellman_ford(graph, [source], weight, nil, nil, nil, cutoff)
341
+ end
342
+
343
+ # Shortest path lengths between all nodes using Bellman Ford algorithm
344
+ #
345
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
346
+ # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
347
+ #
348
+ # @return [Array<Object, Hash{ Object => Numeric }>] path lengths from source to all nodes
349
+ def self.allpairs_bellmanford_path_length(graph, cutoff=nil)
350
+ path_lengths = []
351
+ graph.nodes.each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
352
+ path_lengths
353
+ end
354
+
355
+ # Shortest paths 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 => Array<Object> }>] path lengths from source to all nodes
361
+ def self.allpairs_bellmanford_path(graph, cutoff=nil)
362
+ paths = []
363
+ graph.nodes.each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
364
+ paths
365
+ end
366
+
367
+ # Helper function to get sources
368
+ def self.get_sources(graph)
369
+ graph.nodes.collect { |k, _v| k }
370
+ end
371
+
372
+ # Helper function to get distances
373
+ def self.dist_path_lambda(_graph, _new_weight)
374
+ lambda do |graph, v, new_weight|
375
+ paths = {v => [v]}
376
+ _ = help_dijkstra(graph, v, new_weight, nil, paths)
377
+ paths
378
+ end
379
+ end
380
+
381
+ # Helper function to set path lengths for Johnson algorithm
382
+ def self.set_path_lengths_johnson(graph, dist_path, new_weight)
383
+ path_lengths = []
384
+ graph.nodes.each_key { |n| path_lengths << [n, dist_path.call(graph, n, new_weight)] }
385
+ path_lengths
386
+ end
387
+
388
+ # TODO: Reduce method length and method complexity
389
+
390
+ # Returns shortest path between all pairs of nodes
391
+ #
392
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
393
+ #
394
+ # @return [Array<Object, Hash { Object => Array<Object> }] shortest paths between all pairs of nodes
395
+ def self.johnson(graph)
396
+ dist, pred = {}, {}
397
+ sources = get_sources(graph)
398
+ graph.nodes.each_key do |n|
399
+ dist[n], pred[n] = 0, []
400
+ end
401
+ weight = get_weight(graph)
402
+ dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist=dist)
403
+ new_weight = ->(u, v, d) { weight.call(u, v, d) + dist_bellman[u] - dist_bellman[v] }
404
+ dist_path = dist_path_lambda(graph, new_weight)
405
+ path_lengths = set_path_lengths_johnson(graph, dist_path, new_weight)
406
+ path_lengths
407
+ end
408
+ end