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,255 @@
1
+ # TODO: Reduce module length
2
+
3
+ module NetworkX
4
+ # Returns a label for unique node
5
+ def self.generate_unique_node
6
+ SecureRandom.uuid
7
+ end
8
+
9
+ # Finds if there is a negative edge cycle in the graph
10
+ #
11
+ # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
12
+ #
13
+ # @return [Boolean] whether there exists a negative cycle in graph
14
+ def self.negative_edge_cycle(graph)
15
+ newnode = generate_unique_node
16
+ graph.add_edges(graph.nodes.keys.map { |n| [newnode, n] })
17
+ begin
18
+ bellmanford_predecesor_distance(graph, newnode)
19
+ rescue ArgumentError
20
+ return true
21
+ ensure
22
+ graph.remove_node(newnode)
23
+ end
24
+ false
25
+ end
26
+
27
+ # TODO: Reduce method complexity and method length
28
+
29
+ # Detects the unboundedness in the residual graph
30
+ def self._detect_unboundedness(residual)
31
+ g = NetworkX::DiGraph.new
32
+ g.add_nodes(residual.nodes.keys.zip(residual.nodes.values))
33
+ inf = residual.graph[:inf]
34
+
35
+ residual.nodes.each do |u, _attr|
36
+ residual.adj[u].each do |v, uv_attrs|
37
+ w = inf
38
+ uv_attrs.each { |_key, edge_attrs| w = [w, edge_attrs[:weight]].min if edge_attrs[:capacity] == inf }
39
+ g.add_edge(u, v, weight: w) unless w == inf
40
+ end
41
+ end
42
+ raise ArgumentError, 'Negative cost cycle of infinite capacity found!' if negative_edge_cycle(g)
43
+ end
44
+
45
+ # TODO: Reduce method complexity and method length
46
+
47
+ # Returns the residual graph of the given graph
48
+ def self._build_residual_network(graph)
49
+ raise ArgumentError, 'Sum of demands should be 0!' unless\
50
+ graph.nodes.values.map { |attr| attr[:demand] || 0 }.inject(0, :+).zero?
51
+ residual = NetworkX::MultiDiGraph.new(inf: 0)
52
+ residual.add_nodes(graph.nodes.map { |u, attr| [u, excess: (attr[:demand] || 0) * -1, potential: 0] })
53
+ inf = Float::INFINITY
54
+ edge_list = []
55
+
56
+ # TODO: Selfloop edges check
57
+
58
+ if graph.multigraph?
59
+ graph.adj.each do |u, u_edges|
60
+ u_edges.each do |v, uv_edges|
61
+ uv_edges.each do |k, attrs|
62
+ edge_list << [u, v, k, e] if u != v && (attrs[:capacity] || inf) > 0
63
+ end
64
+ end
65
+ end
66
+ else
67
+ graph.adj.each do |u, u_edges|
68
+ u_edges.each do |v, attrs|
69
+ edge_list << [u, v, 0, attrs] if u != v && (attrs[:capacity] || inf) > 0
70
+ end
71
+ end
72
+ end
73
+
74
+ temp_inf = [residual.nodes.map { |_u, attrs| attrs[:excess].abs }.inject(0, :+), edge_list.map do |_, _, _, e|
75
+ (e.key?(:capacity) && e[:capacity] != inf ? e[:capacity] : 0)
76
+ end.inject(0, :+) * 2].max
77
+ inf = temp_inf.zero? ? 1 : temp_inf
78
+
79
+ edge_list.each do |u, v, k, e|
80
+ r = [e[:capacity] || inf, inf].min
81
+ w = e[:weight] || 0
82
+ residual.add_edge(u, v, temp_key: [k, true], capacity: r, weight: w, flow: 0)
83
+ residual.add_edge(v, u, temp_key: [k, false], capacity: 0, weight: -w, flow: 0)
84
+ end
85
+ residual.graph[:inf] = inf
86
+ _detect_unboundedness(residual)
87
+ residual
88
+ end
89
+
90
+ # TODO: Reduce method complexity and method length
91
+
92
+ # Returns the flowdict of the graph
93
+ def self._build_flow_dict(graph, residual)
94
+ flow_dict = {}
95
+ inf = Float::INFINITY
96
+
97
+ if graph.multigraph?
98
+ graph.nodes.each_key do |u|
99
+ flow_dict[u] = {}
100
+ graph.adj[u].each do |v, uv_edges|
101
+ flow_dict[u][v] = Hash[uv_edges.map do |k, e|
102
+ [k, u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity]]
103
+ end]
104
+ end
105
+ residual.adj[u].each do |v, uv_edges|
106
+ flow_dict[u][v].merge!(Hash[uv_edges.map { |_, val| [val[:temp_key][0], val[:flow]] if val[:flow] > 0 }])
107
+ end
108
+ end
109
+ else
110
+ graph.nodes.each_key do |u|
111
+ flow_dict[u] = Hash[graph.adj[u].map do |v, e|
112
+ [v, u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity]]
113
+ end]
114
+ merge_dict = {}
115
+ residual.adj[u].each do |v, uv_edges|
116
+ uv_edges.each_value { |attrs| merge_dict[v] = attrs[:flow] if attrs[:flow] > 0 }
117
+ end
118
+ flow_dict[u].merge!(merge_dict)
119
+ end
120
+ end
121
+ flow_dict
122
+ end
123
+
124
+ # Counter for the algorithm
125
+ @itr = 0
126
+ def self.count
127
+ @itr += 1
128
+ @itr
129
+ end
130
+
131
+ # TODO: Reduce method complexity and method length
132
+
133
+ # Computes max flow using capacity scaling algorithm
134
+ #
135
+ # @param graph [DiGraph, MultiDiGraph] a graph
136
+ #
137
+ # @return [Array<Numeric, Hash{ Object => Hash{ Object => Numeric } }>]
138
+ # flow cost and flowdict containing all the flow values in the edges
139
+ def self.capacity_scaling(graph)
140
+ residual = _build_residual_network(graph)
141
+ inf = Float::INFINITY
142
+ flow_cost = 0
143
+
144
+ # TODO: Account cost of self-loof edges
145
+
146
+ wmax = ([-inf] + residual.adj.each_with_object([]) do |u, arr|
147
+ u[1].each { |_, key_attrs| key_attrs.each { |_, attrs| arr << attrs[:capacity] } }
148
+ end).max
149
+
150
+ return flow_cost, _build_flow_dict(graph, residual) if wmax == -inf
151
+ r_nodes = residual.nodes
152
+ r_adj = residual.adj
153
+
154
+ delta = 2 ** Math.log2(wmax).floor
155
+ while delta >= 1
156
+ r_nodes.each do |u, u_attrs|
157
+ p_u = u_attrs[:potential]
158
+ r_adj[u].each do |v, uv_edges|
159
+ uv_edges.each do |_k, e|
160
+ flow = e[:capacity]
161
+ next unless e[:weight] - p_u + r_nodes[v][:potential] < 0
162
+ flow = e[:capacity] - e[:flow]
163
+ next unless flow >= delta
164
+ e[:flow] += flow
165
+ r_adj[v][u].each_key do |val|
166
+ val[:flow] += val[:temp_key][0] == e[:temp_key][0] && val[:temp_key][1] != e[:temp_key][1] ? -flow : 0
167
+ end
168
+ r_nodes[u][:excess] -= flow
169
+ r_nodes[v][:excess] += flow
170
+ end
171
+ end
172
+ end
173
+
174
+ s_set = Set.new
175
+ t_set = Set.new
176
+
177
+ residual.nodes.each do |u, _attrs|
178
+ excess = r_nodes[u][:excess]
179
+ if excess >= delta
180
+ s_set.add(u)
181
+ elsif excess <= -delta
182
+ t_set.add(u)
183
+ end
184
+ end
185
+
186
+ while !s_set.empty? && !t_set.empty?
187
+ s = arbitrary_element
188
+ t = nil
189
+ d = {}
190
+ pred = {s => nil}
191
+ h = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) }
192
+ h_dict = {s => 0}
193
+ h << [0, count, s]
194
+ until h.empty?
195
+ d_u, _, u = h.pop
196
+ h_dict.delete(u)
197
+ d[u] = d_u
198
+ if t_set.include?(u)
199
+ t = u
200
+ break
201
+ end
202
+ p_u = r_nodes[u][:potential]
203
+ r_adj[u].each do |v, uv_edges|
204
+ next if d.key?(v)
205
+ wmin = inf
206
+ uv_edges.each_value do |e|
207
+ next unless e[:capacity] - e[:flow] >= delta
208
+ w = e[:weight]
209
+ next unless w < wmin
210
+ wmin = w
211
+ end
212
+ next if wmin == inf
213
+ d_v = d_u + wmin - p_u + r_nodes[v][:potential]
214
+ next unless h_dict[v] > d_v
215
+ h << [d_v, count, v]
216
+ h_dict[v] = d_v
217
+ pred[v] = [u, kmin, emin]
218
+ end
219
+ end
220
+
221
+ if !t.nil?
222
+ while u != s
223
+ v = u
224
+ u, k, e = pred[v]
225
+ e[:flow] += delta
226
+ r_adj[v][u].each_key do |val|
227
+ val[:flow] += val[:temp_key][0] == k[0] && val[:temp_key][1] != k[1] ? -delta : 0
228
+ end
229
+ end
230
+ r_nodes[s][:excess] -= delta
231
+ r_nodes[t][:excess] += delta
232
+ s_set.delete(s) if r_nodes[s][:excess] < delta
233
+ t_set.delete(t) if r_nodes[t][:excess] > -delta
234
+ d_t = d[t]
235
+ d.each { |node, d_u_node| r_nodes[node][:potential] -= (d_u_node - d_t) }
236
+ else
237
+ s_set.delete(s)
238
+ end
239
+ end
240
+ delta = (delta / 2).floor
241
+ end
242
+
243
+ r_nodes.each_value { |attrs| raise ArgumentError, 'No flow satisfying all demands!' if attrs[:excess] != 0 }
244
+
245
+ residual.nodes.each_key do |node|
246
+ residual.adj[node].each_value do |uv_edges|
247
+ uv_edges.each_value do |k_attrs|
248
+ flow = k_attrs[:flow]
249
+ flow_cost += (flow * k_attrs[:weight])
250
+ end
251
+ end
252
+ end
253
+ [flow_cost, _build_flow_dict(graph, residual)]
254
+ end
255
+ end
@@ -0,0 +1,113 @@
1
+ module NetworkX
2
+ # TODO: Reduce method complexity and method length
3
+
4
+ # Helper function to augment the flow in a residual graph
5
+ def self.augment(residual, inf, path)
6
+ flow = inf
7
+ path_first_elem = path.shift
8
+ u = path_first_elem
9
+ path.each do |v|
10
+ flow = [flow, residual.adj[u][v][:capacity] - residual.adj[u][v][:flow]].min
11
+ u = v
12
+ end
13
+ raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf
14
+ u = path_first_elem
15
+ path.each do |v|
16
+ residual.adj[u][v][:flow] += flow
17
+ residual.adj[v][u][:flow] -= flow
18
+ u = v
19
+ end
20
+ flow
21
+ end
22
+
23
+ # TODO: Reduce method complexity and method length
24
+
25
+ # Helper function for the bidirectional bfs
26
+ def self.bidirectional_bfs(residual, source, target)
27
+ pred, succ = {source => nil}, {target => nil}
28
+ q_s, q_t = [source], [target]
29
+ loop do
30
+ q = []
31
+ if q_s.length <= q_t.length
32
+ q_s.each do |u|
33
+ residual.adj[u].each do |v, uv_attrs|
34
+ next unless !pred.include?(v) && (uv_attrs[:flow] < uv_attrs[:capacity])
35
+ pred[v] = u
36
+ return [v, pred, succ] if succ.key?(v)
37
+ q << v
38
+ end
39
+ end
40
+ return [nil, nil, nil] if q.empty?
41
+ else
42
+ q_t.each do |u|
43
+ residual.pred[u].each do |v, uv_attrs|
44
+ next unless !succ.key?(v) && uv_attrs[:flow] < uv_attrs[:capacity]
45
+ succ[v] = u
46
+ return [v, pred, succ] if pred.key?(v)
47
+ q << v
48
+ end
49
+ end
50
+ return [nil, nil, nil] if q.empty?
51
+ q_t = q
52
+ end
53
+ end
54
+ end
55
+
56
+ # TODO: Reduce method complexity and method length
57
+
58
+ # Core helper function for the EdmondsKarp algorithm
59
+ def self.edmondskarp_core(residual, source, target, cutoff)
60
+ inf = residual.graph[:inf]
61
+ flow_val = 0
62
+ while flow_val < cutoff
63
+ v, pred, succ = bidirectional_bfs(residual, source, target)
64
+ break if pred.nil?
65
+ path = [v]
66
+ u = v
67
+ while u != source
68
+ u = pred[u]
69
+ path << u
70
+ end
71
+ path.reverse!
72
+ u = v
73
+ while u != target
74
+ u = succ[u]
75
+ path << u
76
+ end
77
+ flow_val += augment(residual, inf, path)
78
+ end
79
+ flow_val
80
+ end
81
+
82
+ # TODO: Reduce method complexity and method length
83
+
84
+ # Helper function for the edmondskarp function
85
+ def self.edmondskarp_impl(graph, source, target, residual, cutoff)
86
+ raise ArgumentError, 'Source not in graph!' unless graph.nodes.key?(source)
87
+ raise ArgumentError, 'Target not in graph!' unless graph.nodes.key?(target)
88
+ raise ArgumentError, 'Source and target are same node!' if source == target
89
+ res_graph = residual.nil? ? build_residual_network(graph) : residual.clone
90
+ res_graph.adj.each do |u, u_edges|
91
+ u_edges.each do |v, _attrs|
92
+ res_graph.adj[u][v][:flow] = 0
93
+ res_graph.pred[v][u][:flow] = 0
94
+ end
95
+ end
96
+ cutoff = Float::INFINITY if cutoff.nil?
97
+ res_graph.graph[:flow_val] = edmondskarp_core(res_graph, source, target, cutoff)
98
+ res_graph
99
+ end
100
+
101
+ # Computes max flow using edmonds karp algorithm
102
+ #
103
+ # @param graph [Graph, DiGraph] a graph
104
+ # @param source [Object] source node
105
+ # @param target [Object] target node
106
+ # @param residual [DiGraph, nil] residual graph
107
+ # @param cutoff [Numeric] cutoff for the algorithm
108
+ #
109
+ # @return [DiGraph] a residual graph containing the flow values
110
+ def self.edmondskarp(graph, source, target, residual=nil, cutoff=nil)
111
+ edmondskarp_impl(graph, source, target, residual, cutoff)
112
+ end
113
+ end
@@ -0,0 +1,252 @@
1
+ # TODO: Reduce module length
2
+
3
+ module NetworkX
4
+ # Helper function to return an arbitrary element from an iterable object
5
+ def self.arbitrary_element(iterable)
6
+ iterable.each { |u| return u }
7
+ end
8
+
9
+ # TODO: Reduce method complexity and method length
10
+
11
+ # Helper function to apply the preflow push algorithm
12
+ def self.preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only)
13
+ raise ArgumentError, 'Source not in graph!' unless graph.nodes.key?(source)
14
+ raise ArgumentError, 'Target not in graph!' unless graph.nodes.key?(target)
15
+ raise ArgumentError, 'Source and Target are same!' if source == target
16
+
17
+ globalrelabel_freq = 0 if globalrelabel_freq.nil?
18
+ raise ArgumentError, 'Global Relabel Freq must be nonnegative!' if globalrelabel_freq < 0
19
+ r_network = residual.nil? ? build_residual_network(graph) : residual
20
+ detect_unboundedness(r_network, source, target)
21
+
22
+ residual_nodes = r_network.nodes
23
+ residual_adj = r_network.adj
24
+ residual_pred = r_network.pred
25
+
26
+ residual_nodes.each do |u, u_attrs|
27
+ u_attrs[:excess] = 0
28
+ residual_adj[u].each { |_v, attrs| attrs[:flow] = 0 }
29
+ end
30
+
31
+ heights = reverse_bfs(target, residual_pred)
32
+
33
+ unless heights.key?(source)
34
+ r_network.graph[:flow_value] = 0
35
+ return r_network
36
+ end
37
+
38
+ n = r_network.nodes.length
39
+ max_height = heights.map { |u, h| u == source ? -1 : h }.max
40
+ heights[source] = n
41
+
42
+ grt = GlobalRelabelThreshold.new(n, r_network.size, globalrelabel_freq)
43
+
44
+ residual_nodes.each do |u, u_attrs|
45
+ u_attrs[:height] = heights.key?(u) ? heights[u] : (n + 1)
46
+ u_attrs[:curr_edge] = CurrentEdge.new(residual_adj[u])
47
+ end
48
+
49
+ residual_adj[source].each do |u, attr|
50
+ flow = attr[:capacity]
51
+ push(source, u, flow, residual_nodes, residual_adj) if flow > 0
52
+ end
53
+
54
+ levels = (0..(2 * n - 1)).map { |_| Level.new }
55
+ residual_nodes.each do |u, attr|
56
+ if u != source && u != target
57
+ level = levels[attr[:height]]
58
+ residual_nodes[u][:excess] > 0 ? level.active.add(u) : level.inactive.add(u)
59
+ end
60
+ end
61
+
62
+ height = max_height
63
+ while height > 0
64
+ loop do
65
+ level = levels[height]
66
+ if level.active.empty?
67
+ height -= 1
68
+ break
69
+ end
70
+ old_height = height
71
+ old_level = level
72
+ u = arbitrary_element(level.active)
73
+ height = discharge(u, true, residual_nodes, residual_adj, height, levels, grt, source, target)
74
+ if grt.reached?
75
+ height = global_relabel(true, source, target, residual_nodes, n, levels, residual_pred)
76
+ max_height = height
77
+ grt.clear_work
78
+ elsif old_level.active.empty? && old_level.inactive.empty?
79
+ gap_heuristic(old_height, levels, residual_nodes)
80
+ height = old_height - 1
81
+ max_height = height
82
+ else
83
+ max_height = [max_height, height].max
84
+ end
85
+ end
86
+ end
87
+
88
+ if value_only
89
+ r_network.graph[:flow_value] = residual_nodes[target][:excess]
90
+ return r_network
91
+ end
92
+
93
+ height = global_relabel(false, source, target, residual_nodes, n, levels, residual_pred)
94
+ grt.clear_work
95
+
96
+ while height > n
97
+ loop do
98
+ level = levels[height]
99
+ if level.active.empty?
100
+ height -= 1
101
+ break
102
+ end
103
+ u = arbitrary_element(level.active)
104
+ height = discharge(u, false, residual_nodes, residual_adj, height, levels, grt, source, target)
105
+ if grt.reached?
106
+ height = global_relabel(false, source, target, residual_nodes, n, levels, residual_pred)
107
+ grt.clear_work
108
+ end
109
+ end
110
+ end
111
+ r_network.graph[:flow_value] = residual_nodes[target][:excess]
112
+ r_network
113
+ end
114
+
115
+ # Helper function to move a node from inactive set to active set
116
+ def self.activate(node, source, target, levels, residual_nodes)
117
+ return if node == source || node == target
118
+ return unless level.inactive.include?(node)
119
+ level = levels[residual_nodes[node][:height]]
120
+ level.inactive.delete(node)
121
+ level.active.add(node)
122
+ end
123
+
124
+ # Helper function to relable a node to create a permissible edge
125
+ def self.relabel(u_node, grt, r_adj, _r_nodes, _source, _target, _levels)
126
+ grt.add_work(r_adj[u_node].length)
127
+ r_adj[u_node].map { |v, attr| attr[:flow] < (attr[:capacity] + 1) ? _nodes[v][:height] : Float::INFINITY }.min
128
+ end
129
+
130
+ # TODO: Reduce method complexity and method length
131
+
132
+ # Helper function for discharging a node
133
+ def self.discharge(u_node, is_phase_1, residual_nodes, residual_adj, height, levels, grt, source, target)
134
+ height_val = residual_nodes[u_node][:height]
135
+ curr_edge = residual_nodes[u_node][:curr_edge]
136
+ next_height = height_val
137
+ levels[height_val].active.delete(u_node)
138
+
139
+ loop do
140
+ v, attr = curr_edge.get
141
+ if height_val == residual_nodes[v][:height] + 1 && attr[:flow] < attr[:capacity]
142
+ flow = [residual_nodes[u_node][:excess], attr[:capacity] - attr[:flow]].min
143
+ push(u_node, v, flow, residual_nodes, residual_adj)
144
+ activate(v, source, target, levels, residual_nodes)
145
+ if residual_nodes[u_node][:excess].zero?
146
+ levels[height_val].inactive.add(u_node)
147
+ break
148
+ end
149
+ end
150
+ begin
151
+ curr_edge.move_to_next
152
+ rescue StopIteration
153
+ height_val = relabel(u_node, grt, residual_adj, residual_nodes, source, target, levels)
154
+ if is_phase_1 && height_val >= n - 1
155
+ levels[height].active.add(u_node)
156
+ break
157
+ end
158
+ next_height = height_val
159
+ end
160
+ end
161
+ residual_nodes[u_node][:height] = height_val
162
+ next_height
163
+ end
164
+
165
+ # TODO: Reduce method complexity
166
+
167
+ # Helper function for applying gap heuristic
168
+ def self.gap_heuristic(height, levels, residual_nodes)
169
+ ((height + 1)..(max_height)).each do |idx|
170
+ level = levels[idx]
171
+ level.active.each { |u| residual_nodes[u][:height] = n + 1 }
172
+ level.inactive.each { |u| residual_nodes[u][:height] = n + 1 }
173
+ levels[n + 1].active.merge!(level.active)
174
+ level.active.clear
175
+ levels[n + 1].inactive.merge!(level.inactive)
176
+ level.inactive.clear
177
+ end
178
+ end
179
+
180
+ # TODO: Reduce method complexity and method length
181
+
182
+ # Helper function for global relabel heuristic
183
+ def self.global_relabel(from_sink, source, target, residual_nodes, num, levels, residual_pred)
184
+ src = from_sink ? target : source
185
+ heights = reverse_bfs(src, residual_pred)
186
+ heights.delete(target) unless from_sink
187
+ max_height = heights.values.max
188
+ if from_sink
189
+ residual_nodes.each { |u, attr| heights[u] = num + 1 if !heights.key?(u) && attr[:height] < num }
190
+ else
191
+ heights.each_key { |u| heights[u] += num }
192
+ max_height += num
193
+ end
194
+ heights.delete(src)
195
+ heights.each do |u, new_height|
196
+ old_height = residual_nodes[u][:height]
197
+ next unless new_height != old_height
198
+ if levels[old_height].active.include?(u)
199
+ levels[old_height].active.delete(u)
200
+ levels[new_height].active.add(u)
201
+ else
202
+ levels[old_height].inactive.delete(u)
203
+ levels[new_height].inactive.add(u)
204
+ end
205
+ residual_nodes[u][:height] = new_height
206
+ end
207
+ max_height
208
+ end
209
+
210
+ # Helper function for augmenting flow
211
+ def self.push(node_1, node_2, flow, residual_nodes, residual_adj)
212
+ residual_adj[node_1][node_2][:flow] += flow
213
+ residual_adj[node_2][node_1][:flow] -= flow
214
+ residual_nodes[node_1][:excess] -= flow
215
+ residual_nodes[node_2][:excess] += flow
216
+ end
217
+
218
+ # TODO: Reduce method length
219
+
220
+ # Helper function for reverse bfs
221
+ def self.reverse_bfs(src, residual_pred)
222
+ heights = {src => 0}
223
+ q = [[src, 0]]
224
+
225
+ until q.empty?
226
+ u, height = q.shift
227
+ height += 1
228
+ residual_pred[u].each do |v, attr|
229
+ if !heights.key?(v) && attr[:flow] < attr[:capacity]
230
+ heights[v] = height
231
+ q << [v, height]
232
+ end
233
+ end
234
+ end
235
+ heights
236
+ end
237
+
238
+ # Computes max flow using preflow push algorithm
239
+ #
240
+ # @param graph [DiGraph] a graph
241
+ # @param source [Object] source node
242
+ # @param target [Object] target node
243
+ # @param residual [DiGraph, nil] residual graph
244
+ # @param globalrelabel_freq [Numeric] global relabel freq
245
+ # @param value_only [Boolean] if false, compute maximum flow else
246
+ # maximum preflow
247
+ #
248
+ # @return [DiGraph] a residual graph containing the flow values
249
+ def self.preflowpush(graph, source, target, residual=nil, globalrelabel_freq=1, value_only=false)
250
+ preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only)
251
+ end
252
+ end