networkx 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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