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.
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} +20 -10
  4. data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +1 -1
  5. data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +2 -4
  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 +57 -71
  11. data/.yardopts +0 -1
  12. data/README.md +27 -27
  13. data/Rakefile +2 -3
  14. data/lib/networkx/auxillary_functions/cliques.rb +9 -12
  15. data/lib/networkx/auxillary_functions/cycles.rb +17 -7
  16. data/lib/networkx/auxillary_functions/dag.rb +10 -5
  17. data/lib/networkx/auxillary_functions/eccentricity.rb +2 -1
  18. data/lib/networkx/auxillary_functions/mis.rb +2 -2
  19. data/lib/networkx/auxillary_functions/mst.rb +1 -3
  20. data/lib/networkx/auxillary_functions/union_find.rb +92 -12
  21. data/lib/networkx/auxillary_functions/wiener.rb +1 -1
  22. data/lib/networkx/converters/to_csv.rb +1 -3
  23. data/lib/networkx/converters/to_json.rb +0 -2
  24. data/lib/networkx/digraph.rb +55 -49
  25. data/lib/networkx/flow/capacityscaling.rb +29 -35
  26. data/lib/networkx/flow/edmondskarp.rb +17 -15
  27. data/lib/networkx/flow/preflowpush.rb +29 -32
  28. data/lib/networkx/flow/shortestaugmentingpath.rb +17 -20
  29. data/lib/networkx/flow/utils.rb +6 -27
  30. data/lib/networkx/graph.rb +179 -72
  31. data/lib/networkx/link_analysis/hits.rb +9 -9
  32. data/lib/networkx/link_analysis/pagerank.rb +48 -6
  33. data/lib/networkx/multidigraph.rb +90 -81
  34. data/lib/networkx/multigraph.rb +91 -63
  35. data/lib/networkx/operators/all.rb +8 -4
  36. data/lib/networkx/operators/binary.rb +106 -128
  37. data/lib/networkx/operators/product.rb +61 -64
  38. data/lib/networkx/operators/unary.rb +1 -1
  39. data/lib/networkx/others/bridges.rb +30 -0
  40. data/lib/networkx/others/generators.rb +237 -0
  41. data/lib/networkx/others/grid_2d_graph.rb +38 -0
  42. data/lib/networkx/others/info.rb +11 -0
  43. data/lib/networkx/others/number_connected_components.rb +17 -0
  44. data/lib/networkx/others/reads.rb +52 -0
  45. data/lib/networkx/shortest_path/astar.rb +10 -8
  46. data/lib/networkx/shortest_path/dense.rb +1 -3
  47. data/lib/networkx/shortest_path/unweighted.rb +13 -16
  48. data/lib/networkx/shortest_path/weighted.rb +51 -42
  49. data/lib/networkx/to_matrix.rb +2 -3
  50. data/lib/networkx/traversals/bfs.rb +54 -2
  51. data/lib/networkx/traversals/dfs.rb +62 -6
  52. data/lib/networkx/traversals/edge_dfs.rb +36 -12
  53. data/lib/networkx/version.rb +1 -1
  54. data/lib/networkx.rb +7 -1
  55. data/networkx.gemspec +12 -13
  56. metadata +71 -81
  57. data/.rspec_formatter.rb +0 -24
  58. data/.travis.yml +0 -18
  59. data/Guardfile +0 -7
  60. 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
- def initialize(nodes)
4
- @unions = {}
5
- nodes.each_with_index do |node, index|
6
- @unions[node] = index
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
- def connected?(node_1, node_2)
11
- @unions[node_1] == @unions[node_2]
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 union(node_1, node_2)
15
- return if connected?(node_1, node_2)
16
- node1_id = @unions[node_1]
17
- node2_id = @unions[node_2]
57
+ def to_sets
58
+ each.group_by { |node| root(node) }.values
59
+ end
60
+ alias groups to_sets
18
61
 
19
- @unions.each do |node, id|
20
- @unions[node] = node1_id if id == node2_id
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
- Hash[total].each { |_, distances| Hash[distances].each { |_, val| wiener_ind += val } }
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']
@@ -13,8 +13,6 @@ module NetworkX
13
13
  json_hash.to_json
14
14
  end
15
15
 
16
- # TODO: Reduce method length
17
-
18
16
  # Returns a graph from the json encoded graph
19
17
  #
20
18
  # @param json_str [JSON] json encoded string
@@ -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, :nodes, :graph, :pred
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 node_1 [Object] the first node of the edge
34
- # @param node_2 [Object] the second node of the edge
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(node_1, node_2, **edge_attrs)
37
- add_node(node_1)
38
- add_node(node_2)
36
+ def add_edge(node1, node2, **edge_attrs)
37
+ add_node(node1)
38
+ add_node(node2)
39
39
 
40
- edge_attrs = (@adj[node_1][node_2] || {}).merge(edge_attrs)
41
- @adj[node_1][node_2] = edge_attrs
42
- @pred[node_2][node_1] = edge_attrs
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.key?(node)
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.key?(node)
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 node_1 [Object] the first node of the edge
84
- # @param node_2 [Object] the second node of the edge
85
- def remove_edge(node_1, node_2)
86
- raise KeyError, "#{node_1} is not a valid node." unless @nodes.key?(node_1)
87
- raise KeyError, "#{node_2} is not a valid node" unless @nodes.key?(node_2)
88
- raise KeyError, 'The given edge is not a valid one.' unless @adj[node_1].key?(node_2)
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[node_1].delete(node_2)
91
- @pred[node_2].delete(node_1)
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).inject(:+)
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.key?(:weight) }
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(@graph)
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(@graph)
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(@graph)
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
- sub_graph.add_node(u, @nodes[u])
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].key?(v) && nodes.include?(v)
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
- "received #{nodes.class.name} instead."
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(@graph)
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.key?(u)\
216
- && @adj[u].key?(v)
217
- sub_graph.add_node(u, @nodes[u])
218
- sub_graph.add_node(v, @nodes[v])
219
- sub_graph.add_edge(u, v, @adj[u][v])
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
- return sub_graph
223
+ sub_graph
222
224
  else
223
- raise ArgumentError, 'Expected Argument to be Array or Set of edges, '\
224
- "received #{edges.class.name} instead."
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) > 0
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) > 0
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.key?(:capacity) && e[:capacity] != inf ? e[:capacity] : 0)
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] = Hash[uv_edges.map do |k, e|
102
- [k, u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity]]
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!(Hash[uv_edges.map { |_, val| [val[:temp_key][0], val[:flow]] if val[:flow] > 0 }])
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] = Hash[graph.adj[u].map do |v, e|
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] > 0 }
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 ** Math.log2(wmax).floor
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] < 0
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.key?(v)
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 !t.nil?
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.key?(v)
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.key?(v) && uv_attrs[:flow] < uv_attrs[:capacity]
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.key?(v)
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.key?(source)
87
- raise ArgumentError, 'Target not in graph!' unless graph.nodes.key?(target)
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[:flow_val] = edmondskarp_core(res_graph, source, target, cutoff)
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