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,21 +1,24 @@
|
|
1
|
-
# TODO: Reduce module length
|
2
|
-
|
3
1
|
module NetworkX
|
4
2
|
# Helper function to return an arbitrary element from an iterable object
|
5
3
|
def self.arbitrary_element(iterable)
|
6
|
-
iterable.
|
4
|
+
if iterable.is_a?(Hash)
|
5
|
+
iterable.first[0]
|
6
|
+
elsif iterable.respond_to?(:first)
|
7
|
+
iterable.first
|
8
|
+
elsif iterable.respond_to?(:[])
|
9
|
+
iterable[0]
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
|
-
# TODO: Reduce method complexity and method length
|
10
|
-
|
11
13
|
# Helper function to apply the preflow push algorithm
|
12
14
|
def self.preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only)
|
13
|
-
raise ArgumentError, 'Source not in graph!' unless graph.nodes.
|
14
|
-
raise ArgumentError, 'Target not in graph!' unless graph.nodes.
|
15
|
+
raise ArgumentError, 'Source not in graph!' unless graph.nodes.has_key?(source)
|
16
|
+
raise ArgumentError, 'Target not in graph!' unless graph.nodes.has_key?(target)
|
15
17
|
raise ArgumentError, 'Source and Target are same!' if source == target
|
16
18
|
|
17
19
|
globalrelabel_freq = 0 if globalrelabel_freq.nil?
|
18
|
-
raise ArgumentError, 'Global Relabel Freq must be nonnegative!' if globalrelabel_freq
|
20
|
+
raise ArgumentError, 'Global Relabel Freq must be nonnegative!' if globalrelabel_freq.negative?
|
21
|
+
|
19
22
|
r_network = residual.nil? ? build_residual_network(graph) : residual
|
20
23
|
detect_unboundedness(r_network, source, target)
|
21
24
|
|
@@ -30,7 +33,7 @@ module NetworkX
|
|
30
33
|
|
31
34
|
heights = reverse_bfs(target, residual_pred)
|
32
35
|
|
33
|
-
unless heights.
|
36
|
+
unless heights.has_key?(source)
|
34
37
|
r_network.graph[:flow_value] = 0
|
35
38
|
return r_network
|
36
39
|
end
|
@@ -42,25 +45,25 @@ module NetworkX
|
|
42
45
|
grt = GlobalRelabelThreshold.new(n, r_network.size, globalrelabel_freq)
|
43
46
|
|
44
47
|
residual_nodes.each do |u, u_attrs|
|
45
|
-
u_attrs[:height] = heights.
|
48
|
+
u_attrs[:height] = heights.has_key?(u) ? heights[u] : (n + 1)
|
46
49
|
u_attrs[:curr_edge] = CurrentEdge.new(residual_adj[u])
|
47
50
|
end
|
48
51
|
|
49
52
|
residual_adj[source].each do |u, attr|
|
50
53
|
flow = attr[:capacity]
|
51
|
-
push(source, u, flow, residual_nodes, residual_adj) if flow
|
54
|
+
push(source, u, flow, residual_nodes, residual_adj) if flow.positive?
|
52
55
|
end
|
53
56
|
|
54
|
-
levels = (0..(2 * n - 1)).map { |_| Level.new }
|
57
|
+
levels = (0..((2 * n) - 1)).map { |_| Level.new }
|
55
58
|
residual_nodes.each do |u, attr|
|
56
59
|
if u != source && u != target
|
57
60
|
level = levels[attr[:height]]
|
58
|
-
residual_nodes[u][:excess]
|
61
|
+
(residual_nodes[u][:excess]).positive? ? level.active.add(u) : level.inactive.add(u)
|
59
62
|
end
|
60
63
|
end
|
61
64
|
|
62
65
|
height = max_height
|
63
|
-
while height
|
66
|
+
while height.positive?
|
64
67
|
loop do
|
65
68
|
level = levels[height]
|
66
69
|
if level.active.empty?
|
@@ -116,6 +119,7 @@ module NetworkX
|
|
116
119
|
def self.activate(node, source, target, levels, residual_nodes)
|
117
120
|
return if node == source || node == target
|
118
121
|
return unless level.inactive.include?(node)
|
122
|
+
|
119
123
|
level = levels[residual_nodes[node][:height]]
|
120
124
|
level.inactive.delete(node)
|
121
125
|
level.active.add(node)
|
@@ -127,10 +131,8 @@ module NetworkX
|
|
127
131
|
r_adj[u_node].map { |v, attr| attr[:flow] < (attr[:capacity] + 1) ? _nodes[v][:height] : Float::INFINITY }.min
|
128
132
|
end
|
129
133
|
|
130
|
-
# TODO: Reduce method complexity and method length
|
131
|
-
|
132
134
|
# Helper function for discharging a node
|
133
|
-
def self.discharge(u_node,
|
135
|
+
def self.discharge(u_node, is_phase1, residual_nodes, residual_adj, height, levels, grt, source, target)
|
134
136
|
height_val = residual_nodes[u_node][:height]
|
135
137
|
curr_edge = residual_nodes[u_node][:curr_edge]
|
136
138
|
next_height = height_val
|
@@ -151,7 +153,7 @@ module NetworkX
|
|
151
153
|
curr_edge.move_to_next
|
152
154
|
rescue StopIteration
|
153
155
|
height_val = relabel(u_node, grt, residual_adj, residual_nodes, source, target, levels)
|
154
|
-
if
|
156
|
+
if is_phase1 && height_val >= n - 1
|
155
157
|
levels[height].active.add(u_node)
|
156
158
|
break
|
157
159
|
end
|
@@ -162,8 +164,6 @@ module NetworkX
|
|
162
164
|
next_height
|
163
165
|
end
|
164
166
|
|
165
|
-
# TODO: Reduce method complexity
|
166
|
-
|
167
167
|
# Helper function for applying gap heuristic
|
168
168
|
def self.gap_heuristic(height, levels, residual_nodes)
|
169
169
|
((height + 1)..(max_height)).each do |idx|
|
@@ -177,8 +177,6 @@ module NetworkX
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
-
# TODO: Reduce method complexity and method length
|
181
|
-
|
182
180
|
# Helper function for global relabel heuristic
|
183
181
|
def self.global_relabel(from_sink, source, target, residual_nodes, num, levels, residual_pred)
|
184
182
|
src = from_sink ? target : source
|
@@ -186,7 +184,7 @@ module NetworkX
|
|
186
184
|
heights.delete(target) unless from_sink
|
187
185
|
max_height = heights.values.max
|
188
186
|
if from_sink
|
189
|
-
residual_nodes.each { |u, attr| heights[u] = num + 1 if !heights.
|
187
|
+
residual_nodes.each { |u, attr| heights[u] = num + 1 if !heights.has_key?(u) && attr[:height] < num }
|
190
188
|
else
|
191
189
|
heights.each_key { |u| heights[u] += num }
|
192
190
|
max_height += num
|
@@ -195,6 +193,7 @@ module NetworkX
|
|
195
193
|
heights.each do |u, new_height|
|
196
194
|
old_height = residual_nodes[u][:height]
|
197
195
|
next unless new_height != old_height
|
196
|
+
|
198
197
|
if levels[old_height].active.include?(u)
|
199
198
|
levels[old_height].active.delete(u)
|
200
199
|
levels[new_height].active.add(u)
|
@@ -208,15 +207,13 @@ module NetworkX
|
|
208
207
|
end
|
209
208
|
|
210
209
|
# Helper function for augmenting flow
|
211
|
-
def self.push(
|
212
|
-
residual_adj[
|
213
|
-
residual_adj[
|
214
|
-
residual_nodes[
|
215
|
-
residual_nodes[
|
210
|
+
def self.push(node1, node2, flow, residual_nodes, residual_adj)
|
211
|
+
residual_adj[node1][node2][:flow] += flow
|
212
|
+
residual_adj[node2][node1][:flow] -= flow
|
213
|
+
residual_nodes[node1][:excess] -= flow
|
214
|
+
residual_nodes[node2][:excess] += flow
|
216
215
|
end
|
217
216
|
|
218
|
-
# TODO: Reduce method length
|
219
|
-
|
220
217
|
# Helper function for reverse bfs
|
221
218
|
def self.reverse_bfs(src, residual_pred)
|
222
219
|
heights = {src => 0}
|
@@ -226,7 +223,7 @@ module NetworkX
|
|
226
223
|
u, height = q.shift
|
227
224
|
height += 1
|
228
225
|
residual_pred[u].each do |v, attr|
|
229
|
-
if !heights.
|
226
|
+
if !heights.has_key?(v) && attr[:flow] < attr[:capacity]
|
230
227
|
heights[v] = height
|
231
228
|
q << [v, height]
|
232
229
|
end
|
@@ -246,7 +243,7 @@ module NetworkX
|
|
246
243
|
# maximum preflow
|
247
244
|
#
|
248
245
|
# @return [DiGraph] a residual graph containing the flow values
|
249
|
-
def self.preflowpush(graph, source, target, residual=nil, globalrelabel_freq=1, value_only=false)
|
246
|
+
def self.preflowpush(graph, source, target, residual = nil, globalrelabel_freq = 1, value_only = false)
|
250
247
|
preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only)
|
251
248
|
end
|
252
249
|
end
|
@@ -1,12 +1,8 @@
|
|
1
|
-
# TODO: Reduce module length
|
2
|
-
|
3
1
|
module NetworkX
|
4
|
-
# TODO: Reduce method complexity and method length
|
5
|
-
|
6
2
|
# Helper function for running the shortest augmenting path algorithm
|
7
3
|
def self.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
|
8
|
-
raise ArgumentError, 'Source is not in the graph!' unless graph.nodes.
|
9
|
-
raise ArgumentError, 'Target is not in the graph!' unless graph.nodes.
|
4
|
+
raise ArgumentError, 'Source is not in the graph!' unless graph.nodes.has_key?(source)
|
5
|
+
raise ArgumentError, 'Target is not in the graph!' unless graph.nodes.has_key?(target)
|
10
6
|
raise ArgumentError, 'Source and Target are the same!' if source == target
|
11
7
|
|
12
8
|
residual = residual.nil? ? build_residual_network(graph) : residual
|
@@ -27,14 +23,14 @@ module NetworkX
|
|
27
23
|
u, height = q.shift
|
28
24
|
height += 1
|
29
25
|
r_pred[u].each do |v, attrs|
|
30
|
-
if !heights.
|
26
|
+
if !heights.has_key?(v) && attrs[:flow] < attrs[:capacity]
|
31
27
|
heights[v] = height
|
32
28
|
q << [v, height]
|
33
29
|
end
|
34
30
|
end
|
35
31
|
end
|
36
32
|
|
37
|
-
unless heights.
|
33
|
+
unless heights.has_key?(source)
|
38
34
|
residual.graph[:flow_value] = 0
|
39
35
|
return residual
|
40
36
|
end
|
@@ -43,12 +39,11 @@ module NetworkX
|
|
43
39
|
m = residual.size / 2
|
44
40
|
|
45
41
|
r_nodes.each do |node, attrs|
|
46
|
-
attrs[:height] = heights.
|
42
|
+
attrs[:height] = heights.has_key?(node) ? heights[node] : n
|
47
43
|
attrs[:curr_edge] = CurrentEdge.new(r_adj[node])
|
48
44
|
end
|
49
45
|
|
50
|
-
counts =
|
51
|
-
counts.fill(0)
|
46
|
+
counts = [0] * (2 * n - 1)
|
52
47
|
r_nodes.each_value { |attrs| counts[attrs[:height]] += 1 }
|
53
48
|
inf = graph.graph[:inf]
|
54
49
|
|
@@ -56,7 +51,7 @@ module NetworkX
|
|
56
51
|
flow_value = 0
|
57
52
|
path = [source]
|
58
53
|
u = source
|
59
|
-
d = two_phase ? n : [m
|
54
|
+
d = two_phase ? n : [m**0.5, 2 * (n**(2./ 3))].min.floor
|
60
55
|
done = r_nodes[source][:height] >= d
|
61
56
|
|
62
57
|
until done
|
@@ -79,12 +74,12 @@ module NetworkX
|
|
79
74
|
end
|
80
75
|
height = relabel(u, n, r_adj, r_nodes)
|
81
76
|
if u == source && height >= d
|
82
|
-
if
|
83
|
-
residual.graph[:flow_value] = flow_value
|
84
|
-
return residual
|
85
|
-
else
|
77
|
+
if two_phase
|
86
78
|
done = true
|
87
79
|
break
|
80
|
+
else
|
81
|
+
residual.graph[:flow_value] = flow_value
|
82
|
+
return residual
|
88
83
|
end
|
89
84
|
end
|
90
85
|
counts[height] += 1
|
@@ -97,6 +92,7 @@ module NetworkX
|
|
97
92
|
end
|
98
93
|
end
|
99
94
|
next unless u == target
|
95
|
+
|
100
96
|
flow_value += augment(path, inf, r_adj)
|
101
97
|
if flow_value >= cutoff
|
102
98
|
residual.graph[:flow_value] = flow_value
|
@@ -108,8 +104,6 @@ module NetworkX
|
|
108
104
|
residual
|
109
105
|
end
|
110
106
|
|
111
|
-
# TODO: Reduce method complexity and method length
|
112
|
-
|
113
107
|
# Helper function for augmenting flow
|
114
108
|
def augment(path, inf, r_adj)
|
115
109
|
flow = inf
|
@@ -121,6 +115,7 @@ module NetworkX
|
|
121
115
|
u = v
|
122
116
|
end
|
123
117
|
raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf
|
118
|
+
|
124
119
|
temp_path = path.clone
|
125
120
|
u = temp_path.shift
|
126
121
|
temp_path.each do |v|
|
@@ -146,12 +141,14 @@ module NetworkX
|
|
146
141
|
# @param source [Object] source node
|
147
142
|
# @param target [Object] target node
|
148
143
|
# @param residual [DiGraph, nil] residual graph
|
149
|
-
# @param
|
144
|
+
# @param _value_only [Boolean] if true, compute only the maximum flow value
|
150
145
|
# @param two_phase [Boolean] if true, two phase variant is used
|
151
146
|
# @param cutoff [Numeric] cutoff value for the algorithm
|
152
147
|
#
|
153
148
|
# @return [DiGraph] a residual graph containing the flow values
|
154
|
-
def self.shortest_augmenting_path(graph, source, target, residual
|
149
|
+
def self.shortest_augmenting_path(graph, source, target, residual = nil, \
|
150
|
+
_value_only = false, two_phase = false, cutoff = nil)
|
151
|
+
|
155
152
|
shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
|
156
153
|
end
|
157
154
|
end
|
data/lib/networkx/flow/utils.rb
CHANGED
@@ -34,9 +34,9 @@ module NetworkX
|
|
34
34
|
|
35
35
|
# Helper class for preflow push algorithm
|
36
36
|
class GlobalRelabelThreshold
|
37
|
-
def initialize(
|
37
|
+
def initialize(num1, num2, freq)
|
38
38
|
freq = freq.nil? ? Float::INFINITY : freq
|
39
|
-
@threshold = (
|
39
|
+
@threshold = (num1 + num2) / freq
|
40
40
|
@work = 0
|
41
41
|
end
|
42
42
|
|
@@ -53,8 +53,6 @@ module NetworkX
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
# TODO: Reduce method complexity and method length
|
57
|
-
|
58
56
|
# Builds a residual graph from a constituent graph
|
59
57
|
#
|
60
58
|
# @param graph [DiGraph] a graph
|
@@ -69,31 +67,13 @@ module NetworkX
|
|
69
67
|
edge_list = []
|
70
68
|
|
71
69
|
graph.adj.each do |u, u_edges|
|
72
|
-
require 'spec_helper'
|
73
|
-
RSpec.describe NetworkX::DiGraph do
|
74
|
-
subject { graph }
|
75
|
-
|
76
|
-
let(:graph) { described_class.new }
|
77
|
-
|
78
|
-
before do
|
79
|
-
graph.add_edge(1, 2)
|
80
|
-
graph.add_edge(2, 4)
|
81
|
-
end
|
82
|
-
|
83
|
-
context 'when capacity_scaling is called' do
|
84
|
-
subject { NetworkX.capacity_scaling(graph) }
|
85
|
-
|
86
|
-
it { is_expected.to eq([0, {1=>{2=>0}, 2=>{4=>0}, 4=>{}}]) }
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
70
|
u_edges.each do |v, uv_attrs|
|
91
|
-
edge_list << [u, v, uv_attrs] if (uv_attrs[:capacity] || inf)
|
71
|
+
edge_list << [u, v, uv_attrs] if (uv_attrs[:capacity] || inf).positive? && u != v
|
92
72
|
end
|
93
73
|
end
|
94
74
|
|
95
75
|
inf_chk = 3 * edge_list.inject(0) do |result, arr|
|
96
|
-
arr[2].
|
76
|
+
arr[2].has_key?(:capacity) && arr[2][:capacity] != inf ? (result + arr[2][:capacity]) : result
|
97
77
|
end
|
98
78
|
inf = inf_chk.zero? ? 1 : inf_chk
|
99
79
|
|
@@ -118,8 +98,6 @@ module NetworkX
|
|
118
98
|
r_network
|
119
99
|
end
|
120
100
|
|
121
|
-
# TODO: Reduce method complexity and method length
|
122
|
-
|
123
101
|
# Detects unboundedness in a graph, raises exception when
|
124
102
|
# infinite capacity flow is found
|
125
103
|
#
|
@@ -135,6 +113,7 @@ module NetworkX
|
|
135
113
|
r_network.adj[u].each do |v, uv_attrs|
|
136
114
|
next unless uv_attrs[:capacity] == inf && !seen.include?(v)
|
137
115
|
raise ArgumentError, 'Infinite capacity flow!' if v == target
|
116
|
+
|
138
117
|
seen << v
|
139
118
|
q << v
|
140
119
|
end
|
@@ -153,7 +132,7 @@ module NetworkX
|
|
153
132
|
graph.edges.each do |u, u_edges|
|
154
133
|
flow_dict[u] = {}
|
155
134
|
u_edges.each_key { |v| flow_dict[u][v] = 0 }
|
156
|
-
u_edges.each_key { |v| flow_dict[u][v] = residual[u][v][:flow] if residual[u][v][:flow]
|
135
|
+
u_edges.each_key { |v| flow_dict[u][v] = residual[u][v][:flow] if (residual[u][v][:flow]).positive? }
|
157
136
|
end
|
158
137
|
flow_dict
|
159
138
|
end
|