networkx 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/{CODE_OF_CONDUCT.md → .github/CODE_OF_CONDUCT.md} +0 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +20 -10
  4. data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +1 -1
  5. data/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +2 -4
  6. data/.github/workflows/ci.yml +17 -0
  7. data/.github/workflows/doc.yml +23 -0
  8. data/.rspec +0 -1
  9. data/.rubocop.yml +57 -71
  10. data/.yardopts +0 -1
  11. data/README.md +32 -34
  12. data/Rakefile +2 -3
  13. data/lib/networkx/auxillary_functions/cliques.rb +9 -12
  14. data/lib/networkx/auxillary_functions/cycles.rb +17 -7
  15. data/lib/networkx/auxillary_functions/dag.rb +10 -5
  16. data/lib/networkx/auxillary_functions/eccentricity.rb +2 -1
  17. data/lib/networkx/auxillary_functions/mis.rb +2 -2
  18. data/lib/networkx/auxillary_functions/mst.rb +1 -3
  19. data/lib/networkx/auxillary_functions/union_find.rb +92 -12
  20. data/lib/networkx/auxillary_functions/wiener.rb +1 -1
  21. data/lib/networkx/converters/to_csv.rb +1 -3
  22. data/lib/networkx/converters/to_json.rb +0 -2
  23. data/lib/networkx/digraph.rb +55 -49
  24. data/lib/networkx/flow/capacityscaling.rb +29 -35
  25. data/lib/networkx/flow/edmondskarp.rb +17 -15
  26. data/lib/networkx/flow/preflowpush.rb +29 -32
  27. data/lib/networkx/flow/shortestaugmentingpath.rb +17 -20
  28. data/lib/networkx/flow/utils.rb +6 -27
  29. data/lib/networkx/graph.rb +179 -72
  30. data/lib/networkx/link_analysis/hits.rb +9 -9
  31. data/lib/networkx/link_analysis/pagerank.rb +29 -31
  32. data/lib/networkx/multidigraph.rb +90 -81
  33. data/lib/networkx/multigraph.rb +91 -63
  34. data/lib/networkx/operators/all.rb +8 -4
  35. data/lib/networkx/operators/binary.rb +106 -128
  36. data/lib/networkx/operators/product.rb +61 -64
  37. data/lib/networkx/operators/unary.rb +1 -1
  38. data/lib/networkx/others/bridges.rb +30 -0
  39. data/lib/networkx/others/generators.rb +237 -0
  40. data/lib/networkx/others/grid_2d_graph.rb +38 -0
  41. data/lib/networkx/others/info.rb +11 -0
  42. data/lib/networkx/others/number_connected_components.rb +17 -0
  43. data/lib/networkx/others/reads.rb +52 -0
  44. data/lib/networkx/shortest_path/astar.rb +10 -8
  45. data/lib/networkx/shortest_path/dense.rb +1 -3
  46. data/lib/networkx/shortest_path/unweighted.rb +13 -16
  47. data/lib/networkx/shortest_path/weighted.rb +51 -42
  48. data/lib/networkx/to_matrix.rb +2 -3
  49. data/lib/networkx/traversals/bfs.rb +54 -2
  50. data/lib/networkx/traversals/dfs.rb +62 -6
  51. data/lib/networkx/traversals/edge_dfs.rb +36 -12
  52. data/lib/networkx/version.rb +1 -1
  53. data/lib/networkx.rb +7 -1
  54. data/networkx.gemspec +17 -14
  55. metadata +74 -84
  56. data/.rspec_formatter.rb +0 -24
  57. data/.travis.yml +0 -18
  58. data/Guardfile +0 -7
  59. data/RELEASE_POLICY.md +0 -20
@@ -1,19 +1,15 @@
1
- # TODO: Reduce module length
2
-
3
1
  module NetworkX
4
2
  # Helper function to extract weight from a adjecency hash
5
3
  def self.get_weight(graph)
6
- weight_get = lambda do |_, _, attrs|
4
+ lambda do |_, _, attrs|
7
5
  return attrs[:weight] || 1 unless graph.multigraph?
6
+
8
7
  attrs.group_by { |_k, vals| vals[:weight] || 1 }.keys.max
9
8
  end
10
- weight_get
11
9
  end
12
10
 
13
- # TODO: Reduce method length and method complexity
14
-
15
11
  # Helper function for multisource dijkstra
16
- def self.help_multisource_dijkstra(graph, sources, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
12
+ def self.help_multisource_dijkstra(graph, sources, weight, pred = nil, paths = nil, cutoff = nil, target = nil)
17
13
  count = ->(i) { i + 1 }
18
14
  i = -1
19
15
  dist = {}
@@ -26,17 +22,21 @@ module NetworkX
26
22
 
27
23
  until fringe.empty?
28
24
  d, _, v = fringe.pop
29
- next if dist.key?(v)
25
+ next if dist.has_key?(v)
26
+
30
27
  dist[v] = d
31
28
  break if v == target
29
+
32
30
  graph.adj[v].each do |u, attrs|
33
31
  cost = weight.call(v, u, attrs)
34
32
  next if cost.nil?
33
+
35
34
  vu_dist = dist[v] + cost
36
35
  next if !cutoff.nil? && vu_dist > cutoff
37
- if dist.key?(u)
36
+
37
+ if dist.has_key?(u)
38
38
  raise ValueError, 'Contradictory weights found!' if vu_dist < dist[u]
39
- elsif !seen.key?(u) || vu_dist < seen[u]
39
+ elsif !seen.has_key?(u) || vu_dist < seen[u]
40
40
  seen[u] = vu_dist
41
41
  fringe << [vu_dist, count.call(i), u]
42
42
  paths[u] = paths[v] + [u] unless paths.nil?
@@ -50,7 +50,7 @@ module NetworkX
50
50
  end
51
51
 
52
52
  # Helper function for single source dijkstra
53
- def self.help_dijkstra(graph, source, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
53
+ def self.help_dijkstra(graph, source, weight, pred = nil, paths = nil, cutoff = nil, target = nil)
54
54
  help_multisource_dijkstra(graph, [source], weight, pred, paths, cutoff, target)
55
55
  end
56
56
 
@@ -62,15 +62,17 @@ module NetworkX
62
62
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
63
63
  #
64
64
  # @return [Numeric, Array<Object>] path lengths for all nodes
65
- def self.multisource_dijkstra(graph, sources, target=nil, cutoff=nil)
65
+ def self.multisource_dijkstra(graph, sources, target = nil, cutoff = nil)
66
66
  raise ValueError, 'Sources cannot be empty' if sources.empty?
67
67
  return [0, [target]] if sources.include?(target)
68
+
68
69
  paths = {}
69
70
  weight = get_weight(graph)
70
71
  sources.each { |source| paths[source] = [source] }
71
72
  dist = help_multisource_dijkstra(graph, sources, weight, nil, paths, cutoff, target)
72
73
  return [dist, paths] if target.nil?
73
- raise KeyError, "No path to #{target}!" unless dist.key?(target)
74
+ raise KeyError, "No path to #{target}!" unless dist.has_key?(target)
75
+
74
76
  [dist[target], paths[target]]
75
77
  end
76
78
 
@@ -81,8 +83,9 @@ module NetworkX
81
83
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
82
84
  #
83
85
  # @return [Hash{ Object => Numeric }] path lengths for any nodes from given nodes
84
- def self.multisource_dijkstra_path_length(graph, sources, cutoff=nil)
86
+ def self.multisource_dijkstra_path_length(graph, sources, cutoff = nil)
85
87
  raise ValueError, 'Sources cannot be empty' if sources.empty?
88
+
86
89
  weight = get_weight(graph)
87
90
  help_multisource_dijkstra(graph, sources, weight, nil, nil, cutoff)
88
91
  end
@@ -94,7 +97,7 @@ module NetworkX
94
97
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
95
98
  #
96
99
  # @return [Hash{ Object => Array<Object> }] paths for any nodes from given nodes
97
- def self.multisource_dijkstra_path(graph, sources, cutoff=nil)
100
+ def self.multisource_dijkstra_path(graph, sources, cutoff = nil)
98
101
  _, path = multisource_dijkstra(graph, sources, nil, cutoff)
99
102
  path
100
103
  end
@@ -107,7 +110,7 @@ module NetworkX
107
110
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
108
111
  #
109
112
  # @return [Hash{ Object => Array<Object> }, Array<Object>] paths for all nodes/target node from given node
110
- def self.singlesource_dijkstra(graph, source, target=nil, cutoff=nil)
113
+ def self.singlesource_dijkstra(graph, source, target = nil, cutoff = nil)
111
114
  multisource_dijkstra(graph, [source], target, cutoff)
112
115
  end
113
116
 
@@ -118,7 +121,7 @@ module NetworkX
118
121
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
119
122
  #
120
123
  # @return [Hash{ Object => Numeric }] path lengths for all nodes from given node
121
- def self.singlesource_dijkstra_path_length(graph, source, cutoff=nil)
124
+ def self.singlesource_dijkstra_path_length(graph, source, cutoff = nil)
122
125
  multisource_dijkstra_path_length(graph, [source], cutoff)
123
126
  end
124
127
 
@@ -129,7 +132,7 @@ module NetworkX
129
132
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
130
133
  #
131
134
  # @return [Hash{ Object => Array<Object> }] paths for all nodes from given node
132
- def self.singlesource_dijkstra_path(graph, source, cutoff=nil)
135
+ def self.singlesource_dijkstra_path(graph, source, cutoff = nil)
133
136
  multisource_dijkstra_path(graph, [source], cutoff)
134
137
  end
135
138
 
@@ -142,9 +145,11 @@ module NetworkX
142
145
  # @return [Numeric] path length for target node from given node
143
146
  def self.dijkstra_path_length(graph, source, target)
144
147
  return 0 if source == target
148
+
145
149
  weight = get_weight(graph)
146
150
  length = help_dijkstra(graph, source, weight, nil, nil, nil, target)
147
- raise KeyError, 'Node not reachable!' unless length.key?(target)
151
+ raise KeyError, 'Node not reachable!' unless length.has_key?(target)
152
+
148
153
  length[target]
149
154
  end
150
155
 
@@ -168,7 +173,7 @@ module NetworkX
168
173
  #
169
174
  # @return [<Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>]
170
175
  # predcessor hash and distance hash
171
- def self.dijkstra_predecessor_distance(graph, source, cutoff=nil)
176
+ def self.dijkstra_predecessor_distance(graph, source, cutoff = nil)
172
177
  weight = get_weight(graph)
173
178
  pred = {source => []}
174
179
  [pred, help_dijkstra(graph, source, weight, pred, nil, cutoff)]
@@ -181,7 +186,7 @@ module NetworkX
181
186
  #
182
187
  # @return [Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>]
183
188
  # paths and path lengths between all nodes
184
- def self.all_pairs_dijkstra(graph, cutoff=nil)
189
+ def self.all_pairs_dijkstra(graph, cutoff = nil)
185
190
  path = []
186
191
  graph.nodes.each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
187
192
  path
@@ -193,7 +198,7 @@ module NetworkX
193
198
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
194
199
  #
195
200
  # @return [Array<Object, Hash{ Object => Numeric }>] path lengths between all nodes
196
- def self.all_pairs_dijkstra_path_length(graph, cutoff=nil)
201
+ def self.all_pairs_dijkstra_path_length(graph, cutoff = nil)
197
202
  path_lengths = []
198
203
  graph.nodes.each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
199
204
  path_lengths
@@ -205,16 +210,14 @@ module NetworkX
205
210
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
206
211
  #
207
212
  # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths between all nodes
208
- def self.all_pairs_dijkstra_path(graph, cutoff=nil)
213
+ def self.all_pairs_dijkstra_path(graph, cutoff = nil)
209
214
  paths = []
210
215
  graph.nodes.each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
211
216
  paths
212
217
  end
213
218
 
214
- # TODO: Reduce method length and method complexity
215
-
216
219
  # Helper function for bellman ford
217
- def self.help_bellman_ford(graph, sources, weight, pred=nil, paths=nil, dist=nil, cutoff=nil, target=nil)
220
+ def self.help_bellman_ford(graph, sources, weight, pred = nil, paths = nil, dist = nil, cutoff = nil, target = nil)
218
221
  pred = sources.product([[]]).to_h if pred.nil?
219
222
  dist = sources.product([0]).to_h if dist.nil?
220
223
 
@@ -225,28 +228,31 @@ module NetworkX
225
228
  skip = false
226
229
  pred[u].each { |k| skip = true if in_q.include?(k) }
227
230
  next if skip
231
+
228
232
  dist_u = dist[u]
229
233
  graph.adj[u].each do |v, e|
230
234
  dist_v = dist_u + weight.call(u, v, e)
231
235
  next if !cutoff.nil? && dist_v > cutoff
232
236
  next if !target.nil? && dist_v > (dist[target] || inf)
237
+
233
238
  if dist_v < (dist[v] || inf)
234
239
  unless in_q.include?(v)
235
240
  q << v
236
241
  in_q.add(v)
237
242
  count_v = (count[v] || 0) + 1
238
243
  raise ArgumentError, 'Negative edge cycle detected!' if count_v == n
244
+
239
245
  count[v] = count_v
240
246
  end
241
247
  dist[v] = dist_v
242
248
  pred[v] = [u]
243
- elsif dist.key?(v) && dist_v == dist[v]
249
+ elsif dist.has_key?(v) && dist_v == dist[v]
244
250
  pred[v] << u
245
251
  end
246
252
  end
247
253
  end
248
254
  unless paths.nil?
249
- dsts = target.nil? ? pred : [target]
255
+ dsts = pred
250
256
  dsts.each_key do |dst|
251
257
  path, cur = [dst], dst
252
258
  until pred[cur][0].nil?
@@ -268,24 +274,28 @@ module NetworkX
268
274
  # @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
269
275
  #
270
276
  # @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predecessors and distances
271
- def self.bellmanford_predecesor_distance(graph, source, target=nil, cutoff=nil)
277
+ def self.bellmanford_predecesor_distance(graph, source, target = nil, cutoff = nil)
272
278
  raise ArgumentError, 'Node not found!' unless graph.node?(source)
279
+
273
280
  weight = get_weight(graph)
274
281
  # TODO: Detection of selfloop edges
275
282
  dist = {source => 0}
276
283
  pred = {source => []}
277
284
  return [pred, dist] if graph.nodes.length == 1
285
+
278
286
  dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target)
279
287
  [pred, dist]
280
288
  end
281
289
 
282
- def self.singlesource_bellmanford(graph, source, target=nil, cutoff=nil)
290
+ def self.singlesource_bellmanford(graph, source, target = nil, cutoff = nil)
283
291
  return [0, [source]] if source == target
292
+
284
293
  weight = get_weight(graph)
285
294
  paths = {source => [source]}
286
295
  dist = help_bellman_ford(graph, [source], weight, nil, paths, nil, cutoff, target)
287
296
  return [dist, paths] if target.nil?
288
- raise ArgumentError, 'Node not reachable!' unless dist.key?(target)
297
+ raise ArgumentError, 'Node not reachable!' unless dist.has_key?(target)
298
+
289
299
  [dist[target], paths[target]]
290
300
  end
291
301
 
@@ -298,9 +308,11 @@ module NetworkX
298
308
  # @return [Numeric] distance between source and target
299
309
  def self.bellmanford_path_length(graph, source, target)
300
310
  return 0 if source == target
311
+
301
312
  weight = get_weight(graph)
302
- length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target=target)
303
- raise ArgumentError, 'Node not reachable!' unless length.key?(target)
313
+ length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target)
314
+ raise ArgumentError, 'Node not reachable!' unless length.has_key?(target)
315
+
304
316
  length[target]
305
317
  end
306
318
 
@@ -323,7 +335,7 @@ module NetworkX
323
335
  # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
324
336
  #
325
337
  # @return [Hash{ Object => Array<Object> }] path from source to all nodes
326
- def self.singlesource_bellmanford_path(graph, source, cutoff=nil)
338
+ def self.singlesource_bellmanford_path(graph, source, cutoff = nil)
327
339
  _, path = singlesource_bellmanford(graph, source, cutoff)
328
340
  path
329
341
  end
@@ -335,7 +347,7 @@ module NetworkX
335
347
  # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
336
348
  #
337
349
  # @return [Hash{ Object => Numeric }] path lengths from source to all nodes
338
- def self.singlesource_bellmanford_path_length(graph, source, cutoff=nil)
350
+ def self.singlesource_bellmanford_path_length(graph, source, cutoff = nil)
339
351
  weight = get_weight(graph)
340
352
  help_bellman_ford(graph, [source], weight, nil, nil, nil, cutoff)
341
353
  end
@@ -346,7 +358,7 @@ module NetworkX
346
358
  # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
347
359
  #
348
360
  # @return [Array<Object, Hash{ Object => Numeric }>] path lengths from source to all nodes
349
- def self.allpairs_bellmanford_path_length(graph, cutoff=nil)
361
+ def self.allpairs_bellmanford_path_length(graph, cutoff = nil)
350
362
  path_lengths = []
351
363
  graph.nodes.each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
352
364
  path_lengths
@@ -358,7 +370,7 @@ module NetworkX
358
370
  # @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
359
371
  #
360
372
  # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths from source to all nodes
361
- def self.allpairs_bellmanford_path(graph, cutoff=nil)
373
+ def self.allpairs_bellmanford_path(graph, cutoff = nil)
362
374
  paths = []
363
375
  graph.nodes.each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
364
376
  paths
@@ -385,8 +397,6 @@ module NetworkX
385
397
  path_lengths
386
398
  end
387
399
 
388
- # TODO: Reduce method length and method complexity
389
-
390
400
  # Returns shortest path between all pairs of nodes
391
401
  #
392
402
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
@@ -399,10 +409,9 @@ module NetworkX
399
409
  dist[n], pred[n] = 0, []
400
410
  end
401
411
  weight = get_weight(graph)
402
- dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist=dist)
412
+ dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist)
403
413
  new_weight = ->(u, v, d) { weight.call(u, v, d) + dist_bellman[u] - dist_bellman[v] }
404
414
  dist_path = dist_path_lambda(graph, new_weight)
405
- path_lengths = set_path_lengths_johnson(graph, dist_path, new_weight)
406
- path_lengths
415
+ set_path_lengths_johnson(graph, dist_path, new_weight)
407
416
  end
408
417
  end
@@ -1,11 +1,10 @@
1
1
  module NetworkX
2
- # TODO: Reduce condition branches and method length
3
- def self.to_matrix(graph, val, multigraph_weight='sum')
2
+ def self.to_matrix(graph, val, multigraph_weight = 'sum')
4
3
  is_undirected = !graph.directed?
5
4
  is_multigraph = graph.multigraph?
6
5
  nodelen = graph.nodes.length
7
6
 
8
- m = NMatrix.new(nodelen, val)
7
+ m = Matrix.build(nodelen) { val }
9
8
  index = {}
10
9
  inv_index = {}
11
10
  ind = 0
@@ -1,6 +1,4 @@
1
1
  module NetworkX
2
- # TODO: Reduce method length
3
-
4
2
  # Returns edges of the graph travelled in breadth first fashion
5
3
  #
6
4
  # @example
@@ -10,6 +8,7 @@ module NetworkX
10
8
  # @param source [Object] node to start bfs from
11
9
  def self.bfs_edges(graph, source)
12
10
  raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source)
11
+
13
12
  bfs_edges = []
14
13
  visited = [source]
15
14
  queue = Queue.new.push([source, graph.neighbours(source)])
@@ -17,6 +16,7 @@ module NetworkX
17
16
  parent, children = queue.pop
18
17
  children.each_key do |child|
19
18
  next if visited.include?(child)
19
+
20
20
  bfs_edges << [parent, child]
21
21
  visited << child
22
22
  queue.push([child, graph.neighbours(child)])
@@ -55,4 +55,56 @@ module NetworkX
55
55
  bfs_edges.each { |u, v| predecessors[v] = u }
56
56
  predecessors
57
57
  end
58
+
59
+ class Graph
60
+ def bfs_nodes(root)
61
+ each_bfs_node(root).to_a
62
+ end
63
+
64
+ def each_bfs_node(root)
65
+ return enum_for(:each_bfs_node, root) unless block_given?
66
+
67
+ queue = [root]
68
+ dist = {root => 0}
69
+ while (v = queue.shift)
70
+ yield v
71
+ d = dist[v]
72
+ @adj[v].each do |u, _data|
73
+ next if dist[u]
74
+
75
+ dist[u] = d + 1
76
+ queue << u
77
+ end
78
+ end
79
+ end
80
+
81
+ # [EXPERIMENTAL]
82
+ #
83
+ # @param [Object] node which is root, start ,source
84
+ def bfs_edges(node)
85
+ each_bfs_edge(node).to_a
86
+ end
87
+
88
+ # [EXPERIMENTAL]
89
+ #
90
+ # @param [Object] node which is root, start ,source
91
+ def each_bfs_edge(node)
92
+ return enum_for(:each_bfs_edge, node) unless block_given?
93
+
94
+ que = [node]
95
+ used = {node => true}
96
+ while que[0]
97
+ node = que.shift
98
+
99
+ @adj[node].each do |v, _data|
100
+ next if used[v]
101
+
102
+ used[v] = true
103
+
104
+ yield(node, v)
105
+ que << v
106
+ end
107
+ end
108
+ end
109
+ end
58
110
  end
@@ -1,6 +1,4 @@
1
1
  module NetworkX
2
- # TODO: Reduce method complexity and method length
3
-
4
2
  # Returns edges of the graph travelled in depth first fashion
5
3
  #
6
4
  # @example
@@ -9,8 +7,10 @@ module NetworkX
9
7
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
10
8
  # @param source [Object] node to start dfs from
11
9
  # @param depth_limit [Integer, nil] the depth limit of dfs
12
- def self.dfs_edges(graph, source, depth_limit=nil)
10
+ def self.dfs_edges(graph, source, depth_limit = nil)
13
11
  raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source)
12
+
13
+ depth_limit += 1 if depth_limit
14
14
  depth_limit = graph.nodes.length if depth_limit.nil?
15
15
  dfs_edges = []
16
16
  visited = [source]
@@ -37,7 +37,7 @@ module NetworkX
37
37
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
38
38
  # @param source [Object] node to start dfs from
39
39
  # @param depth_limit [Integer, nil] the depth limit of dfs
40
- def self.dfs_tree(graph, source, depth_limit=nil)
40
+ def self.dfs_tree(graph, source, depth_limit = nil)
41
41
  t = NetworkX::DiGraph.new
42
42
  t.add_node(source)
43
43
  t.add_edges_from(dfs_edges(graph, source, depth_limit))
@@ -52,7 +52,7 @@ module NetworkX
52
52
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
53
53
  # @param source [Object] node to start dfs from
54
54
  # @param depth_limit [Integer, nil] the depth limit of dfs
55
- def self.dfs_successors(graph, source, depth_limit=nil)
55
+ def self.dfs_successors(graph, source, depth_limit = nil)
56
56
  dfs_edges = dfs_edges(graph, source, depth_limit)
57
57
  successors = {}
58
58
  dfs_edges.each do |u, v|
@@ -70,10 +70,66 @@ module NetworkX
70
70
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
71
71
  # @param source [Object] node to start dfs from
72
72
  # @param depth_limit [Integer, nil] the depth limit of dfs
73
- def self.dfs_predecessors(graph, source, depth_limit=nil)
73
+ def self.dfs_predecessors(graph, source, depth_limit = nil)
74
74
  dfs_edges = dfs_edges(graph, source, depth_limit)
75
75
  predecessors = {}
76
76
  dfs_edges.each { |u, v| predecessors[v] = u }
77
77
  predecessors
78
78
  end
79
+
80
+ class Graph
81
+ # [EXPERIMENTAL]
82
+ #
83
+ # @param root [Object] node which is root, start, source
84
+ #
85
+ # @return [Array[Object]] nodes
86
+ def dfs_preorder_nodes(root)
87
+ each_dfs_preorder_node(root).to_a
88
+ end
89
+
90
+ # [EXPERIMENTAL]
91
+ #
92
+ # @param root [Object] node which is root, start, source
93
+ def each_dfs_preorder_node(root)
94
+ return enum_for(:each_dfs_preorder_node, root) unless block_given?
95
+
96
+ st = [root]
97
+ used = {root => true}
98
+ while st[-1]
99
+ node = st.pop
100
+ yield(node)
101
+ @adj[node].reverse_each do |v, _data|
102
+ next if used[v]
103
+
104
+ used[v] = node
105
+ st << v
106
+ end
107
+ end
108
+ end
109
+
110
+ # [EXPERIMENTAL]
111
+ #
112
+ # @param root [Object] node which is root, start, source
113
+ #
114
+ # @return [Array[Object]] array of dfs postorder nodes
115
+ def dfs_postorder_nodes(root, used = {root => true})
116
+ res = []
117
+ @adj[root].each do |v, _data|
118
+ next if used[v]
119
+
120
+ used[v] = true
121
+ res.concat dfs_postorder_nodes(v, used)
122
+ end
123
+
124
+ res << root
125
+ res
126
+ end
127
+
128
+ # @param root [Object] node which is root, start, source
129
+ def each_dfs_postorder_node(root, &block)
130
+ return enum_for(:each_dfs_postorder_node, root) unless block_given?
131
+
132
+ dfs_postorder_nodes(root).each(&block)
133
+ end
134
+ end
79
135
  end
@@ -1,6 +1,4 @@
1
1
  module NetworkX
2
- # TODO: Reduce method complexity and method length
3
-
4
2
  # Helper function for edge_dfs
5
3
  #
6
4
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
@@ -11,7 +9,7 @@ module NetworkX
11
9
  case graph.class.name
12
10
  when 'NetworkX::Graph', 'NetworkX::DiGraph'
13
11
  graph.adj[node].each do |v, _|
14
- if graph.class.name == 'NetworkX::DiGraph' || visited[[v, node]].nil?
12
+ if graph.instance_of?(::NetworkX::DiGraph) || visited[[v, node]].nil?
15
13
  visited[[node, v]] = true
16
14
  edges << [node, v]
17
15
  end
@@ -19,7 +17,7 @@ module NetworkX
19
17
  else
20
18
  graph.adj[node].each do |v, uv_keys|
21
19
  uv_keys.each_key do |k|
22
- if graph.class.name == 'NetworkX::MultiDiGraph' || visited[[v, node, k]].nil?
20
+ if graph.instance_of?(::NetworkX::MultiDiGraph) || visited[[v, node, k]].nil?
23
21
  visited[[node, v, k]] = true
24
22
  edges << [node, v, k]
25
23
  end
@@ -32,12 +30,11 @@ module NetworkX
32
30
  # Helper function of edge_dfs
33
31
  def self.edge_id(graph, edge)
34
32
  return edge if graph.directed?
35
- return Set.new([edge, (edge[0..1].reverse + edge[2])]) if graph.multigraph?
33
+ return Set.new([edge, (edge[0..1].reverse << edge[2])]) if graph.multigraph?
34
+
36
35
  Set.new([edge, edge.reverse])
37
36
  end
38
37
 
39
- # TODO: Reduce method complexity and method length
40
-
41
38
  # Performs edge dfs on the graph
42
39
  # Orientation :ignore, directed edges can be
43
40
  # travelled in both fashions
@@ -49,15 +46,15 @@ module NetworkX
49
46
  # NetworkX.edge_dfs(graph, source, 'ignore')
50
47
  #
51
48
  # @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
52
- # @param source [Object] node to start dfs from
49
+ # @param start [Object] node to start dfs from
53
50
  # @param orientation [:ignore, :reverse', nil] the orientation of edges of graph
54
- def self.edge_dfs(graph, start, orientation=nil)
51
+ def self.edge_dfs(graph, start, orientation = nil)
55
52
  case orientation
56
53
  when :reverse
57
- graph = graph.reverse if graph.class.name == 'NetworkX::DiGraph' || graph.class.name == 'NetworkX::MultiDiGraph'
54
+ graph = graph.reverse if graph.instance_of?(::NetworkX::DiGraph) || graph.instance_of?(::NetworkX::MultiDiGraph)
58
55
  when :ignore
59
- graph = graph.to_undirected if graph.class.name == 'NetworkX::DiGraph'
60
- graph = graph.to_multigraph if graph.class.name == 'NetworkX::MultiDiGraph'
56
+ graph = graph.to_undirected if graph.instance_of?(::NetworkX::DiGraph)
57
+ graph = graph.to_multigraph if graph.instance_of?(::NetworkX::MultiDiGraph)
61
58
  end
62
59
 
63
60
  visited_edges = []
@@ -87,4 +84,31 @@ module NetworkX
87
84
  end
88
85
  e.take(graph.number_of_edges)
89
86
  end
87
+
88
+ class Graph
89
+ def dfs_edges(node)
90
+ each_dfs_edge(node).to_a
91
+ end
92
+
93
+ def each_dfs_edge(node)
94
+ return enum_for(:each_dfs_edge, node) unless block_given?
95
+
96
+ st = [node]
97
+ used = {}
98
+ parents = {}
99
+ while st[-1]
100
+ node = st.pop
101
+
102
+ yield(parents[node], node) if parents[node]
103
+
104
+ used[node] = true
105
+ @adj[node].reverse_each do |v, _data|
106
+ next if used[v]
107
+
108
+ parents[v] = node
109
+ st << v unless used[v]
110
+ end
111
+ end
112
+ end
113
+ end
90
114
  end
@@ -1,3 +1,3 @@
1
1
  module NetworkX
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
data/lib/networkx.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'set'
2
- require 'nmatrix'
2
+ require 'matrix'
3
3
  require 'rb_heap'
4
4
  require 'securerandom'
5
5
  require_relative 'networkx/version'
@@ -35,3 +35,9 @@ require_relative 'networkx/auxillary_functions/wiener'
35
35
  require_relative 'networkx/auxillary_functions/cycles'
36
36
  require_relative 'networkx/link_analysis/hits'
37
37
  require_relative 'networkx/link_analysis/pagerank'
38
+ require_relative 'networkx/others/bridges'
39
+ require_relative 'networkx/others/generators'
40
+ require_relative 'networkx/others/grid_2d_graph'
41
+ require_relative 'networkx/others/info'
42
+ require_relative 'networkx/others/number_connected_components'
43
+ require_relative 'networkx/others/reads'
data/networkx.gemspec CHANGED
@@ -13,26 +13,29 @@ Gem::Specification.new do |spec|
13
13
  spec.email = ['athityakumar@gmail.com']
14
14
  spec.summary = 'A Ruby implemenation of the well-known graph library called networkx.'
15
15
  spec.description = NetworkX::DESCRIPTION
16
- spec.homepage = 'https://github.com/athityakumar/networkx.rb'
16
+ spec.homepage = 'https://github.com/SciRuby/networkx.rb'
17
17
  spec.license = 'MIT'
18
18
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
19
  spec.bindir = 'bin'
20
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_development_dependency 'bundler', '~> 1.15'
24
- spec.add_development_dependency 'rake', '~> 10.0'
25
- spec.add_development_dependency 'redcarpet'
23
+ spec.required_ruby_version = '>= 2.7'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 2.0'
26
+ spec.add_development_dependency 'rake', '~> 13.0'
26
27
  spec.add_development_dependency 'rspec', '~> 3.0'
27
- spec.add_development_dependency 'rspec-its'
28
- spec.add_development_dependency 'rubocop', '>= 0.40.0'
29
- spec.add_development_dependency 'rubocop-rspec'
30
- spec.add_development_dependency 'rubygems-tasks'
31
- spec.add_development_dependency 'saharspec'
32
- spec.add_development_dependency 'simplecov'
33
- spec.add_development_dependency 'yard'
34
- spec.add_runtime_dependency 'nmatrix'
35
- spec.add_runtime_dependency 'rb_heap'
28
+ spec.add_development_dependency 'rspec-its', '~> 1.0'
29
+ spec.add_development_dependency 'rubocop', '~> 1.0'
30
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
31
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.0'
32
+ spec.add_development_dependency 'rubygems-tasks', '~> 0.2'
33
+ spec.add_development_dependency 'saharspec', '~> 0.0'
34
+ spec.add_development_dependency 'simplecov', '~> 0.21'
35
+ spec.add_development_dependency 'yard', '~> 0.9'
36
+
37
+ spec.add_runtime_dependency 'matrix', '~> 0.4'
38
+ spec.add_runtime_dependency 'rb_heap', '~> 1.0'
36
39
 
37
- spec.add_development_dependency 'guard-rspec' if RUBY_VERSION >= '2.2.5'
40
+ spec.metadata['rubygems_mfa_required'] = 'true'
38
41
  end