networkx 0.1.0 → 0.1.1

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 (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