networkx 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +18 -11
  3. data/CONTRIBUTING.md +2 -2
  4. data/Guardfile +1 -1
  5. data/ISSUE_TEMPLATE.md +15 -0
  6. data/PULL_REQUEST_TEMPLATE.md +12 -0
  7. data/README.md +4 -4
  8. data/RELEASE_POLICY.md +20 -0
  9. data/lib/networkx.rb +37 -1
  10. data/lib/networkx/auxillary_functions/cliques.rb +65 -0
  11. data/lib/networkx/auxillary_functions/cycles.rb +104 -0
  12. data/lib/networkx/auxillary_functions/dag.rb +54 -0
  13. data/lib/networkx/auxillary_functions/eccentricity.rb +36 -0
  14. data/lib/networkx/auxillary_functions/mis.rb +23 -0
  15. data/lib/networkx/auxillary_functions/mst.rb +35 -0
  16. data/lib/networkx/auxillary_functions/union_find.rb +24 -0
  17. data/lib/networkx/auxillary_functions/vitality.rb +13 -0
  18. data/lib/networkx/auxillary_functions/wiener.rb +13 -0
  19. data/lib/networkx/converters/to_csv.rb +47 -0
  20. data/lib/networkx/converters/to_json.rb +39 -0
  21. data/lib/networkx/digraph.rb +228 -0
  22. data/lib/networkx/flow/capacityscaling.rb +255 -0
  23. data/lib/networkx/flow/edmondskarp.rb +113 -0
  24. data/lib/networkx/flow/preflowpush.rb +252 -0
  25. data/lib/networkx/flow/shortestaugmentingpath.rb +157 -0
  26. data/lib/networkx/flow/utils.rb +160 -0
  27. data/lib/networkx/graph.rb +341 -0
  28. data/lib/networkx/link_analysis/hits.rb +59 -0
  29. data/lib/networkx/link_analysis/pagerank.rb +47 -0
  30. data/lib/networkx/multidigraph.rb +240 -0
  31. data/lib/networkx/multigraph.rb +171 -0
  32. data/lib/networkx/operators/all.rb +61 -0
  33. data/lib/networkx/operators/binary.rb +244 -0
  34. data/lib/networkx/operators/product.rb +204 -0
  35. data/lib/networkx/operators/unary.rb +17 -0
  36. data/lib/networkx/shortest_path/astar.rb +71 -0
  37. data/lib/networkx/shortest_path/dense.rb +31 -0
  38. data/lib/networkx/shortest_path/unweighted.rb +139 -0
  39. data/lib/networkx/shortest_path/weighted.rb +408 -0
  40. data/lib/networkx/to_matrix.rb +52 -0
  41. data/lib/networkx/traversals/bfs.rb +58 -0
  42. data/lib/networkx/traversals/dfs.rb +79 -0
  43. data/lib/networkx/traversals/edge_dfs.rb +90 -0
  44. data/lib/networkx/version.rb +1 -1
  45. data/networkx.gemspec +4 -1
  46. 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