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,227 +1,205 @@
|
|
1
|
-
# TODO: Reduce module length
|
2
|
-
|
3
1
|
module NetworkX
|
4
2
|
# Returns the edges of the graph in an array
|
5
3
|
def self.get_edges(graph)
|
6
4
|
edges = []
|
7
|
-
graph.
|
8
|
-
|
9
|
-
|
5
|
+
if graph.is_a?(MultiGraph)
|
6
|
+
graph.adj.each do |u, v_keys|
|
7
|
+
v_keys.each do |v, key_attrs|
|
8
|
+
next if u > v
|
9
|
+
|
10
|
+
key_attrs.each do |_key, attributes|
|
11
|
+
edges << [u, v, attributes]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
else
|
16
|
+
graph.adj.each do |u, u_attrs|
|
17
|
+
u_attrs.each do |v, uv_attrs|
|
18
|
+
edges << [u, v, uv_attrs]
|
19
|
+
end
|
10
20
|
end
|
11
21
|
end
|
12
22
|
edges
|
13
23
|
end
|
14
24
|
|
15
|
-
# TODO: Reduce method complexity and method length
|
16
|
-
|
17
25
|
# Transforms the labels of the nodes of the graphs
|
18
26
|
# so that they are disjoint.
|
19
|
-
def self.convert_to_distinct_labels(graph, starting_int
|
20
|
-
new_graph =
|
21
|
-
new_graph.clear
|
27
|
+
def self.convert_to_distinct_labels(graph, starting_int = -1)
|
28
|
+
new_graph = graph.class.new
|
22
29
|
|
23
|
-
idx_dict =
|
30
|
+
idx_dict = graph.nodes.keys.to_h do |v|
|
24
31
|
starting_int += 1
|
25
32
|
[v, starting_int]
|
26
|
-
end
|
33
|
+
end
|
27
34
|
|
28
35
|
graph.nodes.each do |u, attrs|
|
29
|
-
new_graph.add_node(u.to_s + idx_dict[u].to_s, attrs)
|
36
|
+
new_graph.add_node(u.to_s + idx_dict[u].to_s, **attrs)
|
30
37
|
end
|
31
38
|
|
32
39
|
graph.adj.each do |u, u_edges|
|
33
40
|
u_edges.each do |v, uv_attrs|
|
34
41
|
if graph.multigraph?
|
35
42
|
uv_attrs.each do |_k, attrs|
|
36
|
-
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, attrs)
|
43
|
+
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, **attrs)
|
37
44
|
end
|
38
45
|
else
|
39
|
-
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, uv_attrs)
|
46
|
+
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, **uv_attrs)
|
40
47
|
end
|
41
48
|
end
|
42
49
|
end
|
43
50
|
new_graph
|
44
51
|
end
|
45
52
|
|
46
|
-
# TODO: Reduce method complexity and method length
|
47
|
-
|
48
53
|
# Performs the intersection of two graphs
|
49
54
|
#
|
50
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
51
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
55
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
56
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
52
57
|
#
|
53
58
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the intersection of the two graphs
|
54
|
-
def self.intersection(
|
55
|
-
result =
|
56
|
-
|
57
|
-
|
58
|
-
raise ArgumentError, '
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
elsif g_2.edge?(u, v)
|
71
|
-
result.add_edge(u, v, uv_attrs)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
else
|
76
|
-
g_2.adj.each do |u, u_edges|
|
77
|
-
u_edges.each do |v, uv_attrs|
|
78
|
-
if g_2.multigraph?
|
79
|
-
uv_attrs.each do |k, attrs|
|
80
|
-
result.add_edge(u, v, attrs) if g_1.edge?(u, v, k)
|
81
|
-
end
|
82
|
-
elsif g_1.edge?(u, v)
|
83
|
-
result.add_edge(u, v, uv_attrs)
|
59
|
+
def self.intersection(g1, g2)
|
60
|
+
result = g1.class.new
|
61
|
+
|
62
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
63
|
+
raise ArgumentError, 'Node sets must be equal!' unless (g1.nodes.keys - g2.nodes.keys).empty?
|
64
|
+
|
65
|
+
g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
|
66
|
+
|
67
|
+
g1, g2 = g2, g1 if g1.number_of_edges > g2.number_of_edges
|
68
|
+
g1.adj.each do |u, u_edges|
|
69
|
+
u_edges.each do |v, uv_attrs|
|
70
|
+
if g1.multigraph?
|
71
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
72
|
+
|
73
|
+
uv_attrs.each do |k, attrs|
|
74
|
+
result.add_edge(u, v, **attrs) if g2.edge?(u, v, k)
|
84
75
|
end
|
76
|
+
elsif g2.edge?(u, v)
|
77
|
+
result.add_edge(u, v, **uv_attrs)
|
85
78
|
end
|
86
79
|
end
|
87
80
|
end
|
88
81
|
result
|
89
82
|
end
|
90
83
|
|
91
|
-
# TODO: Reduce method complexity and method length
|
92
|
-
|
93
84
|
# Performs the difference of two graphs
|
94
85
|
#
|
95
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
96
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
86
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
87
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
97
88
|
#
|
98
89
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the difference of the two graphs
|
99
|
-
def self.difference(
|
100
|
-
result =
|
101
|
-
result.clear
|
90
|
+
def self.difference(g1, g2)
|
91
|
+
result = g1.class.new
|
102
92
|
|
103
|
-
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless
|
104
|
-
raise ArgumentError, 'Node sets must be equal!' unless (
|
93
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
94
|
+
raise ArgumentError, 'Node sets must be equal!' unless (g1.nodes.keys - g2.nodes.keys).empty?
|
105
95
|
|
106
|
-
|
96
|
+
g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
|
107
97
|
|
108
|
-
|
98
|
+
g1.adj.each do |u, u_edges|
|
109
99
|
u_edges.each do |v, uv_attrs|
|
110
|
-
if
|
100
|
+
if g1.multigraph?
|
101
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
102
|
+
|
111
103
|
uv_attrs.each do |k, attrs|
|
112
|
-
result.add_edge(u, v, attrs) unless
|
104
|
+
result.add_edge(u, v, **attrs) unless g2.edge?(u, v, k)
|
113
105
|
end
|
114
106
|
else
|
115
|
-
result.add_edge(u, v, uv_attrs) unless
|
107
|
+
result.add_edge(u, v, **uv_attrs) unless g2.edge?(u, v)
|
116
108
|
end
|
117
109
|
end
|
118
110
|
end
|
119
111
|
result
|
120
112
|
end
|
121
113
|
|
122
|
-
# TODO: Reduce method complexity and method length
|
123
|
-
|
124
114
|
# Performs the symmetric difference of two graphs
|
125
115
|
#
|
126
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
127
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
116
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
117
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
128
118
|
#
|
129
119
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the symmetric difference of the two graphs
|
130
|
-
def self.symmetric_difference(
|
131
|
-
result =
|
132
|
-
result.clear
|
120
|
+
def self.symmetric_difference(g1, g2)
|
121
|
+
result = g1.class.new
|
133
122
|
|
134
|
-
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless
|
135
|
-
raise ArgumentError, 'Node sets must be equal!' unless (
|
123
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
124
|
+
raise ArgumentError, 'Node sets must be equal!' unless (g1.nodes.keys - g2.nodes.keys).empty?
|
136
125
|
|
137
|
-
|
126
|
+
g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
|
138
127
|
|
139
|
-
|
128
|
+
g1.adj.each do |u, u_edges|
|
140
129
|
u_edges.each do |v, uv_attrs|
|
141
|
-
if
|
130
|
+
if g1.multigraph?
|
131
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
132
|
+
|
142
133
|
uv_attrs.each do |k, attrs|
|
143
|
-
result.add_edge(u, v, attrs) unless
|
134
|
+
result.add_edge(u, v, **attrs) unless g2.edge?(u, v, k)
|
144
135
|
end
|
145
136
|
else
|
146
|
-
result.add_edge(u, v, uv_attrs) unless
|
137
|
+
result.add_edge(u, v, **uv_attrs) unless g2.edge?(u, v)
|
147
138
|
end
|
148
139
|
end
|
149
140
|
end
|
150
141
|
|
151
|
-
|
142
|
+
g2.adj.each do |u, u_edges|
|
152
143
|
u_edges.each do |v, uv_attrs|
|
153
|
-
if
|
144
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
145
|
+
|
146
|
+
if g2.multigraph?
|
154
147
|
uv_attrs.each do |k, attrs|
|
155
|
-
result.add_edge(u, v, attrs) unless
|
148
|
+
result.add_edge(u, v, **attrs) unless g1.edge?(u, v, k)
|
156
149
|
end
|
157
150
|
else
|
158
|
-
result.add_edge(u, v, uv_attrs) unless
|
151
|
+
result.add_edge(u, v, **uv_attrs) unless g1.edge?(u, v)
|
159
152
|
end
|
160
153
|
end
|
161
154
|
end
|
162
155
|
result
|
163
156
|
end
|
164
157
|
|
165
|
-
# TODO: Reduce method complexity and method length
|
166
|
-
|
167
158
|
# Performs the composition of two graphs
|
168
159
|
#
|
169
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
170
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
160
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
161
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
171
162
|
#
|
172
163
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the composition of the two graphs
|
173
|
-
def self.compose(
|
174
|
-
result =
|
175
|
-
result.clear
|
164
|
+
def self.compose(g1, g2)
|
165
|
+
result = g1.class.new
|
176
166
|
|
177
|
-
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless
|
167
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
178
168
|
|
179
|
-
result.add_nodes(
|
180
|
-
result.add_nodes(
|
169
|
+
result.add_nodes(g1.nodes.map { |u, attrs| [u, attrs] })
|
170
|
+
result.add_nodes(g2.nodes.map { |u, attrs| [u, attrs] })
|
181
171
|
|
182
|
-
if
|
183
|
-
|
184
|
-
|
172
|
+
if g1.multigraph?
|
173
|
+
g1.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, **attrs) } } }
|
174
|
+
g2.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, **attrs) } } }
|
185
175
|
else
|
186
|
-
|
187
|
-
|
176
|
+
g1.adj.each { |u, u_edges| u_edges.each { |v, attrs| result.add_edge(u, v, **attrs) } }
|
177
|
+
g2.adj.each { |u, u_edges| u_edges.each { |v, attrs| result.add_edge(u, v, **attrs) } }
|
188
178
|
end
|
189
179
|
result
|
190
180
|
end
|
191
181
|
|
192
|
-
# TODO: Reduce method complexity and method length
|
193
|
-
|
194
182
|
# Performs the union of two graphs
|
195
183
|
#
|
196
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
197
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
184
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
185
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
198
186
|
#
|
199
187
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the union of the two graphs
|
200
|
-
def self.union(
|
201
|
-
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless
|
202
|
-
|
203
|
-
case g_1
|
204
|
-
when NetworkX::MultiGraph
|
205
|
-
new_graph = NetworkX::MultiGraph.new
|
206
|
-
when NetworkX::MultiDiGraph
|
207
|
-
new_graph = NetworkX::MultiDiGraph.new
|
208
|
-
when NetworkX::Graph
|
209
|
-
new_graph = NetworkX::Graph.new
|
210
|
-
when NetworkX::DiGraph
|
211
|
-
new_graph = NetworkX::DiGraph.new
|
212
|
-
end
|
188
|
+
def self.union(g1, g2)
|
189
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
213
190
|
|
214
|
-
new_graph.
|
215
|
-
new_graph.graph.merge!(
|
191
|
+
new_graph = g1.class.new
|
192
|
+
new_graph.graph.merge!(g1.graph)
|
193
|
+
new_graph.graph.merge!(g2.graph)
|
216
194
|
|
217
|
-
raise ArgumentError, 'Graphs must be disjoint!' unless (
|
195
|
+
raise ArgumentError, 'Graphs must be disjoint!' unless (g1.nodes.keys & g2.nodes.keys).empty?
|
218
196
|
|
219
|
-
g1_edges = get_edges(
|
220
|
-
g2_edges = get_edges(
|
197
|
+
g1_edges = get_edges(g1)
|
198
|
+
g2_edges = get_edges(g2)
|
221
199
|
|
222
|
-
new_graph.add_nodes(
|
200
|
+
new_graph.add_nodes(g1.nodes.keys)
|
223
201
|
new_graph.add_edges(g1_edges)
|
224
|
-
new_graph.add_nodes(
|
202
|
+
new_graph.add_nodes(g2.nodes.keys)
|
225
203
|
new_graph.add_edges(g2_edges)
|
226
204
|
|
227
205
|
new_graph
|
@@ -229,16 +207,16 @@ module NetworkX
|
|
229
207
|
|
230
208
|
# Performs the disjoint union of two graphs
|
231
209
|
#
|
232
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
233
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
210
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
211
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
234
212
|
#
|
235
213
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the disjoint union of the two graphs
|
236
|
-
def self.disjoint_union(
|
237
|
-
|
238
|
-
|
239
|
-
result = union(
|
240
|
-
result.graph.merge!(
|
241
|
-
result.graph.merge!(
|
214
|
+
def self.disjoint_union(g1, g2)
|
215
|
+
new_g1 = convert_to_distinct_labels(g1)
|
216
|
+
new_g2 = convert_to_distinct_labels(g2)
|
217
|
+
result = union(new_g1, new_g2)
|
218
|
+
result.graph.merge!(g1.graph)
|
219
|
+
result.graph.merge!(g2.graph)
|
242
220
|
result
|
243
221
|
end
|
244
222
|
end
|
@@ -1,8 +1,4 @@
|
|
1
|
-
# TODO: Reduce module length
|
2
|
-
|
3
1
|
module NetworkX
|
4
|
-
# TODO: Reduce method length
|
5
|
-
|
6
2
|
# Returns the edges of the graph in an array
|
7
3
|
def self.edges_in_array(graph)
|
8
4
|
edge_array = []
|
@@ -25,26 +21,26 @@ module NetworkX
|
|
25
21
|
end
|
26
22
|
|
27
23
|
# Returns the hash product of two hashes
|
28
|
-
def self.hash_product(
|
29
|
-
|
24
|
+
def self.hash_product(hash1, hash2)
|
25
|
+
(hash1.keys | hash2.keys).to_h { |n| [n, [hash1[n], hash2[n]]] }
|
30
26
|
end
|
31
27
|
|
32
28
|
# Returns the node product of nodes of two graphs
|
33
|
-
def self.node_product(
|
29
|
+
def self.node_product(g1, g2)
|
34
30
|
n_product = []
|
35
|
-
|
36
|
-
|
37
|
-
n_product << [[
|
31
|
+
g1.nodes.each do |k1, attrs1|
|
32
|
+
g2.nodes.each do |k2, attrs2|
|
33
|
+
n_product << [[k1, k2], hash_product(attrs1, attrs2)]
|
38
34
|
end
|
39
35
|
end
|
40
36
|
n_product
|
41
37
|
end
|
42
38
|
|
43
39
|
# Returns the product of directed edges with edges
|
44
|
-
def self.directed_edges_cross_edges(
|
40
|
+
def self.directed_edges_cross_edges(g1, g2)
|
45
41
|
result = []
|
46
|
-
edges_in_array(
|
47
|
-
edges_in_array(
|
42
|
+
edges_in_array(g1).each do |u, v, c|
|
43
|
+
edges_in_array(g2).each do |x, y, d|
|
48
44
|
result << [[u, x], [v, y], hash_product(c, d)]
|
49
45
|
end
|
50
46
|
end
|
@@ -52,10 +48,10 @@ module NetworkX
|
|
52
48
|
end
|
53
49
|
|
54
50
|
# Returns the product of undirected edges with edges
|
55
|
-
def self.undirected_edges_cross_edges(
|
51
|
+
def self.undirected_edges_cross_edges(g1, g2)
|
56
52
|
result = []
|
57
|
-
edges_in_array(
|
58
|
-
edges_in_array(
|
53
|
+
edges_in_array(g1).each do |u, v, c|
|
54
|
+
edges_in_array(g2).each do |x, y, d|
|
59
55
|
result << [[v, x], [u, y], hash_product(c, d)]
|
60
56
|
end
|
61
57
|
end
|
@@ -63,10 +59,10 @@ module NetworkX
|
|
63
59
|
end
|
64
60
|
|
65
61
|
# Returns the product of edges with edges
|
66
|
-
def self.edges_cross_nodes(
|
62
|
+
def self.edges_cross_nodes(g1, g2)
|
67
63
|
result = []
|
68
|
-
edges_in_array(
|
69
|
-
|
64
|
+
edges_in_array(g1).each do |u, v, d|
|
65
|
+
g2.nodes.each_key do |x|
|
70
66
|
result << [[u, x], [v, x], d]
|
71
67
|
end
|
72
68
|
end
|
@@ -74,10 +70,10 @@ module NetworkX
|
|
74
70
|
end
|
75
71
|
|
76
72
|
# Returns the product of directed nodes with edges
|
77
|
-
def self.nodes_cross_edges(
|
73
|
+
def self.nodes_cross_edges(g1, g2)
|
78
74
|
result = []
|
79
|
-
|
80
|
-
edges_in_array(
|
75
|
+
g1.nodes.each_key do |x|
|
76
|
+
edges_in_array(g2).each do |u, v, d|
|
81
77
|
result << [[x, u], [x, v], d]
|
82
78
|
end
|
83
79
|
end
|
@@ -85,11 +81,11 @@ module NetworkX
|
|
85
81
|
end
|
86
82
|
|
87
83
|
# Returns the product of edges with pairs of nodes
|
88
|
-
def self.edges_cross_nodes_and_nodes(
|
84
|
+
def self.edges_cross_nodes_and_nodes(g1, g2)
|
89
85
|
result = []
|
90
|
-
edges_in_array(
|
91
|
-
|
92
|
-
|
86
|
+
edges_in_array(g1).each do |u, v, d|
|
87
|
+
g2.nodes.each_key do |x|
|
88
|
+
g2.nodes.each_key do |y|
|
93
89
|
result << [[u, x], [v, y], d]
|
94
90
|
end
|
95
91
|
end
|
@@ -98,10 +94,10 @@ module NetworkX
|
|
98
94
|
end
|
99
95
|
|
100
96
|
# Initializes the product graph
|
101
|
-
def self.init_product_graph(
|
102
|
-
raise ArgumentError, 'Arguments must be both directed or undirected!' unless
|
97
|
+
def self.init_product_graph(g1, g2)
|
98
|
+
raise ArgumentError, 'Arguments must be both directed or undirected!' unless g1.directed? == g2.directed?
|
103
99
|
|
104
|
-
g = if
|
100
|
+
g = if g1.multigraph? || g2.multigraph?
|
105
101
|
NetworkX::MultiGraph.new
|
106
102
|
else
|
107
103
|
NetworkX::Graph.new
|
@@ -112,72 +108,71 @@ module NetworkX
|
|
112
108
|
|
113
109
|
# Returns the tensor product of two graphs
|
114
110
|
#
|
115
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
116
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
111
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
112
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
117
113
|
#
|
118
114
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the tensor product of the two graphs
|
119
|
-
def self.tensor_product(
|
120
|
-
g = init_product_graph(
|
121
|
-
g.add_nodes(node_product(
|
122
|
-
g.add_edges(directed_edges_cross_edges(
|
123
|
-
g.add_edges(undirected_edges_cross_edges(
|
115
|
+
def self.tensor_product(g1, g2)
|
116
|
+
g = init_product_graph(g1, g2)
|
117
|
+
g.add_nodes(node_product(g1, g2))
|
118
|
+
g.add_edges(directed_edges_cross_edges(g1, g2))
|
119
|
+
g.add_edges(undirected_edges_cross_edges(g1, g2)) unless g.directed?
|
124
120
|
g
|
125
121
|
end
|
126
122
|
|
127
123
|
# Returns the cartesian product of two graphs
|
128
124
|
#
|
129
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
130
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
125
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
126
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
131
127
|
#
|
132
128
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the cartesian product of the two graphs
|
133
|
-
def self.cartesian_product(
|
134
|
-
g = init_product_graph(
|
135
|
-
g.add_nodes(node_product(
|
136
|
-
g.add_edges(edges_cross_nodes(
|
137
|
-
g.add_edges(nodes_cross_edges(
|
129
|
+
def self.cartesian_product(g1, g2)
|
130
|
+
g = init_product_graph(g1, g2)
|
131
|
+
g.add_nodes(node_product(g1, g2))
|
132
|
+
g.add_edges(edges_cross_nodes(g1, g2))
|
133
|
+
g.add_edges(nodes_cross_edges(g1, g2))
|
138
134
|
g
|
139
135
|
end
|
140
136
|
|
141
137
|
# Returns the lexicographic product of two graphs
|
142
138
|
#
|
143
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
144
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
139
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
140
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
145
141
|
#
|
146
142
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the lexicographic product of the two graphs
|
147
|
-
def self.lexicographic_product(
|
148
|
-
g = init_product_graph(
|
149
|
-
g.add_nodes(node_product(
|
150
|
-
g.add_edges(edges_cross_nodes_and_nodes(
|
151
|
-
g.add_edges(nodes_cross_edges(
|
143
|
+
def self.lexicographic_product(g1, g2)
|
144
|
+
g = init_product_graph(g1, g2)
|
145
|
+
g.add_nodes(node_product(g1, g2))
|
146
|
+
g.add_edges(edges_cross_nodes_and_nodes(g1, g2))
|
147
|
+
g.add_edges(nodes_cross_edges(g1, g2))
|
152
148
|
g
|
153
149
|
end
|
154
150
|
|
155
151
|
# Returns the strong product of two graphs
|
156
152
|
#
|
157
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
158
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
153
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
154
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
159
155
|
#
|
160
156
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the strong product of the two graphs
|
161
|
-
def self.strong_product(
|
162
|
-
g = init_product_graph(
|
163
|
-
g.add_nodes(node_product(
|
164
|
-
g.add_edges(nodes_cross_edges(
|
165
|
-
g.add_edges(edges_cross_nodes(
|
166
|
-
g.add_edges(directed_edges_cross_edges(
|
167
|
-
g.add_edges(undirected_edges_cross_edges(
|
157
|
+
def self.strong_product(g1, g2)
|
158
|
+
g = init_product_graph(g1, g2)
|
159
|
+
g.add_nodes(node_product(g1, g2))
|
160
|
+
g.add_edges(nodes_cross_edges(g1, g2))
|
161
|
+
g.add_edges(edges_cross_nodes(g1, g2))
|
162
|
+
g.add_edges(directed_edges_cross_edges(g1, g2))
|
163
|
+
g.add_edges(undirected_edges_cross_edges(g1, g2)) unless g.directed?
|
168
164
|
g
|
169
165
|
end
|
170
166
|
|
171
|
-
# TODO: Reduce method complexity and method length
|
172
|
-
|
173
167
|
# Returns the specified power of the graph
|
174
168
|
#
|
175
|
-
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
176
|
-
# @param [Numeric] the power to which to raise the graph to
|
169
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
170
|
+
# @param pow [Numeric] the power to which to raise the graph to
|
177
171
|
#
|
178
172
|
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the power of the graph
|
179
173
|
def self.power(graph, pow)
|
180
174
|
raise ArgumentError, 'Power must be a positive quantity!' if pow <= 0
|
175
|
+
|
181
176
|
result = NetworkX::Graph.new
|
182
177
|
result.add_nodes(graph.nodes.map { |n, attrs| [n, attrs] })
|
183
178
|
graph.nodes.each do |n, _attrs|
|
@@ -189,12 +184,14 @@ module NetworkX
|
|
189
184
|
next_level = {}
|
190
185
|
this_level.each do |v, _attrs|
|
191
186
|
next if v == n
|
192
|
-
|
187
|
+
|
188
|
+
unless seen.has_key?(v)
|
193
189
|
seen[v] = level
|
194
190
|
next_level.merge!(graph.adj[v])
|
195
191
|
end
|
196
192
|
end
|
197
193
|
break if pow <= level
|
194
|
+
|
198
195
|
level += 1
|
199
196
|
end
|
200
197
|
result.add_edges(seen.map { |v, _| [n, v] })
|
@@ -10,7 +10,7 @@ module NetworkX
|
|
10
10
|
|
11
11
|
result.add_nodes(graph.nodes.map { |u, attrs| [u, attrs] })
|
12
12
|
graph.adj.each do |u, u_edges|
|
13
|
-
graph.nodes.each { |v, attrs| result.add_edge(u, v, attrs) if !u_edges.
|
13
|
+
graph.nodes.each { |v, attrs| result.add_edge(u, v, **attrs) if !u_edges.has_key?(v) && u != v }
|
14
14
|
end
|
15
15
|
result
|
16
16
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../auxillary_functions/union_find'
|
2
|
+
|
3
|
+
module NetworkX
|
4
|
+
# @return [[Object, Object]] bridges
|
5
|
+
#
|
6
|
+
# @param graph [Graph] Graph
|
7
|
+
def self.bridges(graph)
|
8
|
+
each_bridge(graph).to_a
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param graph [Graph] Graph
|
12
|
+
def self.each_bridge(graph)
|
13
|
+
return enum_for(:each_bridge, graph) unless block_given?
|
14
|
+
|
15
|
+
graph.each_edge.with_index do |(s_i, t_i), i|
|
16
|
+
uf = UnionFind.new(1..graph.number_of_nodes)
|
17
|
+
graph.each_edge.with_index do |(s_j, t_j), j|
|
18
|
+
uf.unite(s_j, t_j) if i != j
|
19
|
+
end
|
20
|
+
yield [s_i, t_i] unless uf.same?(s_i, t_i)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] the number of bridges
|
25
|
+
#
|
26
|
+
# @param graph [Graph] Graph
|
27
|
+
def self.number_of_bridges(graph)
|
28
|
+
bridges(graph).size
|
29
|
+
end
|
30
|
+
end
|