networkx 0.1.0 → 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} +21 -11
- data/.github/ISSUE_TEMPLATE.md +15 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +10 -0
- 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 +56 -63
- data/.yardopts +0 -1
- data/README.md +27 -27
- data/Rakefile +2 -3
- data/lib/networkx/auxillary_functions/cliques.rb +62 -0
- data/lib/networkx/auxillary_functions/cycles.rb +114 -0
- data/lib/networkx/auxillary_functions/dag.rb +59 -0
- data/lib/networkx/auxillary_functions/eccentricity.rb +37 -0
- data/lib/networkx/auxillary_functions/mis.rb +23 -0
- data/lib/networkx/auxillary_functions/mst.rb +33 -0
- data/lib/networkx/auxillary_functions/union_find.rb +104 -0
- data/lib/networkx/auxillary_functions/vitality.rb +13 -0
- data/lib/networkx/auxillary_functions/wiener.rb +13 -0
- data/lib/networkx/converters/to_csv.rb +45 -0
- data/lib/networkx/converters/to_json.rb +37 -0
- data/lib/networkx/digraph.rb +234 -0
- data/lib/networkx/flow/capacityscaling.rb +249 -0
- data/lib/networkx/flow/edmondskarp.rb +115 -0
- data/lib/networkx/flow/preflowpush.rb +249 -0
- data/lib/networkx/flow/shortestaugmentingpath.rb +154 -0
- data/lib/networkx/flow/utils.rb +139 -0
- data/lib/networkx/graph.rb +448 -0
- data/lib/networkx/link_analysis/hits.rb +59 -0
- data/lib/networkx/link_analysis/pagerank.rb +89 -0
- data/lib/networkx/multidigraph.rb +249 -0
- data/lib/networkx/multigraph.rb +199 -0
- data/lib/networkx/operators/all.rb +65 -0
- data/lib/networkx/operators/binary.rb +222 -0
- data/lib/networkx/operators/product.rb +201 -0
- data/lib/networkx/operators/unary.rb +17 -0
- 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 +73 -0
- data/lib/networkx/shortest_path/dense.rb +29 -0
- data/lib/networkx/shortest_path/unweighted.rb +136 -0
- data/lib/networkx/shortest_path/weighted.rb +417 -0
- data/lib/networkx/to_matrix.rb +51 -0
- data/lib/networkx/traversals/bfs.rb +110 -0
- data/lib/networkx/traversals/dfs.rb +135 -0
- data/lib/networkx/traversals/edge_dfs.rb +114 -0
- data/lib/networkx/version.rb +1 -1
- data/lib/networkx.rb +43 -1
- data/networkx.gemspec +14 -12
- metadata +118 -62
- data/.rspec_formatter.rb +0 -24
- data/.travis.yml +0 -18
- data/Guardfile +0 -7
@@ -0,0 +1,154 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Helper function for running the shortest augmenting path algorithm
|
3
|
+
def self.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
|
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)
|
6
|
+
raise ArgumentError, 'Source and Target are the same!' if source == target
|
7
|
+
|
8
|
+
residual = residual.nil? ? build_residual_network(graph) : residual
|
9
|
+
r_nodes = residual.nodes
|
10
|
+
r_pred = residual.pred
|
11
|
+
r_adj = residual.adj
|
12
|
+
|
13
|
+
r_adj.each_value do |u_edges|
|
14
|
+
u_edges.each_value do |attrs|
|
15
|
+
attrs[:flow] = 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
heights = {target => 0}
|
20
|
+
q = [[target, 0]]
|
21
|
+
|
22
|
+
until q.empty?
|
23
|
+
u, height = q.shift
|
24
|
+
height += 1
|
25
|
+
r_pred[u].each do |v, attrs|
|
26
|
+
if !heights.has_key?(v) && attrs[:flow] < attrs[:capacity]
|
27
|
+
heights[v] = height
|
28
|
+
q << [v, height]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
unless heights.has_key?(source)
|
34
|
+
residual.graph[:flow_value] = 0
|
35
|
+
return residual
|
36
|
+
end
|
37
|
+
|
38
|
+
n = graph.nodes.length
|
39
|
+
m = residual.size / 2
|
40
|
+
|
41
|
+
r_nodes.each do |node, attrs|
|
42
|
+
attrs[:height] = heights.has_key?(node) ? heights[node] : n
|
43
|
+
attrs[:curr_edge] = CurrentEdge.new(r_adj[node])
|
44
|
+
end
|
45
|
+
|
46
|
+
counts = [0] * (2 * n - 1)
|
47
|
+
r_nodes.each_value { |attrs| counts[attrs[:height]] += 1 }
|
48
|
+
inf = graph.graph[:inf]
|
49
|
+
|
50
|
+
cutoff = Float::INFINITY if cutoff.nil?
|
51
|
+
flow_value = 0
|
52
|
+
path = [source]
|
53
|
+
u = source
|
54
|
+
d = two_phase ? n : [m**0.5, 2 * (n**(2./ 3))].min.floor
|
55
|
+
done = r_nodes[source][:height] >= d
|
56
|
+
|
57
|
+
until done
|
58
|
+
height = r_nodes[u][:height]
|
59
|
+
curr_edge = r_nodes[u][:curr_edge]
|
60
|
+
|
61
|
+
loop do
|
62
|
+
v, attr = curr_edge.get
|
63
|
+
if height == r_nodes[v][:height] + 1 && attr[:flow] < attr[:capacity]
|
64
|
+
path << v
|
65
|
+
u = v
|
66
|
+
break
|
67
|
+
end
|
68
|
+
begin
|
69
|
+
curr_edge.move_to_next
|
70
|
+
rescue StopIteration
|
71
|
+
if counts[height].zero?
|
72
|
+
residual.graph[:flow_value] = flow_value
|
73
|
+
return residual
|
74
|
+
end
|
75
|
+
height = relabel(u, n, r_adj, r_nodes)
|
76
|
+
if u == source && height >= d
|
77
|
+
if two_phase
|
78
|
+
done = true
|
79
|
+
break
|
80
|
+
else
|
81
|
+
residual.graph[:flow_value] = flow_value
|
82
|
+
return residual
|
83
|
+
end
|
84
|
+
end
|
85
|
+
counts[height] += 1
|
86
|
+
r_nodes[u][:height] = height
|
87
|
+
unless u == source
|
88
|
+
path.pop
|
89
|
+
u = path[-1]
|
90
|
+
break
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
next unless u == target
|
95
|
+
|
96
|
+
flow_value += augment(path, inf, r_adj)
|
97
|
+
if flow_value >= cutoff
|
98
|
+
residual.graph[:flow_value] = flow_value
|
99
|
+
return residual
|
100
|
+
end
|
101
|
+
end
|
102
|
+
flow_value += edmondskarp_core(residual, source, target, cutoff - flow_value)
|
103
|
+
residual.graph[:flow_value] = flow_value
|
104
|
+
residual
|
105
|
+
end
|
106
|
+
|
107
|
+
# Helper function for augmenting flow
|
108
|
+
def augment(path, inf, r_adj)
|
109
|
+
flow = inf
|
110
|
+
temp_path = path.clone
|
111
|
+
u = temp_path.shift
|
112
|
+
temp_path.each do |v|
|
113
|
+
attr = r_adj[u][v]
|
114
|
+
flow = [flow, attr[:capacity] - attr[:flow]].min
|
115
|
+
u = v
|
116
|
+
end
|
117
|
+
raise ArgumentError, 'Infinite capacity path!' if flow * 2 > inf
|
118
|
+
|
119
|
+
temp_path = path.clone
|
120
|
+
u = temp_path.shift
|
121
|
+
temp_path.each do |v|
|
122
|
+
r_adj[u][v][:flow] += flow
|
123
|
+
r_adj[v][u][:flow] -= flow
|
124
|
+
u = v
|
125
|
+
end
|
126
|
+
flow
|
127
|
+
end
|
128
|
+
|
129
|
+
# Helper function to relable a node to create a permissible edge
|
130
|
+
def self.relabel(node, num, r_adj, r_nodes)
|
131
|
+
height = num - 1
|
132
|
+
r_adj[node].each do |v, attrs|
|
133
|
+
height = [height, r_nodes[v][:height]].min if attrs[:flow] < attrs[:capacity]
|
134
|
+
end
|
135
|
+
height + 1
|
136
|
+
end
|
137
|
+
|
138
|
+
# Computes max flow using shortest augmenting path algorithm
|
139
|
+
#
|
140
|
+
# @param graph [DiGraph] a graph
|
141
|
+
# @param source [Object] source node
|
142
|
+
# @param target [Object] target node
|
143
|
+
# @param residual [DiGraph, nil] residual graph
|
144
|
+
# @param _value_only [Boolean] if true, compute only the maximum flow value
|
145
|
+
# @param two_phase [Boolean] if true, two phase variant is used
|
146
|
+
# @param cutoff [Numeric] cutoff value for the algorithm
|
147
|
+
#
|
148
|
+
# @return [DiGraph] a residual graph containing the flow values
|
149
|
+
def self.shortest_augmenting_path(graph, source, target, residual = nil, \
|
150
|
+
_value_only = false, two_phase = false, cutoff = nil)
|
151
|
+
|
152
|
+
shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Helper class for preflow push algorithm
|
3
|
+
class CurrentEdge
|
4
|
+
attr_reader :curr, :edges
|
5
|
+
|
6
|
+
def initialize(edges)
|
7
|
+
@edges = edges
|
8
|
+
@index = {}
|
9
|
+
@n = edges.length
|
10
|
+
@curr = 0
|
11
|
+
edges.each_with_index { |(key, _value), idx| @index[idx] = key }
|
12
|
+
end
|
13
|
+
|
14
|
+
def get
|
15
|
+
[@index[@curr], @edges[@index[@curr]]]
|
16
|
+
end
|
17
|
+
|
18
|
+
def move_to_next
|
19
|
+
@temp = @curr
|
20
|
+
@curr = (@curr + 1) % @n
|
21
|
+
raise StopIteration if @temp == @n - 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Helper class for preflow push algorithm
|
26
|
+
class Level
|
27
|
+
attr_reader :inactive, :active
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@inactive = Set.new
|
31
|
+
@active = Set.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Helper class for preflow push algorithm
|
36
|
+
class GlobalRelabelThreshold
|
37
|
+
def initialize(num1, num2, freq)
|
38
|
+
freq = freq.nil? ? Float::INFINITY : freq
|
39
|
+
@threshold = (num1 + num2) / freq
|
40
|
+
@work = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_work(work)
|
44
|
+
@work += work
|
45
|
+
end
|
46
|
+
|
47
|
+
def reached?
|
48
|
+
@work >= @threshold
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear_work
|
52
|
+
@work = 0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Builds a residual graph from a constituent graph
|
57
|
+
#
|
58
|
+
# @param graph [DiGraph] a graph
|
59
|
+
#
|
60
|
+
# @return [DiGraph] residual graph
|
61
|
+
def self.build_residual_network(graph)
|
62
|
+
raise NotImplementedError, 'MultiGraph and MultiDiGraph not supported!' if graph.multigraph?
|
63
|
+
|
64
|
+
r_network = NetworkX::DiGraph.new(inf: 0, flow_value: 0)
|
65
|
+
r_network.add_nodes(graph.nodes.keys)
|
66
|
+
inf = Float::INFINITY
|
67
|
+
edge_list = []
|
68
|
+
|
69
|
+
graph.adj.each do |u, u_edges|
|
70
|
+
u_edges.each do |v, uv_attrs|
|
71
|
+
edge_list << [u, v, uv_attrs] if (uv_attrs[:capacity] || inf).positive? && u != v
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
inf_chk = 3 * edge_list.inject(0) do |result, arr|
|
76
|
+
arr[2].has_key?(:capacity) && arr[2][:capacity] != inf ? (result + arr[2][:capacity]) : result
|
77
|
+
end
|
78
|
+
inf = inf_chk.zero? ? 1 : inf_chk
|
79
|
+
|
80
|
+
if graph.directed?
|
81
|
+
edge_list.each do |u, v, attrs|
|
82
|
+
r = [attrs[:capacity] || inf, inf].min
|
83
|
+
if r_network.adj[u][v].nil?
|
84
|
+
r_network.add_edge(u, v, capacity: r)
|
85
|
+
r_network.add_edge(v, u, capacity: 0)
|
86
|
+
else
|
87
|
+
r_network[u][v][:capacity] = r
|
88
|
+
end
|
89
|
+
end
|
90
|
+
else
|
91
|
+
edge_list.each do |u, v, attrs|
|
92
|
+
r = [attrs[:capacity] || inf, inf].min
|
93
|
+
r_network.add_edge(u, v, capacity: r)
|
94
|
+
r_network.add_edge(v, u, capacity: r)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
r_network.graph[:inf] = inf
|
98
|
+
r_network
|
99
|
+
end
|
100
|
+
|
101
|
+
# Detects unboundedness in a graph, raises exception when
|
102
|
+
# infinite capacity flow is found
|
103
|
+
#
|
104
|
+
# @param r_network [DiGraph] a residual graph
|
105
|
+
# @param source [Object] source node
|
106
|
+
# @param target [Object] target node
|
107
|
+
def self.detect_unboundedness(r_network, source, target)
|
108
|
+
q = [source]
|
109
|
+
seen = Set.new([source])
|
110
|
+
inf = r_network.graph[:inf]
|
111
|
+
until q.empty?
|
112
|
+
u = q.shift
|
113
|
+
r_network.adj[u].each do |v, uv_attrs|
|
114
|
+
next unless uv_attrs[:capacity] == inf && !seen.include?(v)
|
115
|
+
raise ArgumentError, 'Infinite capacity flow!' if v == target
|
116
|
+
|
117
|
+
seen << v
|
118
|
+
q << v
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Build flow dictionary of a graph from its residual graph
|
124
|
+
#
|
125
|
+
# @param graph [DiGraph] a graph
|
126
|
+
# @param residual [DiGraph] residual graph
|
127
|
+
#
|
128
|
+
# @return [Hash{ Object => Hash{ Object => Numeric }] flowdict containing all
|
129
|
+
# the flow values in the edges
|
130
|
+
def self.build_flow_dict(graph, residual)
|
131
|
+
flow_dict = {}
|
132
|
+
graph.edges.each do |u, u_edges|
|
133
|
+
flow_dict[u] = {}
|
134
|
+
u_edges.each_key { |v| flow_dict[u][v] = 0 }
|
135
|
+
u_edges.each_key { |v| flow_dict[u][v] = residual[u][v][:flow] if (residual[u][v][:flow]).positive? }
|
136
|
+
end
|
137
|
+
flow_dict
|
138
|
+
end
|
139
|
+
end
|