networkx 0.1.0 → 0.2.0

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