networkx 0.1.1 → 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.
- 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/.github/workflows/gem-push.yml +45 -0
- data/.rspec +0 -1
- data/.rubocop.yml +57 -71
- data/.yardopts +0 -1
- data/README.md +27 -27
- 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 +48 -6
- 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 +12 -13
- metadata +71 -81
- data/.rspec_formatter.rb +0 -24
- data/.travis.yml +0 -18
- data/Guardfile +0 -7
- data/RELEASE_POLICY.md +0 -20
@@ -1,6 +1,4 @@
|
|
1
1
|
module NetworkX
|
2
|
-
# TODO: Reduce method complexity and method length
|
3
|
-
|
4
2
|
# Returns the maximal independent set of a graph
|
5
3
|
#
|
6
4
|
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
@@ -9,9 +7,11 @@ module NetworkX
|
|
9
7
|
# @return [Numeric] radius of the graph
|
10
8
|
def self.maximal_independent_set(graph, nodes)
|
11
9
|
raise 'The array containing the nodes should be a subset of the graph!' if (graph.nodes.keys - nodes).empty?
|
10
|
+
|
12
11
|
neighbours = []
|
13
12
|
nodes.each { |u| graph.adj[u].each { |v, _| neighbours |= [v] } }
|
14
13
|
raise 'Nodes is not an independent set of graph!' if (neighbours - nodes).empty?
|
14
|
+
|
15
15
|
available_nodes = graph.nodes.keys - (neighbours | nodes)
|
16
16
|
until available_nodes.empty?
|
17
17
|
node = available_nodes.sample
|
@@ -11,8 +11,6 @@ module NetworkX
|
|
11
11
|
edges
|
12
12
|
end
|
13
13
|
|
14
|
-
# TODO: Reduce method complexity and method length
|
15
|
-
|
16
14
|
# Returns the minimum spanning tree of a graph
|
17
15
|
#
|
18
16
|
# @param graph [Graph, DiGraph] a graph
|
@@ -27,7 +25,7 @@ module NetworkX
|
|
27
25
|
edge = edges.shift
|
28
26
|
unless union_find.connected?(edge[0][0], edge[0][1])
|
29
27
|
union_find.union(edge[0][0], edge[0][1])
|
30
|
-
mst.add_edge(edge[0][0], edge[0][1], graph.adj[edge[0][0]][edge[0][1]])
|
28
|
+
mst.add_edge(edge[0][0], edge[0][1], **graph.adj[edge[0][0]][edge[0][1]])
|
31
29
|
end
|
32
30
|
end
|
33
31
|
mst
|
@@ -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
|