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,222 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Returns the edges of the graph in an array
|
3
|
+
def self.get_edges(graph)
|
4
|
+
edges = []
|
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
|
20
|
+
end
|
21
|
+
end
|
22
|
+
edges
|
23
|
+
end
|
24
|
+
|
25
|
+
# Transforms the labels of the nodes of the graphs
|
26
|
+
# so that they are disjoint.
|
27
|
+
def self.convert_to_distinct_labels(graph, starting_int = -1)
|
28
|
+
new_graph = graph.class.new
|
29
|
+
|
30
|
+
idx_dict = graph.nodes.keys.to_h do |v|
|
31
|
+
starting_int += 1
|
32
|
+
[v, starting_int]
|
33
|
+
end
|
34
|
+
|
35
|
+
graph.nodes.each do |u, attrs|
|
36
|
+
new_graph.add_node(u.to_s + idx_dict[u].to_s, **attrs)
|
37
|
+
end
|
38
|
+
|
39
|
+
graph.adj.each do |u, u_edges|
|
40
|
+
u_edges.each do |v, uv_attrs|
|
41
|
+
if graph.multigraph?
|
42
|
+
uv_attrs.each do |_k, attrs|
|
43
|
+
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, **attrs)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
new_graph.add_edge(u.to_s + idx_dict[u].to_s, v.to_s + idx_dict[v].to_s, **uv_attrs)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
new_graph
|
51
|
+
end
|
52
|
+
|
53
|
+
# Performs the intersection of two graphs
|
54
|
+
#
|
55
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
56
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
57
|
+
#
|
58
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the intersection of the two graphs
|
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)
|
75
|
+
end
|
76
|
+
elsif g2.edge?(u, v)
|
77
|
+
result.add_edge(u, v, **uv_attrs)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
# Performs the difference of two graphs
|
85
|
+
#
|
86
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
87
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
88
|
+
#
|
89
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the difference of the two graphs
|
90
|
+
def self.difference(g1, g2)
|
91
|
+
result = g1.class.new
|
92
|
+
|
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?
|
95
|
+
|
96
|
+
g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
|
97
|
+
|
98
|
+
g1.adj.each do |u, u_edges|
|
99
|
+
u_edges.each do |v, uv_attrs|
|
100
|
+
if g1.multigraph?
|
101
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
102
|
+
|
103
|
+
uv_attrs.each do |k, attrs|
|
104
|
+
result.add_edge(u, v, **attrs) unless g2.edge?(u, v, k)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
result.add_edge(u, v, **uv_attrs) unless g2.edge?(u, v)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
result
|
112
|
+
end
|
113
|
+
|
114
|
+
# Performs the symmetric difference of two graphs
|
115
|
+
#
|
116
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
117
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
118
|
+
#
|
119
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the symmetric difference of the two graphs
|
120
|
+
def self.symmetric_difference(g1, g2)
|
121
|
+
result = g1.class.new
|
122
|
+
|
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?
|
125
|
+
|
126
|
+
g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
|
127
|
+
|
128
|
+
g1.adj.each do |u, u_edges|
|
129
|
+
u_edges.each do |v, uv_attrs|
|
130
|
+
if g1.multigraph?
|
131
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
132
|
+
|
133
|
+
uv_attrs.each do |k, attrs|
|
134
|
+
result.add_edge(u, v, **attrs) unless g2.edge?(u, v, k)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
result.add_edge(u, v, **uv_attrs) unless g2.edge?(u, v)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
g2.adj.each do |u, u_edges|
|
143
|
+
u_edges.each do |v, uv_attrs|
|
144
|
+
next if u > v && g1.instance_of?(MultiGraph)
|
145
|
+
|
146
|
+
if g2.multigraph?
|
147
|
+
uv_attrs.each do |k, attrs|
|
148
|
+
result.add_edge(u, v, **attrs) unless g1.edge?(u, v, k)
|
149
|
+
end
|
150
|
+
else
|
151
|
+
result.add_edge(u, v, **uv_attrs) unless g1.edge?(u, v)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
result
|
156
|
+
end
|
157
|
+
|
158
|
+
# Performs the composition of two graphs
|
159
|
+
#
|
160
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
161
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
162
|
+
#
|
163
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the composition of the two graphs
|
164
|
+
def self.compose(g1, g2)
|
165
|
+
result = g1.class.new
|
166
|
+
|
167
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
168
|
+
|
169
|
+
result.add_nodes(g1.nodes.map { |u, attrs| [u, attrs] })
|
170
|
+
result.add_nodes(g2.nodes.map { |u, attrs| [u, attrs] })
|
171
|
+
|
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) } } }
|
175
|
+
else
|
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) } }
|
178
|
+
end
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
# Performs the union of two graphs
|
183
|
+
#
|
184
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
185
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
186
|
+
#
|
187
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the union of the two graphs
|
188
|
+
def self.union(g1, g2)
|
189
|
+
raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
|
190
|
+
|
191
|
+
new_graph = g1.class.new
|
192
|
+
new_graph.graph.merge!(g1.graph)
|
193
|
+
new_graph.graph.merge!(g2.graph)
|
194
|
+
|
195
|
+
raise ArgumentError, 'Graphs must be disjoint!' unless (g1.nodes.keys & g2.nodes.keys).empty?
|
196
|
+
|
197
|
+
g1_edges = get_edges(g1)
|
198
|
+
g2_edges = get_edges(g2)
|
199
|
+
|
200
|
+
new_graph.add_nodes(g1.nodes.keys)
|
201
|
+
new_graph.add_edges(g1_edges)
|
202
|
+
new_graph.add_nodes(g2.nodes.keys)
|
203
|
+
new_graph.add_edges(g2_edges)
|
204
|
+
|
205
|
+
new_graph
|
206
|
+
end
|
207
|
+
|
208
|
+
# Performs the disjoint union of two graphs
|
209
|
+
#
|
210
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
211
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
212
|
+
#
|
213
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the disjoint union of the two graphs
|
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)
|
220
|
+
result
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Returns the edges of the graph in an array
|
3
|
+
def self.edges_in_array(graph)
|
4
|
+
edge_array = []
|
5
|
+
if graph.multigraph?
|
6
|
+
graph.adj.each do |u, u_edges|
|
7
|
+
u_edges.each do |v, uv_edges|
|
8
|
+
uv_edges.each do |_k, attrs|
|
9
|
+
edge_array << [u, v, attrs]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
else
|
14
|
+
graph.adj.each do |u, u_edges|
|
15
|
+
u_edges.each do |v, attrs|
|
16
|
+
edge_array << [u, v, attrs]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
edge_array
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the hash product of two hashes
|
24
|
+
def self.hash_product(hash1, hash2)
|
25
|
+
(hash1.keys | hash2.keys).to_h { |n| [n, [hash1[n], hash2[n]]] }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the node product of nodes of two graphs
|
29
|
+
def self.node_product(g1, g2)
|
30
|
+
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)]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
n_product
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the product of directed edges with edges
|
40
|
+
def self.directed_edges_cross_edges(g1, g2)
|
41
|
+
result = []
|
42
|
+
edges_in_array(g1).each do |u, v, c|
|
43
|
+
edges_in_array(g2).each do |x, y, d|
|
44
|
+
result << [[u, x], [v, y], hash_product(c, d)]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the product of undirected edges with edges
|
51
|
+
def self.undirected_edges_cross_edges(g1, g2)
|
52
|
+
result = []
|
53
|
+
edges_in_array(g1).each do |u, v, c|
|
54
|
+
edges_in_array(g2).each do |x, y, d|
|
55
|
+
result << [[v, x], [u, y], hash_product(c, d)]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the product of edges with edges
|
62
|
+
def self.edges_cross_nodes(g1, g2)
|
63
|
+
result = []
|
64
|
+
edges_in_array(g1).each do |u, v, d|
|
65
|
+
g2.nodes.each_key do |x|
|
66
|
+
result << [[u, x], [v, x], d]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the product of directed nodes with edges
|
73
|
+
def self.nodes_cross_edges(g1, g2)
|
74
|
+
result = []
|
75
|
+
g1.nodes.each_key do |x|
|
76
|
+
edges_in_array(g2).each do |u, v, d|
|
77
|
+
result << [[x, u], [x, v], d]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the product of edges with pairs of nodes
|
84
|
+
def self.edges_cross_nodes_and_nodes(g1, g2)
|
85
|
+
result = []
|
86
|
+
edges_in_array(g1).each do |u, v, d|
|
87
|
+
g2.nodes.each_key do |x|
|
88
|
+
g2.nodes.each_key do |y|
|
89
|
+
result << [[u, x], [v, y], d]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# Initializes the product graph
|
97
|
+
def self.init_product_graph(g1, g2)
|
98
|
+
raise ArgumentError, 'Arguments must be both directed or undirected!' unless g1.directed? == g2.directed?
|
99
|
+
|
100
|
+
g = if g1.multigraph? || g2.multigraph?
|
101
|
+
NetworkX::MultiGraph.new
|
102
|
+
else
|
103
|
+
NetworkX::Graph.new
|
104
|
+
end
|
105
|
+
g = g.to_directed if g.directed?
|
106
|
+
g
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the tensor product of two graphs
|
110
|
+
#
|
111
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
112
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
113
|
+
#
|
114
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the tensor product of the two graphs
|
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?
|
120
|
+
g
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the cartesian product of two graphs
|
124
|
+
#
|
125
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
126
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
127
|
+
#
|
128
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the cartesian product of the two graphs
|
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))
|
134
|
+
g
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns the lexicographic product of two graphs
|
138
|
+
#
|
139
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
140
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
141
|
+
#
|
142
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the lexicographic product of the two graphs
|
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))
|
148
|
+
g
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns the strong product of two graphs
|
152
|
+
#
|
153
|
+
# @param g1 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
154
|
+
# @param g2 [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.2
|
155
|
+
#
|
156
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the strong product of the two graphs
|
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?
|
164
|
+
g
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns the specified power of the graph
|
168
|
+
#
|
169
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] graph no.1
|
170
|
+
# @param pow [Numeric] the power to which to raise the graph to
|
171
|
+
#
|
172
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the power of the graph
|
173
|
+
def self.power(graph, pow)
|
174
|
+
raise ArgumentError, 'Power must be a positive quantity!' if pow <= 0
|
175
|
+
|
176
|
+
result = NetworkX::Graph.new
|
177
|
+
result.add_nodes(graph.nodes.map { |n, attrs| [n, attrs] })
|
178
|
+
graph.nodes.each do |n, _attrs|
|
179
|
+
seen = {}
|
180
|
+
level = 1
|
181
|
+
next_level = graph.adj[n]
|
182
|
+
until next_level.empty?
|
183
|
+
this_level = next_level
|
184
|
+
next_level = {}
|
185
|
+
this_level.each do |v, _attrs|
|
186
|
+
next if v == n
|
187
|
+
|
188
|
+
unless seen.has_key?(v)
|
189
|
+
seen[v] = level
|
190
|
+
next_level.merge!(graph.adj[v])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
break if pow <= level
|
194
|
+
|
195
|
+
level += 1
|
196
|
+
end
|
197
|
+
result.add_edges(seen.map { |v, _| [n, v] })
|
198
|
+
end
|
199
|
+
result
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Performs the complement operation on the graph
|
3
|
+
#
|
4
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph
|
5
|
+
#
|
6
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the complement of the graph
|
7
|
+
def self.complement(graph)
|
8
|
+
result = Marshal.load(Marshal.dump(graph))
|
9
|
+
result.clear
|
10
|
+
|
11
|
+
result.add_nodes(graph.nodes.map { |u, attrs| [u, attrs] })
|
12
|
+
graph.adj.each do |u, u_edges|
|
13
|
+
graph.nodes.each { |v, attrs| result.add_edge(u, v, **attrs) if !u_edges.has_key?(v) && u != v }
|
14
|
+
end
|
15
|
+
result
|
16
|
+
end
|
17
|
+
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
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require_relative '../../networkx'
|
2
|
+
|
3
|
+
module NetworkX
|
4
|
+
class Graph
|
5
|
+
# private class method
|
6
|
+
def self.complete_edges(n)
|
7
|
+
n = (0...n) if n.is_a?(Integer)
|
8
|
+
|
9
|
+
edges = []
|
10
|
+
n.each do |i|
|
11
|
+
n.each do |j|
|
12
|
+
edges << [i, j] if i < j
|
13
|
+
end
|
14
|
+
end
|
15
|
+
edges
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.balanced_tree(r, h)
|
19
|
+
edges = []
|
20
|
+
q = [0]
|
21
|
+
i = 0
|
22
|
+
h.times do
|
23
|
+
t = q.dup
|
24
|
+
q.clear
|
25
|
+
t.each do |v|
|
26
|
+
r.times do
|
27
|
+
i += 1
|
28
|
+
edges << [v, i]
|
29
|
+
q << i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
graph = new(name: "balanced_tree(#{r}, #{h})")
|
34
|
+
graph.add_edges(edges)
|
35
|
+
graph
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.barbell_graph(m1, m2)
|
39
|
+
edges = complete_edges(m1)
|
40
|
+
edges.concat((m1..m2 + m1).map { |k| [k - 1, k] })
|
41
|
+
edges.concat complete_edges(m1 + m2...m1 + m2 + m1)
|
42
|
+
|
43
|
+
graph = new(name: "barbell_graph(#{m1}, #{m2})")
|
44
|
+
graph.add_edges(edges)
|
45
|
+
graph
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.complete_graph(n)
|
49
|
+
n = (0...n) if n.is_a?(Integer)
|
50
|
+
|
51
|
+
edges = []
|
52
|
+
n.each do |i|
|
53
|
+
n.each do |j|
|
54
|
+
edges << [i, j] if i < j
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
graph = new(name: "complete_graph(#{n})")
|
59
|
+
graph.add_edges(edges)
|
60
|
+
graph
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.circular_ladder_graph(n)
|
64
|
+
edges = (0...n - 1).map { |v| [v, v + 1] }
|
65
|
+
edges << [n - 1, 0]
|
66
|
+
edges.concat((n...2 * n - 1).map { |v| [v, v + 1] })
|
67
|
+
edges << [2 * n - 1, n]
|
68
|
+
edges.concat((0...n).map { |v| [v, v + n] })
|
69
|
+
|
70
|
+
graph = new(name: "circular_ladder_graph(#{n})")
|
71
|
+
graph.add_edges(edges)
|
72
|
+
graph
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.cycle_graph(n)
|
76
|
+
edges = (0...n - 1).map { |v| [v, v + 1] }
|
77
|
+
edges << [n - 1, 0]
|
78
|
+
|
79
|
+
graph = new(name: "cycle_graph(#{n})")
|
80
|
+
graph.add_edges(edges)
|
81
|
+
graph
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.empty_graph(number_of_nodes)
|
85
|
+
empty_graph = new(name: "empty_graph#{number_of_nodes}")
|
86
|
+
empty_graph.add_nodes_from(0...number_of_nodes)
|
87
|
+
empty_graph
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.ladder_graph(n)
|
91
|
+
edges = (0...n - 1).map { |k| [k, k + 1] }
|
92
|
+
edges.concat((n...2 * n - 1).map { |k| [k, k + 1] })
|
93
|
+
edges.concat((0...n).map { |k| [k, k + n] })
|
94
|
+
|
95
|
+
graph = new(name: "ladder_graph(#{n})")
|
96
|
+
graph.add_edges(edges)
|
97
|
+
graph
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.lollipop_graph(m, n)
|
101
|
+
edges = complete_edges(m)
|
102
|
+
edges.concat((m - 1...m - 1 + n).map { |v| [v, v + 1] })
|
103
|
+
|
104
|
+
graph = new(name: "lollipop_graph(#{m}, #{n})")
|
105
|
+
graph.add_edges(edges)
|
106
|
+
graph
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.null_graph
|
110
|
+
new(name: 'null_graph')
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.path_graph(n)
|
114
|
+
edges = (0...n - 1).map { |v| [v, v + 1] }
|
115
|
+
|
116
|
+
graph = new(name: "path_graph(#{n})")
|
117
|
+
graph.add_edges(edges)
|
118
|
+
graph
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.star_graph(n)
|
122
|
+
edges = (1..n).map { |i| [0, i] }
|
123
|
+
|
124
|
+
graph = new(name: "star_graph(#{n})")
|
125
|
+
graph.add_edges(edges)
|
126
|
+
graph
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.trivial_graph
|
130
|
+
trivial_graph = new(name: 'trivial_grpph')
|
131
|
+
trivial_graph.add_node(0)
|
132
|
+
trivial_graph
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.wheel_graph(n)
|
136
|
+
edges = (1..n - 1).map { |i| [0, i] }
|
137
|
+
edges.concat((1...n - 1).map { |i| [i, i + 1] })
|
138
|
+
edges << [1, n - 1]
|
139
|
+
|
140
|
+
graph = new(name: "wheel_graph(#{n})")
|
141
|
+
graph.add_edges(edges)
|
142
|
+
graph
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.bull_graph
|
146
|
+
edges = [[0, 1], [1, 2], [2, 0], [1, 3], [2, 4]]
|
147
|
+
graph = new(name: 'bull_graph')
|
148
|
+
graph.add_edges(edges)
|
149
|
+
graph
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.cubical_graph
|
153
|
+
graph = circular_ladder_graph(4)
|
154
|
+
graph.graph[:name] = 'cubical_graph'
|
155
|
+
graph
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.diamond_graph
|
159
|
+
edges = [[0, 1], [0, 2], [1, 2], [1, 3], [2, 3]]
|
160
|
+
graph = new(name: 'diamond_graph')
|
161
|
+
graph.add_edges(edges)
|
162
|
+
graph
|
163
|
+
end
|
164
|
+
|
165
|
+
# 12
|
166
|
+
def self.dodecahedral_graph
|
167
|
+
edges = (0...19).map { |k| [k, k + 1] }
|
168
|
+
edges.concat [[0, 19], [0, 10], [1, 8], [2, 6], [3, 19], [4, 17], [5, 15], [7, 14], [9, 13], [11, 18], [12, 16]]
|
169
|
+
graph = new(name: 'dodecahedral_graph')
|
170
|
+
graph.add_edges(edges)
|
171
|
+
graph
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.heawood_graph
|
175
|
+
edges = (0...13).map { |k| [k, k + 1] }
|
176
|
+
edges << [13, 0]
|
177
|
+
edges.concat [[0, 5], [1, 10], [2, 7], [3, 12], [4, 9], [6, 11], [8, 13]]
|
178
|
+
graph = new(name: 'heawood_graph')
|
179
|
+
graph.add_edges(edges)
|
180
|
+
graph
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.house_graph
|
184
|
+
edges = [[0, 1], [0, 2], [1, 3], [2, 3], [2, 4], [3, 4]]
|
185
|
+
graph = new(name: 'house_graph')
|
186
|
+
graph.add_edges(edges)
|
187
|
+
graph
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.house_x_graph
|
191
|
+
edges = (0...4).map { |k| [k, k + 1] }
|
192
|
+
edges.concat [[0, 2], [0, 3], [1, 3], [2, 4], [3, 4]]
|
193
|
+
graph = new(name: 'house_x_graph')
|
194
|
+
graph.add_edges(edges)
|
195
|
+
graph
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.moebius_kantor_graph
|
199
|
+
edges = (0...15).map { |k| [k, k + 1] }
|
200
|
+
edges << [15, 0]
|
201
|
+
edges.concat [[0, 5], [1, 12], [2, 7], [4, 9], [3, 14], [6, 11], [8, 13], [10, 15]]
|
202
|
+
graph = new(name: 'moebius_kantor_graph')
|
203
|
+
graph.add_edges(edges)
|
204
|
+
graph
|
205
|
+
end
|
206
|
+
|
207
|
+
# 8: 6 nodes, 12 edges
|
208
|
+
def self.octahedral_graph
|
209
|
+
edges = []
|
210
|
+
6.times do |i|
|
211
|
+
6.times do |j|
|
212
|
+
edges << [i, j] if i != j && i + j != 5
|
213
|
+
end
|
214
|
+
end
|
215
|
+
graph = new(name: 'octahedral_graph')
|
216
|
+
graph.add_edges(edges)
|
217
|
+
graph
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.tetrahedral_graph
|
221
|
+
graph = complete_graph(4)
|
222
|
+
graph.graph[:name] = 'tetrahedral_graph'
|
223
|
+
graph
|
224
|
+
end
|
225
|
+
|
226
|
+
# Experimental For debug.
|
227
|
+
#
|
228
|
+
# @return data for https://hello-world-494ec.firebaseapp.com/
|
229
|
+
def put_graph_x2
|
230
|
+
output = <<~"OUTPUT"
|
231
|
+
#{number_of_nodes} #{number_of_edges}
|
232
|
+
#{edges.map { |edge| edge.join(' ') }.join("\n")}
|
233
|
+
OUTPUT
|
234
|
+
puts output
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|