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.
- checksums.yaml +4 -4
- data/.rubocop.yml +18 -11
- data/CONTRIBUTING.md +2 -2
- data/Guardfile +1 -1
- data/ISSUE_TEMPLATE.md +15 -0
- data/PULL_REQUEST_TEMPLATE.md +12 -0
- data/README.md +4 -4
- data/RELEASE_POLICY.md +20 -0
- data/lib/networkx.rb +37 -1
- data/lib/networkx/auxillary_functions/cliques.rb +65 -0
- data/lib/networkx/auxillary_functions/cycles.rb +104 -0
- data/lib/networkx/auxillary_functions/dag.rb +54 -0
- data/lib/networkx/auxillary_functions/eccentricity.rb +36 -0
- data/lib/networkx/auxillary_functions/mis.rb +23 -0
- data/lib/networkx/auxillary_functions/mst.rb +35 -0
- data/lib/networkx/auxillary_functions/union_find.rb +24 -0
- data/lib/networkx/auxillary_functions/vitality.rb +13 -0
- data/lib/networkx/auxillary_functions/wiener.rb +13 -0
- data/lib/networkx/converters/to_csv.rb +47 -0
- data/lib/networkx/converters/to_json.rb +39 -0
- data/lib/networkx/digraph.rb +228 -0
- data/lib/networkx/flow/capacityscaling.rb +255 -0
- data/lib/networkx/flow/edmondskarp.rb +113 -0
- data/lib/networkx/flow/preflowpush.rb +252 -0
- data/lib/networkx/flow/shortestaugmentingpath.rb +157 -0
- data/lib/networkx/flow/utils.rb +160 -0
- data/lib/networkx/graph.rb +341 -0
- data/lib/networkx/link_analysis/hits.rb +59 -0
- data/lib/networkx/link_analysis/pagerank.rb +47 -0
- data/lib/networkx/multidigraph.rb +240 -0
- data/lib/networkx/multigraph.rb +171 -0
- data/lib/networkx/operators/all.rb +61 -0
- data/lib/networkx/operators/binary.rb +244 -0
- data/lib/networkx/operators/product.rb +204 -0
- data/lib/networkx/operators/unary.rb +17 -0
- data/lib/networkx/shortest_path/astar.rb +71 -0
- data/lib/networkx/shortest_path/dense.rb +31 -0
- data/lib/networkx/shortest_path/unweighted.rb +139 -0
- data/lib/networkx/shortest_path/weighted.rb +408 -0
- data/lib/networkx/to_matrix.rb +52 -0
- data/lib/networkx/traversals/bfs.rb +58 -0
- data/lib/networkx/traversals/dfs.rb +79 -0
- data/lib/networkx/traversals/edge_dfs.rb +90 -0
- data/lib/networkx/version.rb +1 -1
- data/networkx.gemspec +4 -1
- 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
|