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.
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