networkx 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
- data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +20 -10
- data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +1 -1
- data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +2 -4
- data/.github/workflows/ci.yml +17 -0
- data/.github/workflows/doc.yml +23 -0
- data/.rspec +0 -1
- data/.rubocop.yml +57 -71
- data/.yardopts +0 -1
- data/README.md +32 -34
- data/Rakefile +2 -3
- data/lib/networkx/auxillary_functions/cliques.rb +9 -12
- data/lib/networkx/auxillary_functions/cycles.rb +17 -7
- data/lib/networkx/auxillary_functions/dag.rb +10 -5
- data/lib/networkx/auxillary_functions/eccentricity.rb +2 -1
- data/lib/networkx/auxillary_functions/mis.rb +2 -2
- data/lib/networkx/auxillary_functions/mst.rb +1 -3
- data/lib/networkx/auxillary_functions/union_find.rb +92 -12
- data/lib/networkx/auxillary_functions/wiener.rb +1 -1
- data/lib/networkx/converters/to_csv.rb +1 -3
- data/lib/networkx/converters/to_json.rb +0 -2
- data/lib/networkx/digraph.rb +55 -49
- data/lib/networkx/flow/capacityscaling.rb +29 -35
- data/lib/networkx/flow/edmondskarp.rb +17 -15
- data/lib/networkx/flow/preflowpush.rb +29 -32
- data/lib/networkx/flow/shortestaugmentingpath.rb +17 -20
- data/lib/networkx/flow/utils.rb +6 -27
- data/lib/networkx/graph.rb +179 -72
- data/lib/networkx/link_analysis/hits.rb +9 -9
- data/lib/networkx/link_analysis/pagerank.rb +29 -31
- data/lib/networkx/multidigraph.rb +90 -81
- data/lib/networkx/multigraph.rb +91 -63
- data/lib/networkx/operators/all.rb +8 -4
- data/lib/networkx/operators/binary.rb +106 -128
- data/lib/networkx/operators/product.rb +61 -64
- data/lib/networkx/operators/unary.rb +1 -1
- data/lib/networkx/others/bridges.rb +30 -0
- data/lib/networkx/others/generators.rb +237 -0
- data/lib/networkx/others/grid_2d_graph.rb +38 -0
- data/lib/networkx/others/info.rb +11 -0
- data/lib/networkx/others/number_connected_components.rb +17 -0
- data/lib/networkx/others/reads.rb +52 -0
- data/lib/networkx/shortest_path/astar.rb +10 -8
- data/lib/networkx/shortest_path/dense.rb +1 -3
- data/lib/networkx/shortest_path/unweighted.rb +13 -16
- data/lib/networkx/shortest_path/weighted.rb +51 -42
- data/lib/networkx/to_matrix.rb +2 -3
- data/lib/networkx/traversals/bfs.rb +54 -2
- data/lib/networkx/traversals/dfs.rb +62 -6
- data/lib/networkx/traversals/edge_dfs.rb +36 -12
- data/lib/networkx/version.rb +1 -1
- data/lib/networkx.rb +7 -1
- data/networkx.gemspec +17 -14
- metadata +74 -84
- data/.rspec_formatter.rb +0 -24
- data/.travis.yml +0 -18
- data/Guardfile +0 -7
- data/RELEASE_POLICY.md +0 -20
@@ -1,24 +1,104 @@
|
|
1
1
|
module NetworkX
|
2
|
+
# Union Find Tree
|
3
|
+
#
|
4
|
+
# Reference
|
5
|
+
# - [ac-library-rb DSU (CC0)](https://github.com/universato/ac-library-rb/blob/main/lib/dsu.rb)
|
6
|
+
# - [Python NetworkX UnionFind](https://networkx.org/documentation/stable/_modules/networkx/utils/union_find.html)
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# @attr_reader parents [Hash{ Object => Object }] Return parent of each element
|
10
|
+
# @attr_reader weights [Hash{ Object => Integer }] Return weight of each element
|
2
11
|
class UnionFind
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
12
|
+
attr_accessor :parents, :weights
|
13
|
+
|
14
|
+
# Constructor for initializing Union Find Tree
|
15
|
+
#
|
16
|
+
# @param nodes [?Array[Object]] nodes
|
17
|
+
#
|
18
|
+
# @return [UnionFind] Union Find Tree
|
19
|
+
def initialize(nodes = nil)
|
20
|
+
@weights = {}
|
21
|
+
@parents = {}
|
22
|
+
nodes&.each do |node|
|
23
|
+
@weights[node] = 1
|
24
|
+
@parents[node] = node
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return the root of node
|
29
|
+
#
|
30
|
+
# @param node [Object] node
|
31
|
+
#
|
32
|
+
# @return [Object] root of node, leader of node
|
33
|
+
def [](node)
|
34
|
+
if @parents.has_key?(node)
|
35
|
+
@parents[node] == node ? node : (@parents[node] = self[@parents[node]])
|
36
|
+
else
|
37
|
+
@weights[node] = 1
|
38
|
+
@parents[node] = node
|
7
39
|
end
|
8
40
|
end
|
9
41
|
|
10
|
-
|
11
|
-
|
42
|
+
# Return the root of node
|
43
|
+
#
|
44
|
+
# @param node [Object] node
|
45
|
+
#
|
46
|
+
# @return [Object] root of node, leader of node
|
47
|
+
def root(node)
|
48
|
+
@parents.has_key?(node) or raise ArgumentError.new, "#{node} is not a node"
|
49
|
+
|
50
|
+
@parents[node] == node ? node : (@parents[node] = root(@parents[node]))
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
@parents.each_key(&block)
|
12
55
|
end
|
13
56
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
57
|
+
def to_sets
|
58
|
+
each.group_by { |node| root(node) }.values
|
59
|
+
end
|
60
|
+
alias groups to_sets
|
18
61
|
|
19
|
-
|
20
|
-
|
62
|
+
# Is each root of two nodes the same?
|
63
|
+
#
|
64
|
+
# @param node1 [Object] node
|
65
|
+
# @param node2 [Object] node
|
66
|
+
#
|
67
|
+
# @return [bool] Is each root of node1 and nodes_2 the same?
|
68
|
+
def connected?(node1, node2)
|
69
|
+
root(node1) == root(node2)
|
70
|
+
end
|
71
|
+
alias same? connected?
|
72
|
+
|
73
|
+
# Unite nodes.
|
74
|
+
#
|
75
|
+
# @param nodes [Array[Object]] nodes
|
76
|
+
#
|
77
|
+
# @return [Object | nil] root of united nodes
|
78
|
+
def union(*nodes)
|
79
|
+
return merge(*nodes) if nodes.size == 2
|
80
|
+
|
81
|
+
roots = nodes.map { |node| self[node] }.uniq
|
82
|
+
return if roots.size == 1
|
83
|
+
|
84
|
+
roots.sort_by! { |root| @weights[root] }
|
85
|
+
root = roots[-1]
|
86
|
+
roots[0...-1].each do |r|
|
87
|
+
@weights[root] += @weights[r]
|
88
|
+
@parents[r] = root
|
21
89
|
end
|
90
|
+
root
|
91
|
+
end
|
92
|
+
alias unite union
|
93
|
+
|
94
|
+
def merge(node1, node2)
|
95
|
+
x = self[node1]
|
96
|
+
y = self[node2]
|
97
|
+
return if x == y
|
98
|
+
|
99
|
+
x, y = y, x if @weights[x] < @weights[y]
|
100
|
+
@weights[x] += @weights[y]
|
101
|
+
@parents[y] = x
|
22
102
|
end
|
23
103
|
end
|
24
104
|
end
|
@@ -7,7 +7,7 @@ module NetworkX
|
|
7
7
|
def self.wiener_index(graph)
|
8
8
|
total = all_pairs_shortest_path_length(graph)
|
9
9
|
wiener_ind = 0
|
10
|
-
|
10
|
+
total.to_h.each { |_, distances| distances.to_h.each { |_, val| wiener_ind += val } }
|
11
11
|
graph.directed? ? wiener_ind : wiener_ind / 2
|
12
12
|
end
|
13
13
|
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
module NetworkX
|
2
|
-
# TODO: Reduce method length and method complexity
|
3
|
-
|
4
2
|
# Saves the graph in a csv file
|
5
3
|
#
|
6
4
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
7
5
|
# @param filename [String] filename of the graph
|
8
|
-
def self.graph_to_csv(graph, filename='graph.csv')
|
6
|
+
def self.graph_to_csv(graph, filename = 'graph.csv')
|
9
7
|
CSV.open(filename, 'wb') do |csv|
|
10
8
|
csv << [graph.class.name]
|
11
9
|
csv << ['graph_values']
|
data/lib/networkx/digraph.rb
CHANGED
@@ -8,7 +8,7 @@ module NetworkX
|
|
8
8
|
# @attr_reader nodes [Hash{ Object => Hash{ Object => Object } }] Stores the nodes and their attributes
|
9
9
|
# @attr_reader graph [Hash{ Object => Object }] Stores the attributes of the graph
|
10
10
|
class DiGraph < Graph
|
11
|
-
attr_reader :adj, :
|
11
|
+
attr_reader :adj, :graph, :pred
|
12
12
|
|
13
13
|
# Constructor for initializing graph
|
14
14
|
#
|
@@ -17,7 +17,7 @@ module NetworkX
|
|
17
17
|
#
|
18
18
|
# @param graph_attrs [Hash{ Object => Object }] the graph attributes in a hash format
|
19
19
|
def initialize(**graph_attrs)
|
20
|
-
super(graph_attrs)
|
20
|
+
super(**graph_attrs)
|
21
21
|
|
22
22
|
@pred = {}
|
23
23
|
end
|
@@ -30,16 +30,16 @@ module NetworkX
|
|
30
30
|
# @example Add an edge with no attribute
|
31
31
|
# graph.add_edge("Bangalore", "Chennai")
|
32
32
|
#
|
33
|
-
# @param
|
34
|
-
# @param
|
33
|
+
# @param node1 [Object] the first node of the edge
|
34
|
+
# @param node2 [Object] the second node of the edge
|
35
35
|
# @param edge_attrs [Hash{ Object => Object }] the hash of the edge attributes
|
36
|
-
def add_edge(
|
37
|
-
add_node(
|
38
|
-
add_node(
|
36
|
+
def add_edge(node1, node2, **edge_attrs)
|
37
|
+
add_node(node1)
|
38
|
+
add_node(node2)
|
39
39
|
|
40
|
-
edge_attrs = (@adj[
|
41
|
-
@adj[
|
42
|
-
@pred[
|
40
|
+
edge_attrs = (@adj[node1][node2] || {}).merge(edge_attrs)
|
41
|
+
@adj[node1][node2] = edge_attrs
|
42
|
+
@pred[node2][node1] = edge_attrs
|
43
43
|
end
|
44
44
|
|
45
45
|
# Adds a node and its attributes to the graph
|
@@ -50,9 +50,13 @@ module NetworkX
|
|
50
50
|
# @param node [Object] the node object
|
51
51
|
# @param node_attrs [Hash{ Object => Object }] the hash of the attributes of the node
|
52
52
|
def add_node(node, **node_attrs)
|
53
|
-
super(node, node_attrs)
|
53
|
+
super(node, **node_attrs)
|
54
54
|
|
55
|
-
@pred[node] = {} unless @pred.
|
55
|
+
@pred[node] = {} unless @pred.has_key?(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
def nodes(data: true)
|
59
|
+
data ? @nodes : @nodes.keys
|
56
60
|
end
|
57
61
|
|
58
62
|
# Removes node from the graph
|
@@ -62,7 +66,7 @@ module NetworkX
|
|
62
66
|
#
|
63
67
|
# @param node [Object] the node to be removed
|
64
68
|
def remove_node(node)
|
65
|
-
raise KeyError, "Error in deleting node #{node} from Graph." unless @nodes.
|
69
|
+
raise KeyError, "Error in deleting node #{node} from Graph." unless @nodes.has_key?(node)
|
66
70
|
|
67
71
|
neighbours = @adj[node]
|
68
72
|
neighbours.each_key { |k| @pred[k].delete(node) }
|
@@ -80,15 +84,15 @@ module NetworkX
|
|
80
84
|
# @example
|
81
85
|
# graph.remove_edge('Noida', 'Bangalore')
|
82
86
|
#
|
83
|
-
# @param
|
84
|
-
# @param
|
85
|
-
def remove_edge(
|
86
|
-
raise KeyError, "#{
|
87
|
-
raise KeyError, "#{
|
88
|
-
raise KeyError, 'The given edge is not a valid one.' unless @adj[
|
87
|
+
# @param node1 [Object] the first node of the edge
|
88
|
+
# @param node2 [Object] the second node of the edge
|
89
|
+
def remove_edge(node1, node2)
|
90
|
+
raise KeyError, "#{node1} is not a valid node." unless @nodes.has_key?(node1)
|
91
|
+
raise KeyError, "#{node2} is not a valid node" unless @nodes.has_key?(node2)
|
92
|
+
raise KeyError, 'The given edge is not a valid one.' unless @adj[node1].has_key?(node2)
|
89
93
|
|
90
|
-
@adj[
|
91
|
-
@pred[
|
94
|
+
@adj[node1].delete(node2)
|
95
|
+
@pred[node2].delete(node1)
|
92
96
|
end
|
93
97
|
|
94
98
|
# Clears the graph
|
@@ -106,7 +110,7 @@ module NetworkX
|
|
106
110
|
# @example
|
107
111
|
# graph.number_of_edges
|
108
112
|
def number_of_edges
|
109
|
-
@adj.values.map(&:length).
|
113
|
+
@adj.values.map(&:length).sum
|
110
114
|
end
|
111
115
|
|
112
116
|
# Returns the size of graph
|
@@ -116,11 +120,11 @@ module NetworkX
|
|
116
120
|
#
|
117
121
|
# @param is_weighted [Bool] if true, method returns sum of weights of all edges
|
118
122
|
# else returns number of edges
|
119
|
-
def size(is_weighted=false)
|
123
|
+
def size(is_weighted = false)
|
120
124
|
if is_weighted
|
121
125
|
graph_size = 0
|
122
126
|
@adj.each do |_, hash_val|
|
123
|
-
hash_val.each { |_, v| graph_size += v[:weight] if v.
|
127
|
+
hash_val.each { |_, v| graph_size += v[:weight] if v.has_key?(:weight) }
|
124
128
|
end
|
125
129
|
return graph_size
|
126
130
|
end
|
@@ -152,10 +156,10 @@ module NetworkX
|
|
152
156
|
# @example
|
153
157
|
# graph.reverse
|
154
158
|
def reverse
|
155
|
-
new_graph = NetworkX::DiGraph.new(
|
156
|
-
@nodes.each { |u, attrs| new_graph.add_node(u, attrs) }
|
159
|
+
new_graph = NetworkX::DiGraph.new(**@graph)
|
160
|
+
@nodes.each { |u, attrs| new_graph.add_node(u, **attrs) }
|
157
161
|
@adj.each do |u, edges|
|
158
|
-
edges.each { |v, attrs| new_graph.add_edge(v, u, attrs) }
|
162
|
+
edges.each { |v, attrs| new_graph.add_edge(v, u, **attrs) }
|
159
163
|
end
|
160
164
|
new_graph
|
161
165
|
end
|
@@ -165,16 +169,14 @@ module NetworkX
|
|
165
169
|
# @example
|
166
170
|
# graph.to_undirected
|
167
171
|
def to_undirected
|
168
|
-
new_graph = NetworkX::Graph.new(
|
169
|
-
@nodes.each { |u, attrs| new_graph.add_node(u, attrs) }
|
172
|
+
new_graph = NetworkX::Graph.new(**@graph)
|
173
|
+
@nodes.each { |u, attrs| new_graph.add_node(u, **attrs) }
|
170
174
|
@adj.each do |u, edges|
|
171
|
-
edges.each { |v, attrs| new_graph.add_edge(u, v, attrs) }
|
175
|
+
edges.each { |v, attrs| new_graph.add_edge(u, v, **attrs) }
|
172
176
|
end
|
173
177
|
new_graph
|
174
178
|
end
|
175
179
|
|
176
|
-
# TODO: Reduce method complexity and method length
|
177
|
-
|
178
180
|
# Returns subgraph consisting of given array of nodes
|
179
181
|
#
|
180
182
|
# @example
|
@@ -184,23 +186,22 @@ module NetworkX
|
|
184
186
|
def subgraph(nodes)
|
185
187
|
case nodes
|
186
188
|
when Array, Set
|
187
|
-
sub_graph = NetworkX::DiGraph.new(
|
189
|
+
sub_graph = NetworkX::DiGraph.new(**@graph)
|
188
190
|
nodes.each do |u|
|
189
191
|
raise KeyError, "#{u} does not exist in the current graph!" unless node?(u)
|
190
|
-
|
192
|
+
|
193
|
+
sub_graph.add_node(u, **@nodes[u])
|
191
194
|
@adj[u].each do |v, uv_attrs|
|
192
|
-
sub_graph.add_edge(u, v, uv_attrs) if @adj[u].
|
195
|
+
sub_graph.add_edge(u, v, **uv_attrs) if @adj[u].has_key?(v) && nodes.include?(v)
|
193
196
|
end
|
194
|
-
return sub_graph
|
195
197
|
end
|
198
|
+
sub_graph
|
196
199
|
else
|
197
|
-
raise ArgumentError, 'Expected Argument to be Array or Set of nodes, '\
|
198
|
-
|
200
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of nodes, ' \
|
201
|
+
"received #{nodes.class.name} instead."
|
199
202
|
end
|
200
203
|
end
|
201
204
|
|
202
|
-
# TODO: Reduce method complexity and method length
|
203
|
-
|
204
205
|
# Returns subgraph consisting of given edges
|
205
206
|
#
|
206
207
|
# @example
|
@@ -210,19 +211,24 @@ module NetworkX
|
|
210
211
|
def edge_subgraph(edges)
|
211
212
|
case edges
|
212
213
|
when Array, Set
|
213
|
-
sub_graph = NetworkX::DiGraph.new(
|
214
|
+
sub_graph = NetworkX::DiGraph.new(**@graph)
|
214
215
|
edges.each do |u, v|
|
215
|
-
raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.
|
216
|
-
&& @adj[u].
|
217
|
-
|
218
|
-
sub_graph.add_node(
|
219
|
-
sub_graph.
|
216
|
+
raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.has_key?(u) \
|
217
|
+
&& @adj[u].has_key?(v)
|
218
|
+
|
219
|
+
sub_graph.add_node(u, **@nodes[u])
|
220
|
+
sub_graph.add_node(v, **@nodes[v])
|
221
|
+
sub_graph.add_edge(u, v, **@adj[u][v])
|
220
222
|
end
|
221
|
-
|
223
|
+
sub_graph
|
222
224
|
else
|
223
|
-
raise ArgumentError, 'Expected Argument to be Array or Set of edges, '\
|
224
|
-
|
225
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of edges, ' \
|
226
|
+
"received #{edges.class.name} instead."
|
225
227
|
end
|
226
228
|
end
|
229
|
+
|
230
|
+
def directed?
|
231
|
+
true
|
232
|
+
end
|
227
233
|
end
|
228
234
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# TODO: Reduce module length
|
2
|
-
|
3
1
|
module NetworkX
|
4
2
|
# Returns a label for unique node
|
5
3
|
def self.generate_unique_node
|
@@ -24,8 +22,6 @@ module NetworkX
|
|
24
22
|
false
|
25
23
|
end
|
26
24
|
|
27
|
-
# TODO: Reduce method complexity and method length
|
28
|
-
|
29
25
|
# Detects the unboundedness in the residual graph
|
30
26
|
def self._detect_unboundedness(residual)
|
31
27
|
g = NetworkX::DiGraph.new
|
@@ -42,14 +38,13 @@ module NetworkX
|
|
42
38
|
raise ArgumentError, 'Negative cost cycle of infinite capacity found!' if negative_edge_cycle(g)
|
43
39
|
end
|
44
40
|
|
45
|
-
# TODO: Reduce method complexity and method length
|
46
|
-
|
47
41
|
# Returns the residual graph of the given graph
|
48
42
|
def self._build_residual_network(graph)
|
49
|
-
raise ArgumentError, 'Sum of demands should be 0!' unless\
|
43
|
+
raise ArgumentError, 'Sum of demands should be 0!' unless \
|
50
44
|
graph.nodes.values.map { |attr| attr[:demand] || 0 }.inject(0, :+).zero?
|
45
|
+
|
51
46
|
residual = NetworkX::MultiDiGraph.new(inf: 0)
|
52
|
-
residual.add_nodes(graph.nodes.map { |u, attr| [u, excess: (attr[:demand] || 0) * -1, potential: 0] })
|
47
|
+
residual.add_nodes(graph.nodes.map { |u, attr| [u, {excess: (attr[:demand] || 0) * -1, potential: 0}] })
|
53
48
|
inf = Float::INFINITY
|
54
49
|
edge_list = []
|
55
50
|
|
@@ -59,20 +54,20 @@ module NetworkX
|
|
59
54
|
graph.adj.each do |u, u_edges|
|
60
55
|
u_edges.each do |v, uv_edges|
|
61
56
|
uv_edges.each do |k, attrs|
|
62
|
-
edge_list << [u, v, k, e] if u != v && (attrs[:capacity] || inf)
|
57
|
+
edge_list << [u, v, k, e] if u != v && (attrs[:capacity] || inf).positive?
|
63
58
|
end
|
64
59
|
end
|
65
60
|
end
|
66
61
|
else
|
67
62
|
graph.adj.each do |u, u_edges|
|
68
63
|
u_edges.each do |v, attrs|
|
69
|
-
edge_list << [u, v, 0, attrs] if u != v && (attrs[:capacity] || inf)
|
64
|
+
edge_list << [u, v, 0, attrs] if u != v && (attrs[:capacity] || inf).positive?
|
70
65
|
end
|
71
66
|
end
|
72
67
|
end
|
73
68
|
|
74
69
|
temp_inf = [residual.nodes.map { |_u, attrs| attrs[:excess].abs }.inject(0, :+), edge_list.map do |_, _, _, e|
|
75
|
-
(e.
|
70
|
+
(e.has_key?(:capacity) && e[:capacity] != inf ? e[:capacity] : 0)
|
76
71
|
end.inject(0, :+) * 2].max
|
77
72
|
inf = temp_inf.zero? ? 1 : temp_inf
|
78
73
|
|
@@ -87,8 +82,6 @@ module NetworkX
|
|
87
82
|
residual
|
88
83
|
end
|
89
84
|
|
90
|
-
# TODO: Reduce method complexity and method length
|
91
|
-
|
92
85
|
# Returns the flowdict of the graph
|
93
86
|
def self._build_flow_dict(graph, residual)
|
94
87
|
flow_dict = {}
|
@@ -98,22 +91,24 @@ module NetworkX
|
|
98
91
|
graph.nodes.each_key do |u|
|
99
92
|
flow_dict[u] = {}
|
100
93
|
graph.adj[u].each do |v, uv_edges|
|
101
|
-
flow_dict[u][v] =
|
102
|
-
|
103
|
-
end
|
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
|
104
97
|
end
|
105
98
|
residual.adj[u].each do |v, uv_edges|
|
106
|
-
flow_dict[u][v].merge!(
|
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)
|
107
102
|
end
|
108
103
|
end
|
109
104
|
else
|
110
105
|
graph.nodes.each_key do |u|
|
111
|
-
flow_dict[u] =
|
106
|
+
flow_dict[u] = graph.adj[u].to_h do |v, e|
|
112
107
|
[v, u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity]]
|
113
|
-
end
|
108
|
+
end
|
114
109
|
merge_dict = {}
|
115
110
|
residual.adj[u].each do |v, uv_edges|
|
116
|
-
uv_edges.each_value { |attrs| merge_dict[v] = attrs[:flow] if attrs[:flow]
|
111
|
+
uv_edges.each_value { |attrs| merge_dict[v] = attrs[:flow] if (attrs[:flow]).positive? }
|
117
112
|
end
|
118
113
|
flow_dict[u].merge!(merge_dict)
|
119
114
|
end
|
@@ -121,15 +116,6 @@ module NetworkX
|
|
121
116
|
flow_dict
|
122
117
|
end
|
123
118
|
|
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
119
|
# Computes max flow using capacity scaling algorithm
|
134
120
|
#
|
135
121
|
# @param graph [DiGraph, MultiDiGraph] a graph
|
@@ -148,19 +134,22 @@ module NetworkX
|
|
148
134
|
end).max
|
149
135
|
|
150
136
|
return flow_cost, _build_flow_dict(graph, residual) if wmax == -inf
|
137
|
+
|
151
138
|
r_nodes = residual.nodes
|
152
139
|
r_adj = residual.adj
|
153
140
|
|
154
|
-
delta = 2
|
141
|
+
delta = 2**Math.log2(wmax).floor
|
155
142
|
while delta >= 1
|
156
143
|
r_nodes.each do |u, u_attrs|
|
157
144
|
p_u = u_attrs[:potential]
|
158
145
|
r_adj[u].each do |v, uv_edges|
|
159
146
|
uv_edges.each do |_k, e|
|
160
147
|
flow = e[:capacity]
|
161
|
-
next unless e[:weight] - p_u + r_nodes[v][:potential]
|
148
|
+
next unless (e[:weight] - p_u + r_nodes[v][:potential]).negative?
|
149
|
+
|
162
150
|
flow = e[:capacity] - e[:flow]
|
163
151
|
next unless flow >= delta
|
152
|
+
|
164
153
|
e[:flow] += flow
|
165
154
|
r_adj[v][u].each_key do |val|
|
166
155
|
val[:flow] += val[:temp_key][0] == e[:temp_key][0] && val[:temp_key][1] != e[:temp_key][1] ? -flow : 0
|
@@ -201,24 +190,31 @@ module NetworkX
|
|
201
190
|
end
|
202
191
|
p_u = r_nodes[u][:potential]
|
203
192
|
r_adj[u].each do |v, uv_edges|
|
204
|
-
next if d.
|
193
|
+
next if d.has_key?(v)
|
194
|
+
|
205
195
|
wmin = inf
|
206
196
|
uv_edges.each_value do |e|
|
207
197
|
next unless e[:capacity] - e[:flow] >= delta
|
198
|
+
|
208
199
|
w = e[:weight]
|
209
200
|
next unless w < wmin
|
201
|
+
|
210
202
|
wmin = w
|
211
203
|
end
|
212
204
|
next if wmin == inf
|
205
|
+
|
213
206
|
d_v = d_u + wmin - p_u + r_nodes[v][:potential]
|
214
207
|
next unless h_dict[v] > d_v
|
208
|
+
|
215
209
|
h << [d_v, count, v]
|
216
210
|
h_dict[v] = d_v
|
217
211
|
pred[v] = [u, kmin, emin]
|
218
212
|
end
|
219
213
|
end
|
220
214
|
|
221
|
-
if
|
215
|
+
if t.nil?
|
216
|
+
s_set.delete(s)
|
217
|
+
else
|
222
218
|
while u != s
|
223
219
|
v = u
|
224
220
|
u, k, e = pred[v]
|
@@ -233,8 +229,6 @@ module NetworkX
|
|
233
229
|
t_set.delete(t) if r_nodes[t][:excess] > -delta
|
234
230
|
d_t = d[t]
|
235
231
|
d.each { |node, d_u_node| r_nodes[node][:potential] -= (d_u_node - d_t) }
|
236
|
-
else
|
237
|
-
s_set.delete(s)
|
238
232
|
end
|
239
233
|
end
|
240
234
|
delta = (delta / 2).floor
|
@@ -1,6 +1,4 @@
|
|
1
1
|
module NetworkX
|
2
|
-
# TODO: Reduce method complexity and method length
|
3
|
-
|
4
2
|
# Helper function to augment the flow in a residual graph
|
5
3
|
def self.augment(residual, inf, path)
|
6
4
|
flow = inf
|
@@ -11,6 +9,7 @@ module NetworkX
|
|
11
9
|
u = v
|
12
10
|
end
|
13
11
|
raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf
|
12
|
+
|
14
13
|
u = path_first_elem
|
15
14
|
path.each do |v|
|
16
15
|
residual.adj[u][v][:flow] += flow
|
@@ -20,8 +19,6 @@ module NetworkX
|
|
20
19
|
flow
|
21
20
|
end
|
22
21
|
|
23
|
-
# TODO: Reduce method complexity and method length
|
24
|
-
|
25
22
|
# Helper function for the bidirectional bfs
|
26
23
|
def self.bidirectional_bfs(residual, source, target)
|
27
24
|
pred, succ = {source => nil}, {target => nil}
|
@@ -32,29 +29,34 @@ module NetworkX
|
|
32
29
|
q_s.each do |u|
|
33
30
|
residual.adj[u].each do |v, uv_attrs|
|
34
31
|
next unless !pred.include?(v) && (uv_attrs[:flow] < uv_attrs[:capacity])
|
32
|
+
|
35
33
|
pred[v] = u
|
36
|
-
return [v, pred, succ] if succ.
|
34
|
+
return [v, pred, succ] if succ.has_key?(v)
|
35
|
+
|
37
36
|
q << v
|
38
37
|
end
|
39
38
|
end
|
40
39
|
return [nil, nil, nil] if q.empty?
|
40
|
+
|
41
|
+
q_s = q
|
41
42
|
else
|
42
43
|
q_t.each do |u|
|
43
44
|
residual.pred[u].each do |v, uv_attrs|
|
44
|
-
next unless !succ.
|
45
|
+
next unless !succ.has_key?(v) && uv_attrs[:flow] < uv_attrs[:capacity]
|
46
|
+
|
45
47
|
succ[v] = u
|
46
|
-
return [v, pred, succ] if pred.
|
48
|
+
return [v, pred, succ] if pred.has_key?(v)
|
49
|
+
|
47
50
|
q << v
|
48
51
|
end
|
49
52
|
end
|
50
53
|
return [nil, nil, nil] if q.empty?
|
54
|
+
|
51
55
|
q_t = q
|
52
56
|
end
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
56
|
-
# TODO: Reduce method complexity and method length
|
57
|
-
|
58
60
|
# Core helper function for the EdmondsKarp algorithm
|
59
61
|
def self.edmondskarp_core(residual, source, target, cutoff)
|
60
62
|
inf = residual.graph[:inf]
|
@@ -62,6 +64,7 @@ module NetworkX
|
|
62
64
|
while flow_val < cutoff
|
63
65
|
v, pred, succ = bidirectional_bfs(residual, source, target)
|
64
66
|
break if pred.nil?
|
67
|
+
|
65
68
|
path = [v]
|
66
69
|
u = v
|
67
70
|
while u != source
|
@@ -79,13 +82,12 @@ module NetworkX
|
|
79
82
|
flow_val
|
80
83
|
end
|
81
84
|
|
82
|
-
# TODO: Reduce method complexity and method length
|
83
|
-
|
84
85
|
# Helper function for the edmondskarp function
|
85
86
|
def self.edmondskarp_impl(graph, source, target, residual, cutoff)
|
86
|
-
raise ArgumentError, 'Source not in graph!' unless graph.nodes.
|
87
|
-
raise ArgumentError, 'Target not in graph!' unless graph.nodes.
|
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)
|
88
89
|
raise ArgumentError, 'Source and target are same node!' if source == target
|
90
|
+
|
89
91
|
res_graph = residual.nil? ? build_residual_network(graph) : residual.clone
|
90
92
|
res_graph.adj.each do |u, u_edges|
|
91
93
|
u_edges.each do |v, _attrs|
|
@@ -94,7 +96,7 @@ module NetworkX
|
|
94
96
|
end
|
95
97
|
end
|
96
98
|
cutoff = Float::INFINITY if cutoff.nil?
|
97
|
-
res_graph.graph[:
|
99
|
+
res_graph.graph[:flow_value] = edmondskarp_core(res_graph, source, target, cutoff)
|
98
100
|
res_graph
|
99
101
|
end
|
100
102
|
|
@@ -107,7 +109,7 @@ module NetworkX
|
|
107
109
|
# @param cutoff [Numeric] cutoff for the algorithm
|
108
110
|
#
|
109
111
|
# @return [DiGraph] a residual graph containing the flow values
|
110
|
-
def self.edmondskarp(graph, source, target, residual=nil, cutoff=nil)
|
112
|
+
def self.edmondskarp(graph, source, target, residual = nil, cutoff = nil)
|
111
113
|
edmondskarp_impl(graph, source, target, residual, cutoff)
|
112
114
|
end
|
113
115
|
end
|