networkx 0.1.0 → 0.1.1
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 +4 -4
- data/.rubocop.yml +18 -11
- data/CONTRIBUTING.md +2 -2
- data/Guardfile +1 -1
- data/ISSUE_TEMPLATE.md +15 -0
- data/PULL_REQUEST_TEMPLATE.md +12 -0
- data/README.md +4 -4
- data/RELEASE_POLICY.md +20 -0
- data/lib/networkx.rb +37 -1
- data/lib/networkx/auxillary_functions/cliques.rb +65 -0
- data/lib/networkx/auxillary_functions/cycles.rb +104 -0
- data/lib/networkx/auxillary_functions/dag.rb +54 -0
- data/lib/networkx/auxillary_functions/eccentricity.rb +36 -0
- data/lib/networkx/auxillary_functions/mis.rb +23 -0
- data/lib/networkx/auxillary_functions/mst.rb +35 -0
- data/lib/networkx/auxillary_functions/union_find.rb +24 -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 +47 -0
- data/lib/networkx/converters/to_json.rb +39 -0
- data/lib/networkx/digraph.rb +228 -0
- data/lib/networkx/flow/capacityscaling.rb +255 -0
- data/lib/networkx/flow/edmondskarp.rb +113 -0
- data/lib/networkx/flow/preflowpush.rb +252 -0
- data/lib/networkx/flow/shortestaugmentingpath.rb +157 -0
- data/lib/networkx/flow/utils.rb +160 -0
- data/lib/networkx/graph.rb +341 -0
- data/lib/networkx/link_analysis/hits.rb +59 -0
- data/lib/networkx/link_analysis/pagerank.rb +47 -0
- data/lib/networkx/multidigraph.rb +240 -0
- data/lib/networkx/multigraph.rb +171 -0
- data/lib/networkx/operators/all.rb +61 -0
- data/lib/networkx/operators/binary.rb +244 -0
- data/lib/networkx/operators/product.rb +204 -0
- data/lib/networkx/operators/unary.rb +17 -0
- data/lib/networkx/shortest_path/astar.rb +71 -0
- data/lib/networkx/shortest_path/dense.rb +31 -0
- data/lib/networkx/shortest_path/unweighted.rb +139 -0
- data/lib/networkx/shortest_path/weighted.rb +408 -0
- data/lib/networkx/to_matrix.rb +52 -0
- data/lib/networkx/traversals/bfs.rb +58 -0
- data/lib/networkx/traversals/dfs.rb +79 -0
- data/lib/networkx/traversals/edge_dfs.rb +90 -0
- data/lib/networkx/version.rb +1 -1
- data/networkx.gemspec +4 -1
- metadata +70 -4
@@ -0,0 +1,61 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Performs the union of many graphs
|
3
|
+
#
|
4
|
+
# @param [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
|
5
|
+
#
|
6
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] union of all the graphs
|
7
|
+
def self.union_all(graphs)
|
8
|
+
raise ArgumentError, 'Argument array is empty' if graphs.empty?
|
9
|
+
result = graphs.shift
|
10
|
+
|
11
|
+
graphs.each do |graph|
|
12
|
+
result = NetworkX.union(result, graph)
|
13
|
+
end
|
14
|
+
result
|
15
|
+
end
|
16
|
+
|
17
|
+
# Performs the disjoint union of many graphs
|
18
|
+
#
|
19
|
+
# @param [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
|
20
|
+
#
|
21
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] disjoint union of all the graphs
|
22
|
+
def self.disjoint_union_all(graphs)
|
23
|
+
raise ArgumentError, 'Argument array is empty' if graphs.empty?
|
24
|
+
result = graphs.shift
|
25
|
+
|
26
|
+
graphs.each do |graph|
|
27
|
+
result = NetworkX.disjoint_union(result, graph)
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
|
32
|
+
# Performs the intersection of many graphs
|
33
|
+
#
|
34
|
+
# @param [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
|
35
|
+
#
|
36
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] intersection of all the graphs
|
37
|
+
def self.intersection_all(graphs)
|
38
|
+
raise ArgumentError, 'Argument array is empty' if graphs.empty?
|
39
|
+
result = graphs.shift
|
40
|
+
|
41
|
+
graphs.each do |graph|
|
42
|
+
result = NetworkX.intersection(result, graph)
|
43
|
+
end
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
# Performs the composition of many graphs
|
48
|
+
#
|
49
|
+
# @param [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
|
50
|
+
#
|
51
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] composition of all the graphs
|
52
|
+
def self.compose_all(graphs)
|
53
|
+
raise ArgumentError, 'Argument array is empty' if graphs.empty?
|
54
|
+
result = graphs.shift
|
55
|
+
|
56
|
+
graphs.each do |graph|
|
57
|
+
result = NetworkX.compose(result, graph)
|
58
|
+
end
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
# TODO: Reduce module length
|
2
|
+
|
3
|
+
module NetworkX
|
4
|
+
# Returns the edges of the graph in an array
|
5
|
+
def self.get_edges(graph)
|
6
|
+
edges = []
|
7
|
+
graph.adj.each do |u, u_attrs|
|
8
|
+
u_attrs.each do |v, uv_attrs|
|
9
|
+
edges << [u, v, uv_attrs]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
edges
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: Reduce method complexity and method length
|
16
|
+
|
17
|
+
# Transforms the labels of the nodes of the graphs
|
18
|
+
# so that they are disjoint.
|
19
|
+
def self.convert_to_distinct_labels(graph, starting_int=-1)
|
20
|
+
new_graph = Marshal.load(Marshal.dump(graph))
|
21
|
+
new_graph.clear
|
22
|
+
|
23
|
+
idx_dict = Hash[graph.nodes.keys.map do |v|
|
24
|
+
starting_int += 1
|
25
|
+
[v, starting_int]
|
26
|
+
end]
|
27
|
+
|
28
|
+
graph.nodes.each do |u, attrs|
|
29
|
+
new_graph.add_node(u.to_s + idx_dict[u].to_s, attrs)
|
30
|
+
end
|
31
|
+
|
32
|
+
graph.adj.each do |u, u_edges|
|
33
|
+
u_edges.each do |v, uv_attrs|
|
34
|
+
if graph.multigraph?
|
35
|
+
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)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, uv_attrs)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
new_graph
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: Reduce method complexity and method length
|
47
|
+
|
48
|
+
# Performs the intersection of two graphs
|
49
|
+
#
|
50
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
51
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
52
|
+
#
|
53
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the intersection of the two graphs
|
54
|
+
def self.intersection(g_1, g_2)
|
55
|
+
result = Marshal.load(Marshal.dump(g_1))
|
56
|
+
result.clear
|
57
|
+
|
58
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g_1.multigraph? == g_2.multigraph?
|
59
|
+
raise ArgumentError, 'Node sets must be equal!' unless (g_1.nodes.keys - g_2.nodes.keys).empty?
|
60
|
+
|
61
|
+
g_1.nodes.each { |u, attrs| result.add_node(u, attrs) }
|
62
|
+
|
63
|
+
if g_1.number_of_edges <= g_2.number_of_edges
|
64
|
+
g_1.adj.each do |u, u_edges|
|
65
|
+
u_edges.each do |v, uv_attrs|
|
66
|
+
if g_1.multigraph?
|
67
|
+
uv_attrs.each do |k, attrs|
|
68
|
+
result.add_edge(u, v, attrs) if g_2.edge?(u, v, k)
|
69
|
+
end
|
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)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
result
|
89
|
+
end
|
90
|
+
|
91
|
+
# TODO: Reduce method complexity and method length
|
92
|
+
|
93
|
+
# Performs the difference of two graphs
|
94
|
+
#
|
95
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
96
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
97
|
+
#
|
98
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the difference of the two graphs
|
99
|
+
def self.difference(g_1, g_2)
|
100
|
+
result = Marshal.load(Marshal.dump(g_1))
|
101
|
+
result.clear
|
102
|
+
|
103
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g_1.multigraph? == g_2.multigraph?
|
104
|
+
raise ArgumentError, 'Node sets must be equal!' unless (g_1.nodes.keys - g_2.nodes.keys).empty?
|
105
|
+
|
106
|
+
g_1.nodes.each { |u, attrs| result.add_node(u, attrs) }
|
107
|
+
|
108
|
+
g_1.adj.each do |u, u_edges|
|
109
|
+
u_edges.each do |v, uv_attrs|
|
110
|
+
if g_1.multigraph?
|
111
|
+
uv_attrs.each do |k, attrs|
|
112
|
+
result.add_edge(u, v, attrs) unless g_2.edge?(u, v, k)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
result.add_edge(u, v, uv_attrs) unless g_2.edge?(u, v)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
result
|
120
|
+
end
|
121
|
+
|
122
|
+
# TODO: Reduce method complexity and method length
|
123
|
+
|
124
|
+
# Performs the symmetric difference of two graphs
|
125
|
+
#
|
126
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
127
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
128
|
+
#
|
129
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the symmetric difference of the two graphs
|
130
|
+
def self.symmetric_difference(g_1, g_2)
|
131
|
+
result = Marshal.load(Marshal.dump(g_1))
|
132
|
+
result.clear
|
133
|
+
|
134
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g_1.multigraph? == g_2.multigraph?
|
135
|
+
raise ArgumentError, 'Node sets must be equal!' unless (g_1.nodes.keys - g_2.nodes.keys).empty?
|
136
|
+
|
137
|
+
g_1.nodes.each { |u, attrs| result.add_node(u, attrs) }
|
138
|
+
|
139
|
+
g_1.adj.each do |u, u_edges|
|
140
|
+
u_edges.each do |v, uv_attrs|
|
141
|
+
if g_1.multigraph?
|
142
|
+
uv_attrs.each do |k, attrs|
|
143
|
+
result.add_edge(u, v, attrs) unless g_2.edge?(u, v, k)
|
144
|
+
end
|
145
|
+
else
|
146
|
+
result.add_edge(u, v, uv_attrs) unless g_2.edge?(u, v)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
g_2.adj.each do |u, u_edges|
|
152
|
+
u_edges.each do |v, uv_attrs|
|
153
|
+
if g_2.multigraph?
|
154
|
+
uv_attrs.each do |k, attrs|
|
155
|
+
result.add_edge(u, v, attrs) unless g_1.edge?(u, v, k)
|
156
|
+
end
|
157
|
+
else
|
158
|
+
result.add_edge(u, v, uv_attrs) unless g_1.edge?(u, v)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
result
|
163
|
+
end
|
164
|
+
|
165
|
+
# TODO: Reduce method complexity and method length
|
166
|
+
|
167
|
+
# Performs the composition of two graphs
|
168
|
+
#
|
169
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
170
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
171
|
+
#
|
172
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the composition of the two graphs
|
173
|
+
def self.compose(g_1, g_2)
|
174
|
+
result = Marshal.load(Marshal.dump(g_1))
|
175
|
+
result.clear
|
176
|
+
|
177
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g_1.multigraph? == g_2.multigraph?
|
178
|
+
|
179
|
+
result.add_nodes(g_1.nodes.map { |u, attrs| [u, attrs] })
|
180
|
+
result.add_nodes(g_2.nodes.map { |u, attrs| [u, attrs] })
|
181
|
+
|
182
|
+
if g_1.multigraph?
|
183
|
+
g_1.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, attrs) } } }
|
184
|
+
g_2.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, attrs) } } }
|
185
|
+
else
|
186
|
+
g_1.adj.each { |u, u_edges| u_edges.each { |v, attrs| result.add_edge(u, v, attrs) } }
|
187
|
+
g_2.adj.each { |u, u_edges| u_edges.each { |v, attrs| result.add_edge(u, v, attrs) } }
|
188
|
+
end
|
189
|
+
result
|
190
|
+
end
|
191
|
+
|
192
|
+
# TODO: Reduce method complexity and method length
|
193
|
+
|
194
|
+
# Performs the union of two graphs
|
195
|
+
#
|
196
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
197
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
198
|
+
#
|
199
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the union of the two graphs
|
200
|
+
def self.union(g_1, g_2)
|
201
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g_1.multigraph? == g_2.multigraph?
|
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
|
213
|
+
|
214
|
+
new_graph.graph.merge!(g_1.graph)
|
215
|
+
new_graph.graph.merge!(g_2.graph)
|
216
|
+
|
217
|
+
raise ArgumentError, 'Graphs must be disjoint!' unless (g_1.nodes.keys & g_2.nodes.keys).empty?
|
218
|
+
|
219
|
+
g1_edges = get_edges(g_1)
|
220
|
+
g2_edges = get_edges(g_2)
|
221
|
+
|
222
|
+
new_graph.add_nodes(g_1.nodes.keys)
|
223
|
+
new_graph.add_edges(g1_edges)
|
224
|
+
new_graph.add_nodes(g_2.nodes.keys)
|
225
|
+
new_graph.add_edges(g2_edges)
|
226
|
+
|
227
|
+
new_graph
|
228
|
+
end
|
229
|
+
|
230
|
+
# Performs the disjoint union of two graphs
|
231
|
+
#
|
232
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
233
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
234
|
+
#
|
235
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the disjoint union of the two graphs
|
236
|
+
def self.disjoint_union(g_1, g_2)
|
237
|
+
new_g_1 = convert_to_distinct_labels(g_1)
|
238
|
+
new_g_2 = convert_to_distinct_labels(g_2)
|
239
|
+
result = union(new_g_1, new_g_2)
|
240
|
+
result.graph.merge!(g_1.graph)
|
241
|
+
result.graph.merge!(g_2.graph)
|
242
|
+
result
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
# TODO: Reduce module length
|
2
|
+
|
3
|
+
module NetworkX
|
4
|
+
# TODO: Reduce method length
|
5
|
+
|
6
|
+
# Returns the edges of the graph in an array
|
7
|
+
def self.edges_in_array(graph)
|
8
|
+
edge_array = []
|
9
|
+
if graph.multigraph?
|
10
|
+
graph.adj.each do |u, u_edges|
|
11
|
+
u_edges.each do |v, uv_edges|
|
12
|
+
uv_edges.each do |_k, attrs|
|
13
|
+
edge_array << [u, v, attrs]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
graph.adj.each do |u, u_edges|
|
19
|
+
u_edges.each do |v, attrs|
|
20
|
+
edge_array << [u, v, attrs]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
edge_array
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the hash product of two hashes
|
28
|
+
def self.hash_product(hash_1, hash_2)
|
29
|
+
Hash[(hash_1.keys | hash_2.keys).map { |n| [n, [hash_1[n], hash_2[n]]] }]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the node product of nodes of two graphs
|
33
|
+
def self.node_product(g_1, g_2)
|
34
|
+
n_product = []
|
35
|
+
g_1.nodes.each do |k_1, attrs_1|
|
36
|
+
g_2.nodes.each do |k_2, attrs_2|
|
37
|
+
n_product << [[k_1, k_2], hash_product(attrs_1, attrs_2)]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
n_product
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the product of directed edges with edges
|
44
|
+
def self.directed_edges_cross_edges(g_1, g_2)
|
45
|
+
result = []
|
46
|
+
edges_in_array(g_1).each do |u, v, c|
|
47
|
+
edges_in_array(g_2) do |x, y, d|
|
48
|
+
result << [[u, x], [v, y], hash_product(c, d)]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the product of undirected edges with edges
|
55
|
+
def self.undirected_edges_cross_edges(g_1, g_2)
|
56
|
+
result = []
|
57
|
+
edges_in_array(g_1).each do |u, v, c|
|
58
|
+
edges_in_array(g_2).each do |x, y, d|
|
59
|
+
result << [[v, x], [u, y], hash_product(c, d)]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the product of edges with edges
|
66
|
+
def self.edges_cross_nodes(g_1, g_2)
|
67
|
+
result = []
|
68
|
+
edges_in_array(g_1).each do |u, v, d|
|
69
|
+
g_2.nodes.keys.each do |x|
|
70
|
+
result << [[u, x], [v, x], d]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the product of directed nodes with edges
|
77
|
+
def self.nodes_cross_edges(g_1, g_2)
|
78
|
+
result = []
|
79
|
+
g_1.nodes.keys.each do |x|
|
80
|
+
edges_in_array(g_2).each do |u, v, d|
|
81
|
+
result << [[x, u], [x, v], d]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns the product of edges with pairs of nodes
|
88
|
+
def self.edges_cross_nodes_and_nodes(g_1, g_2)
|
89
|
+
result = []
|
90
|
+
edges_in_array(g_1).each do |u, v, d|
|
91
|
+
g_2.nodes.keys.each do |x|
|
92
|
+
g_2.nodes.keys.each do |y|
|
93
|
+
result << [[u, x], [v, y], d]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
result
|
98
|
+
end
|
99
|
+
|
100
|
+
# Initializes the product graph
|
101
|
+
def self.init_product_graph(g_1, g_2)
|
102
|
+
raise ArgumentError, 'Arguments must be both directed or undirected!' unless g_1.directed? == g_2.directed?
|
103
|
+
|
104
|
+
g = if g_1.multigraph? || g_2.multigraph?
|
105
|
+
NetworkX::MultiGraph.new
|
106
|
+
else
|
107
|
+
NetworkX::Graph.new
|
108
|
+
end
|
109
|
+
g = g.to_directed if g.directed?
|
110
|
+
g
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the tensor product of two graphs
|
114
|
+
#
|
115
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
116
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
117
|
+
#
|
118
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the tensor product of the two graphs
|
119
|
+
def self.tensor_product(g_1, g_2)
|
120
|
+
g = init_product_graph(g_1, g_2)
|
121
|
+
g.add_nodes(node_product(g_1, g_2))
|
122
|
+
g.add_edges(directed_edges_cross_edges(g_1, g_2))
|
123
|
+
g.add_edges(undirected_edges_cross_edges(g_1, g_2)) unless g.directed?
|
124
|
+
g
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns the cartesian product of two graphs
|
128
|
+
#
|
129
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
130
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
131
|
+
#
|
132
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the cartesian product of the two graphs
|
133
|
+
def self.cartesian_product(g_1, g_2)
|
134
|
+
g = init_product_graph(g_1, g_2)
|
135
|
+
g.add_nodes(node_product(g_1, g_2))
|
136
|
+
g.add_edges(edges_cross_nodes(g_1, g_2))
|
137
|
+
g.add_edges(nodes_cross_edges(g_1, g_2))
|
138
|
+
g
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns the lexicographic product of two graphs
|
142
|
+
#
|
143
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
144
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
145
|
+
#
|
146
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the lexicographic product of the two graphs
|
147
|
+
def self.lexicographic_product(g_1, g_2)
|
148
|
+
g = init_product_graph(g_1, g_2)
|
149
|
+
g.add_nodes(node_product(g_1, g_2))
|
150
|
+
g.add_edges(edges_cross_nodes_and_nodes(g_1, g_2))
|
151
|
+
g.add_edges(nodes_cross_edges(g_1, g_2))
|
152
|
+
g
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns the strong product of two graphs
|
156
|
+
#
|
157
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
158
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
159
|
+
#
|
160
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the strong product of the two graphs
|
161
|
+
def self.strong_product(g_1, g_2)
|
162
|
+
g = init_product_graph(g_1, g_2)
|
163
|
+
g.add_nodes(node_product(g_1, g_2))
|
164
|
+
g.add_edges(nodes_cross_edges(g_1, g_2))
|
165
|
+
g.add_edges(edges_cross_nodes(g_1, g_2))
|
166
|
+
g.add_edges(directed_edges_cross_edges(g_1, g_2))
|
167
|
+
g.add_edges(undirected_edges_cross_edges(g_1, g_2)) unless g.directed?
|
168
|
+
g
|
169
|
+
end
|
170
|
+
|
171
|
+
# TODO: Reduce method complexity and method length
|
172
|
+
|
173
|
+
# Returns the specified power of the graph
|
174
|
+
#
|
175
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
176
|
+
# @param [Numeric] the power to which to raise the graph to
|
177
|
+
#
|
178
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the power of the graph
|
179
|
+
def self.power(graph, pow)
|
180
|
+
raise ArgumentError, 'Power must be a positive quantity!' if pow <= 0
|
181
|
+
result = NetworkX::Graph.new
|
182
|
+
result.add_nodes(graph.nodes.map { |n, attrs| [n, attrs] })
|
183
|
+
graph.nodes.each do |n, _attrs|
|
184
|
+
seen = {}
|
185
|
+
level = 1
|
186
|
+
next_level = graph.adj[n]
|
187
|
+
until next_level.empty?
|
188
|
+
this_level = next_level
|
189
|
+
next_level = {}
|
190
|
+
this_level.each do |v, _attrs|
|
191
|
+
next if v == n
|
192
|
+
unless seen.key?(v)
|
193
|
+
seen[v] = level
|
194
|
+
next_level.merge!(graph.adj[v])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
break if pow <= level
|
198
|
+
level += 1
|
199
|
+
end
|
200
|
+
result.add_edges(seen.map { |v, _| [n, v] })
|
201
|
+
end
|
202
|
+
result
|
203
|
+
end
|
204
|
+
end
|