networkx 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e86cfdb083751f6aa9d45d894dd4a83ec75623acd237c6cdc8e7cd356306409
4
- data.tar.gz: 60ef78a9da3df8d7f61e297af48873795b21d9675138e953dd160597d9bfdc09
3
+ metadata.gz: 4bce7e05d9da7dd9774ad976cb82c8de0bd5847be9fb0dfc07af1042568446a9
4
+ data.tar.gz: '085683454ca374c50f594509e13da7c3114131e54936a11f6daf4dd3f4924b29'
5
5
  SHA512:
6
- metadata.gz: a7d64db11e72ecd05bba646d89116da859caa2e48b9063b158780e625c7fdf802cd840c3aace32a00314762805a27fa1373b36795462d7640f4ed7385466d403
7
- data.tar.gz: 46886910fc5f3bb550fb089af740bb5c0e32cd5a1ff3dd2394a13256ddc3f6b4e9f32c48c37d2d827b2bc8edba36cd0894201aec833a2fb4d59318f7fb85b7ae
6
+ metadata.gz: 91cdd246c4692b87a90405676f58a330257ecc02d6cdc9e55c79428afc4d22a9d621fd175d6408e0a7f6aafec50deaa8cd2eb9a0d5ef49360befd5ae8e88dd30
7
+ data.tar.gz: 79bade11b9709a78102bb83d13d77a3abe661fbaf75bdc18dbc5f822e71d6c5ecd53c8b7c46324cf9d6eda1df570f299c77191e8079426c50e4fd5b3f87e7ffd
data/.rubocop.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
- TargetRubyVersion: 3.0
3
+ TargetRubyVersion: 2.7
4
4
  Include:
5
5
  - 'lib/**/*'
6
6
  - 'spec/**/*_spec.rb'
7
7
  - 'Gemfile'
8
8
  - 'Rakefile'
9
- - '*.gemspace'
9
+ - '*.gemspec'
10
10
  DisplayCopNames: true
11
11
 
12
12
  require:
data/README.md CHANGED
@@ -1,23 +1,37 @@
1
1
  # NetworkX.rb
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/networkx.svg)](https://badge.fury.io/rb/networkx)
4
+
3
5
  [NetworkX](https://networkx.github.io/) is a very popular Python library, that handles various use-cases of the Graph Data Structure.
4
6
  This project intends to provide a working alternative to the Ruby community, by closely mimicing as many features as possible.
5
7
 
6
8
  ## List of contents
7
9
 
8
10
  - [Installing](#installing)
11
+ - [Usage](#Usage)
9
12
  - [Document](#document)
10
- - [Roadmap](#roadmap)
11
13
  - [Contributing](#contributing)
12
14
  - [License](#license)
13
15
 
14
16
  ## Installing
15
17
 
16
- - Clone the repository or fork
17
- - Clone this repository with `git clone https://github.com/SciRuby/networkx.rb.git`
18
- - or You can fork and do clone it.
19
- - Navigate to networkx with `cd networkx.rb`
20
- - Install dependencies with `gem install bundler && bundle install`
18
+ - install it yourself as:
19
+ ```console
20
+ $ gem install networkx
21
+ ```
22
+
23
+
24
+ - Or use Bundler & Gemfile
25
+ 1. add this line to your application's Gemfile:
26
+ ```ruby
27
+ gem 'networkx'
28
+ ```
29
+ 2. And then execute:
30
+ ```console
31
+ $ bundle install
32
+ ```
33
+
34
+ ## Usage
21
35
 
22
36
  ```ruby
23
37
  require 'networkx'
@@ -30,22 +44,6 @@ g.add_edge('start', 'stop')
30
44
 
31
45
  You can read [Document](https://SciRuby.github.io/networkx.rb/) for this library.
32
46
 
33
- ## Roadmap
34
-
35
- Quite easily, any networkx user would be able to understand the number of details that have been implemented in the Python library. As a humble start towards the release of v0.1.0, the following could be the goals to achieve :
36
-
37
- - `Node` : This class should be capable of handling different types of nodes (not just `String` / `Integer`).
38
- A possible complex use-case could be XML nodes.
39
-
40
- - `Edge` : This class should be capable of handling different types of edges.
41
- Though a basic undirected Graph doesn't store any metadata in the edges, weighted edges and parametric edges are something that need to be handled.
42
-
43
- - `Graph` : The simplest of graphs.
44
- This class handles just connections between different `Node`s via `Edge`s.
45
-
46
- - `DirectedGraph` : Inherits from `Graph` class.
47
- Uses directions between `Edge`s.
48
-
49
47
  ## Contributing
50
48
 
51
49
  Your contributions are always welcome!
@@ -5,15 +5,15 @@ module NetworkX
5
5
  #
6
6
  # @return [Array<Array<Object>>] Arrays of nodes in the cliques
7
7
  def self.find_cliques(graph)
8
- return nil if graph.nodes.empty?
8
+ return nil if graph.nodes(data: true).empty?
9
9
 
10
10
  q = [nil]
11
11
  adj = {}
12
- graph.nodes.each_key { |u| adj[u] = [] }
12
+ graph.nodes(data: true).each_key { |u| adj[u] = [] }
13
13
  graph.adj.each { |u, u_edges| u_edges.each_key { |v| adj[u] << v if u != v } }
14
14
 
15
- subg = graph.nodes.keys
16
- cand = graph.nodes.keys
15
+ subg = graph.nodes(data: true).keys
16
+ cand = graph.nodes(data: true).keys
17
17
  u = subg.max { |n1, n2| (cand & adj[n1]).length <=> (cand & adj[n2]).length }
18
18
  ext_u = cand - adj[u]
19
19
  stack = []
@@ -6,7 +6,7 @@ module NetworkX
6
6
  #
7
7
  # @return [Array<Array<Object>>] Arrays of nodes in the cycles
8
8
  def self.cycle_basis(graph, root = nil)
9
- gnodes = graph.nodes.keys
9
+ gnodes = graph.nodes(data: true).keys
10
10
  cycles = []
11
11
  until gnodes.empty?
12
12
  root = gnodes.shift if root.nil?
@@ -34,14 +34,14 @@ module NetworkX
34
34
  raise ArgumentError, 'Topological Sort not defined on undirected graphs!' unless graph.directed?
35
35
 
36
36
  nodes = []
37
- indegree_map = graph.nodes.each_key.map do |u|
37
+ indegree_map = graph.nodes(data: true).each_key.map do |u|
38
38
  [u, graph.in_degree(u)] if graph.in_degree(u).positive?
39
39
  end.compact.to_h
40
- zero_indegree = graph.nodes.each_key.select { |u| graph.in_degree(u).zero? }
40
+ zero_indegree = graph.nodes(data: true).each_key.select { |u| graph.in_degree(u).zero? }
41
41
 
42
42
  until zero_indegree.empty?
43
43
  node = zero_indegree.shift
44
- raise ArgumentError, 'Graph changed during iteration!' unless graph.nodes.has_key?(node)
44
+ raise ArgumentError, 'Graph changed during iteration!' unless graph.nodes(data: true).has_key?(node)
45
45
 
46
46
  graph.adj[node].each_key do |child|
47
47
  indegree_map[child] -= 1
@@ -7,10 +7,10 @@ module NetworkX
7
7
  # @return [Array<Numeric>, Numeric] eccentricity/eccentricites of all nodes
8
8
  def self.eccentricity(graph, node = nil)
9
9
  e = {}
10
- graph.nodes.each do |u, _|
10
+ graph.nodes(data: true).each do |u, _|
11
11
  length = single_source_shortest_path_length(graph, u)
12
12
  l = length.length
13
- raise ArgumentError, 'Found infinite path length!' unless l == graph.nodes.length
13
+ raise ArgumentError, 'Found infinite path length!' unless l == graph.nodes(data: true).length
14
14
 
15
15
  e[u] = length.max_by { |a| a[1] }[1]
16
16
  end
@@ -6,13 +6,15 @@ module NetworkX
6
6
  #
7
7
  # @return [Numeric] radius of the graph
8
8
  def self.maximal_independent_set(graph, nodes)
9
- raise 'The array containing the nodes should be a subset of the graph!' if (graph.nodes.keys - nodes).empty?
9
+ if (graph.nodes(data: true).keys - nodes).empty?
10
+ raise 'The array containing the nodes should be a subset of the graph!'
11
+ end
10
12
 
11
13
  neighbours = []
12
14
  nodes.each { |u| graph.adj[u].each { |v, _| neighbours |= [v] } }
13
15
  raise 'Nodes is not an independent set of graph!' if (neighbours - nodes).empty?
14
16
 
15
- available_nodes = graph.nodes.keys - (neighbours | nodes)
17
+ available_nodes = graph.nodes(data: true).keys - (neighbours | nodes)
16
18
  until available_nodes.empty?
17
19
  node = available_nodes.sample
18
20
  nodes << node
@@ -20,8 +20,8 @@ module NetworkX
20
20
  mst = Marshal.load(Marshal.dump(graph))
21
21
  mst.clear
22
22
  edges = get_edges_weights(graph).sort_by { |a| a[1] }
23
- union_find = UnionFind.new(graph.nodes.keys)
24
- while edges.any? && mst.nodes.length <= graph.nodes.length
23
+ union_find = UnionFind.new(graph.nodes(data: true).keys)
24
+ while edges.any? && mst.nodes(data: true).length <= graph.nodes(data: true).length
25
25
  edge = edges.shift
26
26
  unless union_find.connected?(edge[0][0], edge[0][1])
27
27
  union_find.union(edge[0][0], edge[0][1])
@@ -7,7 +7,7 @@ module NetworkX
7
7
  # @return [Numeric] closeness vitality of the given node
8
8
  def self.closeness_vitality(graph, node)
9
9
  before = wiener_index(graph)
10
- after = wiener_index(graph.subgraph(graph.nodes.keys - [node]))
10
+ after = wiener_index(graph.subgraph(graph.nodes(data: true).keys - [node]))
11
11
  before - after
12
12
  end
13
13
  end
@@ -10,7 +10,7 @@ module NetworkX
10
10
  csv << graph.graph.keys
11
11
  csv << graph.graph.values
12
12
  csv << ['graph_nodes']
13
- graph.nodes.each do |u, attrs|
13
+ graph.nodes(data: true).each do |u, attrs|
14
14
  node_attrs = [u]
15
15
  attrs.each do |k, v|
16
16
  node_attrs << k
@@ -11,7 +11,7 @@ module NetworkX
11
11
  # @return [Boolean] whether there exists a negative cycle in graph
12
12
  def self.negative_edge_cycle(graph)
13
13
  newnode = generate_unique_node
14
- graph.add_edges(graph.nodes.keys.map { |n| [newnode, n] })
14
+ graph.add_edges(graph.nodes(data: true).keys.map { |n| [newnode, n] })
15
15
  begin
16
16
  bellmanford_predecesor_distance(graph, newnode)
17
17
  rescue ArgumentError
@@ -25,10 +25,10 @@ module NetworkX
25
25
  # Detects the unboundedness in the residual graph
26
26
  def self._detect_unboundedness(residual)
27
27
  g = NetworkX::DiGraph.new
28
- g.add_nodes(residual.nodes.keys.zip(residual.nodes.values))
28
+ g.add_nodes(residual.nodes(data: true).keys.zip(residual.nodes(data: true).values))
29
29
  inf = residual.graph[:inf]
30
30
 
31
- residual.nodes.each do |u, _attr|
31
+ residual.nodes(data: true).each do |u, _attr|
32
32
  residual.adj[u].each do |v, uv_attrs|
33
33
  w = inf
34
34
  uv_attrs.each { |_key, edge_attrs| w = [w, edge_attrs[:weight]].min if edge_attrs[:capacity] == inf }
@@ -41,10 +41,10 @@ module NetworkX
41
41
  # Returns the residual graph of the given graph
42
42
  def self._build_residual_network(graph)
43
43
  raise ArgumentError, 'Sum of demands should be 0!' unless \
44
- graph.nodes.values.map { |attr| attr[:demand] || 0 }.inject(0, :+).zero?
44
+ graph.nodes(data: true).values.map { |attr| attr[:demand] || 0 }.inject(0, :+).zero?
45
45
 
46
46
  residual = NetworkX::MultiDiGraph.new(inf: 0)
47
- residual.add_nodes(graph.nodes.map { |u, attr| [u, {excess: (attr[:demand] || 0) * -1, potential: 0}] })
47
+ residual.add_nodes(graph.nodes(data: true).map { |u, attr| [u, {excess: (attr[:demand] || 0) * -1, potential: 0}] })
48
48
  inf = Float::INFINITY
49
49
  edge_list = []
50
50
 
@@ -66,9 +66,11 @@ module NetworkX
66
66
  end
67
67
  end
68
68
 
69
- temp_inf = [residual.nodes.map { |_u, attrs| attrs[:excess].abs }.inject(0, :+), edge_list.map do |_, _, _, e|
70
- (e.has_key?(:capacity) && e[:capacity] != inf ? e[:capacity] : 0)
71
- end.inject(0, :+) * 2].max
69
+ temp_inf = [residual.nodes(data: true).map do |_u, attrs|
70
+ attrs[:excess].abs
71
+ end.inject(0, :+), edge_list.map do |_, _, _, e|
72
+ (e.has_key?(:capacity) && e[:capacity] != inf ? e[:capacity] : 0)
73
+ end.inject(0, :+) * 2].max
72
74
  inf = temp_inf.zero? ? 1 : temp_inf
73
75
 
74
76
  edge_list.each do |u, v, k, e|
@@ -88,7 +90,7 @@ module NetworkX
88
90
  inf = Float::INFINITY
89
91
 
90
92
  if graph.multigraph?
91
- graph.nodes.each_key do |u|
93
+ graph.nodes(data: true).each_key do |u|
92
94
  flow_dict[u] = {}
93
95
  graph.adj[u].each do |v, uv_edges|
94
96
  flow_dict[u][v] = uv_edges.transform_values do |e|
@@ -102,7 +104,7 @@ module NetworkX
102
104
  end
103
105
  end
104
106
  else
105
- graph.nodes.each_key do |u|
107
+ graph.nodes(data: true).each_key do |u|
106
108
  flow_dict[u] = graph.adj[u].to_h do |v, e|
107
109
  [v, u != v || (e[:capacity] || inf) <= 0 || (e[:weight] || 0) >= 0 ? 0 : e[:capacity]]
108
110
  end
@@ -163,7 +165,7 @@ module NetworkX
163
165
  s_set = Set.new
164
166
  t_set = Set.new
165
167
 
166
- residual.nodes.each do |u, _attrs|
168
+ residual.nodes(data: true).each do |u, _attrs|
167
169
  excess = r_nodes[u][:excess]
168
170
  if excess >= delta
169
171
  s_set.add(u)
@@ -236,7 +238,7 @@ module NetworkX
236
238
 
237
239
  r_nodes.each_value { |attrs| raise ArgumentError, 'No flow satisfying all demands!' if attrs[:excess] != 0 }
238
240
 
239
- residual.nodes.each_key do |node|
241
+ residual.nodes(data: true).each_key do |node|
240
242
  residual.adj[node].each_value do |uv_edges|
241
243
  uv_edges.each_value do |k_attrs|
242
244
  flow = k_attrs[:flow]
@@ -84,8 +84,8 @@ module NetworkX
84
84
 
85
85
  # Helper function for the edmondskarp function
86
86
  def self.edmondskarp_impl(graph, source, target, residual, cutoff)
87
- raise ArgumentError, 'Source not in graph!' unless graph.nodes.has_key?(source)
88
- raise ArgumentError, 'Target not in graph!' unless graph.nodes.has_key?(target)
87
+ raise ArgumentError, 'Source not in graph!' unless graph.nodes(data: true).has_key?(source)
88
+ raise ArgumentError, 'Target not in graph!' unless graph.nodes(data: true).has_key?(target)
89
89
  raise ArgumentError, 'Source and target are same node!' if source == target
90
90
 
91
91
  res_graph = residual.nil? ? build_residual_network(graph) : residual.clone
@@ -12,8 +12,8 @@ module NetworkX
12
12
 
13
13
  # Helper function to apply the preflow push algorithm
14
14
  def self.preflowpush_impl(graph, source, target, residual, globalrelabel_freq, value_only)
15
- raise ArgumentError, 'Source not in graph!' unless graph.nodes.has_key?(source)
16
- raise ArgumentError, 'Target not in graph!' unless graph.nodes.has_key?(target)
15
+ raise ArgumentError, 'Source not in graph!' unless graph.nodes(data: true).has_key?(source)
16
+ raise ArgumentError, 'Target not in graph!' unless graph.nodes(data: true).has_key?(target)
17
17
  raise ArgumentError, 'Source and Target are same!' if source == target
18
18
 
19
19
  globalrelabel_freq = 0 if globalrelabel_freq.nil?
@@ -38,7 +38,7 @@ module NetworkX
38
38
  return r_network
39
39
  end
40
40
 
41
- n = r_network.nodes.length
41
+ n = r_network.nodes(data: true).length
42
42
  max_height = heights.map { |u, h| u == source ? -1 : h }.max
43
43
  heights[source] = n
44
44
 
@@ -1,8 +1,8 @@
1
1
  module NetworkX
2
2
  # Helper function for running the shortest augmenting path algorithm
3
3
  def self.shortest_augmenting_path_impl(graph, source, target, residual, two_phase, cutoff)
4
- raise ArgumentError, 'Source is not in the graph!' unless graph.nodes.has_key?(source)
5
- raise ArgumentError, 'Target is not in the graph!' unless graph.nodes.has_key?(target)
4
+ raise ArgumentError, 'Source is not in the graph!' unless graph.nodes(data: true).has_key?(source)
5
+ raise ArgumentError, 'Target is not in the graph!' unless graph.nodes(data: true).has_key?(target)
6
6
  raise ArgumentError, 'Source and Target are the same!' if source == target
7
7
 
8
8
  residual = residual.nil? ? build_residual_network(graph) : residual
@@ -35,7 +35,7 @@ module NetworkX
35
35
  return residual
36
36
  end
37
37
 
38
- n = graph.nodes.length
38
+ n = graph.nodes(data: true).length
39
39
  m = residual.size / 2
40
40
 
41
41
  r_nodes.each do |node, attrs|
@@ -62,7 +62,7 @@ module NetworkX
62
62
  raise NotImplementedError, 'MultiGraph and MultiDiGraph not supported!' if graph.multigraph?
63
63
 
64
64
  r_network = NetworkX::DiGraph.new(inf: 0, flow_value: 0)
65
- r_network.add_nodes(graph.nodes.keys)
65
+ r_network.add_nodes(graph.nodes(data: true).keys)
66
66
  inf = Float::INFINITY
67
67
  edge_list = []
68
68
 
@@ -213,14 +213,13 @@ module NetworkX
213
213
  end
214
214
  end
215
215
 
216
- # [TODO] Current default of `data` is true.
217
- # [Alert] Change the default in the futher. Please specify the `data`.
216
+ # Return nodes of graph
218
217
  #
219
218
  # @param data [bool] true if you want data of each edge
220
219
  #
221
220
  # @return [Hash | Array] if data is true, it returns hash including data.
222
221
  # otherwise, simple nodes array.
223
- def nodes(data: true)
222
+ def nodes(data: false)
224
223
  if data
225
224
  @nodes
226
225
  else
@@ -8,7 +8,7 @@ module NetworkX
8
8
  #
9
9
  # @return [Array<Numeric, Numeric>] hits and authority scores
10
10
  def self.hits(graph, max_iter = 100, tol = 1e-8, nstart)
11
- return [{}, {}] if graph.nodes.empty?
11
+ return [{}, {}] if graph.nodes(data: true).empty?
12
12
 
13
13
  h = nstart
14
14
  sum = h.values.sum
@@ -2,61 +2,16 @@ module NetworkX
2
2
  # Computes pagerank values for the graph
3
3
  #
4
4
  # @param graph [Graph] a graph
5
- # @param init [Array<Numeric>] initial pagerank values for the nodes
6
5
  # @param alpha [Numeric] the alpha value to compute the pagerank
7
6
  # @param eps [Numeric] tolerence to check for convergence
8
7
  # @param max_iter [Integer] max iterations for the pagerank algorithm to run
9
8
  #
10
- # @return [Array<Numeric>] pagerank values of the graph
11
- def self.pagerank(graph, init = nil, alpha = 0.85, eps = 1e-4, max_iter = 100)
12
- dim = graph.nodes.length
13
- if init.nil?
14
- init = graph.nodes(data: false).to_h{ |i| [i, 1.0 / dim] }
15
- else
16
- s = init.values.sum.to_f
17
- init = init.transform_values { |v| v / s }
18
- end
19
- raise ArgumentError, 'Init array needs to have same length as number of graph nodes!' \
20
- unless dim == init.length
21
-
22
- matrix = []
23
- elem_ind = {}
24
- p = []
25
- curr = init.values
26
- init.keys.each_with_index { |n, i| elem_ind[n] = i }
27
- graph.adj.each do |_u, u_edges|
28
- adj_arr = Array.new(dim, 0)
29
- u_edges.each do |v, _|
30
- adj_arr[elem_ind[v]] = 1
31
- end
32
- matrix << adj_arr
33
- end
34
- (0..(dim - 1)).each do |i|
35
- p[i] = []
36
- (0..(dim - 1)).each { |j| p[i][j] = matrix[i][j] / (matrix[i].sum * 1.0) }
37
- end
38
-
39
- max_iter.times do |_|
40
- prev = curr.clone
41
- dim.times do |i|
42
- ip = 0
43
- dim.times { |j| ip += p.transpose[i][j] * prev[j] }
44
- curr[i] = (alpha * ip) + ((1 - alpha) / (dim * 1.0))
45
- end
46
- err = 0
47
- dim.times { |i| err += (prev[i] - curr[i]).abs }
48
- return curr if err < eps
49
- end
50
- raise ArgumentError, 'PageRank failed to converge!'
51
- end
52
-
53
- def self.pagerank2(graph, alpha: 0.85, personalization: nil, eps: 1e-6, max_iter: 100)
9
+ # @return [Hash of Object => Float] pagerank values of the graph
10
+ def self.pagerank(graph, alpha: 0.85, personalization: nil, eps: 1e-6, max_iter: 100)
54
11
  n = graph.number_of_nodes
55
12
 
56
13
  matrix, index_to_node = NetworkX.to_matrix(graph, 0)
57
14
 
58
- # index_to_node = {0=>0, 1=>1, 2=>2, 3=>3}
59
-
60
15
  index_from_node = index_to_node.invert
61
16
 
62
17
  probabilities = Array.new(n) do |i|
@@ -84,6 +39,7 @@ module NetworkX
84
39
  err = (0...n).map{|i| (prev[i] - curr[i]).abs }.sum
85
40
  return (0...n).map{|i| [index_to_node[i], curr[i]] }.sort.to_h if err < eps
86
41
  end
42
+ warn "pagerank() failed within #{max_iter} iterations. Please inclease max_iter: or loosen eps:"
87
43
  (0...n).map{|i| [index_to_node[i], curr[i]] }.sort.to_h
88
44
  end
89
45
  end
@@ -27,12 +27,12 @@ module NetworkX
27
27
  def self.convert_to_distinct_labels(graph, starting_int = -1)
28
28
  new_graph = graph.class.new
29
29
 
30
- idx_dict = graph.nodes.keys.to_h do |v|
30
+ idx_dict = graph.nodes(data: true).keys.to_h do |v|
31
31
  starting_int += 1
32
32
  [v, starting_int]
33
33
  end
34
34
 
35
- graph.nodes.each do |u, attrs|
35
+ graph.nodes(data: true).each do |u, attrs|
36
36
  new_graph.add_node(u.to_s + idx_dict[u].to_s, **attrs)
37
37
  end
38
38
 
@@ -60,9 +60,12 @@ module NetworkX
60
60
  result = g1.class.new
61
61
 
62
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
63
 
65
- g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
64
+ unless (g1.nodes(data: true).keys - g2.nodes(data: true).keys).empty?
65
+ raise ArgumentError, 'Node sets must be equal!'
66
+ end
67
+
68
+ g1.nodes(data: true).each { |u, attrs| result.add_node(u, **attrs) }
66
69
 
67
70
  g1, g2 = g2, g1 if g1.number_of_edges > g2.number_of_edges
68
71
  g1.adj.each do |u, u_edges|
@@ -91,9 +94,12 @@ module NetworkX
91
94
  result = g1.class.new
92
95
 
93
96
  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
97
 
96
- g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
98
+ unless (g1.nodes(data: true).keys - g2.nodes(data: true).keys).empty?
99
+ raise ArgumentError, 'Node sets must be equal!'
100
+ end
101
+
102
+ g1.nodes(data: true).each { |u, attrs| result.add_node(u, **attrs) }
97
103
 
98
104
  g1.adj.each do |u, u_edges|
99
105
  u_edges.each do |v, uv_attrs|
@@ -121,9 +127,12 @@ module NetworkX
121
127
  result = g1.class.new
122
128
 
123
129
  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
130
 
126
- g1.nodes.each { |u, attrs| result.add_node(u, **attrs) }
131
+ unless (g1.nodes(data: true).keys - g2.nodes(data: true).keys).empty?
132
+ raise ArgumentError, 'Node sets must be equal!'
133
+ end
134
+
135
+ g1.nodes(data: true).each { |u, attrs| result.add_node(u, **attrs) }
127
136
 
128
137
  g1.adj.each do |u, u_edges|
129
138
  u_edges.each do |v, uv_attrs|
@@ -166,8 +175,8 @@ module NetworkX
166
175
 
167
176
  raise ArgumentError, 'Arguments must be both Graphs or MultiGraphs!' unless g1.multigraph? == g2.multigraph?
168
177
 
169
- result.add_nodes(g1.nodes.map { |u, attrs| [u, attrs] })
170
- result.add_nodes(g2.nodes.map { |u, attrs| [u, attrs] })
178
+ result.add_nodes(g1.nodes(data: true).map { |u, attrs| [u, attrs] })
179
+ result.add_nodes(g2.nodes(data: true).map { |u, attrs| [u, attrs] })
171
180
 
172
181
  if g1.multigraph?
173
182
  g1.adj.each { |u, e| e.each { |v, uv_edges| uv_edges.each_value { |attrs| result.add_edge(u, v, **attrs) } } }
@@ -192,14 +201,16 @@ module NetworkX
192
201
  new_graph.graph.merge!(g1.graph)
193
202
  new_graph.graph.merge!(g2.graph)
194
203
 
195
- raise ArgumentError, 'Graphs must be disjoint!' unless (g1.nodes.keys & g2.nodes.keys).empty?
204
+ unless (g1.nodes(data: true).keys & g2.nodes(data: true).keys).empty?
205
+ raise ArgumentError, 'Graphs must be disjoint!'
206
+ end
196
207
 
197
208
  g1_edges = get_edges(g1)
198
209
  g2_edges = get_edges(g2)
199
210
 
200
- new_graph.add_nodes(g1.nodes.keys)
211
+ new_graph.add_nodes(g1.nodes(data: true).keys)
201
212
  new_graph.add_edges(g1_edges)
202
- new_graph.add_nodes(g2.nodes.keys)
213
+ new_graph.add_nodes(g2.nodes(data: true).keys)
203
214
  new_graph.add_edges(g2_edges)
204
215
 
205
216
  new_graph
@@ -28,8 +28,8 @@ module NetworkX
28
28
  # Returns the node product of nodes of two graphs
29
29
  def self.node_product(g1, g2)
30
30
  n_product = []
31
- g1.nodes.each do |k1, attrs1|
32
- g2.nodes.each do |k2, attrs2|
31
+ g1.nodes(data: true).each do |k1, attrs1|
32
+ g2.nodes(data: true).each do |k2, attrs2|
33
33
  n_product << [[k1, k2], hash_product(attrs1, attrs2)]
34
34
  end
35
35
  end
@@ -62,7 +62,7 @@ module NetworkX
62
62
  def self.edges_cross_nodes(g1, g2)
63
63
  result = []
64
64
  edges_in_array(g1).each do |u, v, d|
65
- g2.nodes.each_key do |x|
65
+ g2.nodes(data: true).each_key do |x|
66
66
  result << [[u, x], [v, x], d]
67
67
  end
68
68
  end
@@ -72,7 +72,7 @@ module NetworkX
72
72
  # Returns the product of directed nodes with edges
73
73
  def self.nodes_cross_edges(g1, g2)
74
74
  result = []
75
- g1.nodes.each_key do |x|
75
+ g1.nodes(data: true).each_key do |x|
76
76
  edges_in_array(g2).each do |u, v, d|
77
77
  result << [[x, u], [x, v], d]
78
78
  end
@@ -84,8 +84,8 @@ module NetworkX
84
84
  def self.edges_cross_nodes_and_nodes(g1, g2)
85
85
  result = []
86
86
  edges_in_array(g1).each do |u, v, d|
87
- g2.nodes.each_key do |x|
88
- g2.nodes.each_key do |y|
87
+ g2.nodes(data: true).each_key do |x|
88
+ g2.nodes(data: true).each_key do |y|
89
89
  result << [[u, x], [v, y], d]
90
90
  end
91
91
  end
@@ -174,8 +174,8 @@ module NetworkX
174
174
  raise ArgumentError, 'Power must be a positive quantity!' if pow <= 0
175
175
 
176
176
  result = NetworkX::Graph.new
177
- result.add_nodes(graph.nodes.map { |n, attrs| [n, attrs] })
178
- graph.nodes.each do |n, _attrs|
177
+ result.add_nodes(graph.nodes(data: true).map { |n, attrs| [n, attrs] })
178
+ graph.nodes(data: true).each do |n, _attrs|
179
179
  seen = {}
180
180
  level = 1
181
181
  next_level = graph.adj[n]
@@ -8,9 +8,9 @@ module NetworkX
8
8
  result = Marshal.load(Marshal.dump(graph))
9
9
  result.clear
10
10
 
11
- result.add_nodes(graph.nodes.map { |u, attrs| [u, attrs] })
11
+ result.add_nodes(graph.nodes(data: true).map { |u, attrs| [u, attrs] })
12
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 }
13
+ graph.nodes(data: true).each { |v, attrs| result.add_edge(u, v, **attrs) if !u_edges.has_key?(v) && u != v }
14
14
  end
15
15
  result
16
16
  end
@@ -7,7 +7,7 @@ module NetworkX
7
7
  # b/w all pairs of nodes
8
8
  def self.floyd_warshall(graph)
9
9
  a, index = to_matrix(graph, Float::INFINITY, 'min')
10
- nodelen = graph.nodes.length
10
+ nodelen = graph.nodes(data: true).length
11
11
  (0..(nodelen - 1)).each { |i| a[i, i] = 0 }
12
12
  (0..(nodelen - 1)).each do |k|
13
13
  (0..(nodelen - 1)).each do |i|
@@ -34,7 +34,7 @@ module NetworkX
34
34
 
35
35
  cutoff = Float::INFINITY if cutoff.nil?
36
36
  nextlevel = {source => 1}
37
- help_single_shortest_path_length(graph.adj, nextlevel, cutoff).take(graph.nodes.length)
37
+ help_single_shortest_path_length(graph.adj, nextlevel, cutoff).take(graph.nodes(data: true).length)
38
38
  end
39
39
 
40
40
  # Computes shortest path values to all nodes from all nodes
@@ -45,7 +45,7 @@ module NetworkX
45
45
  # @return [Array<Object, Array<Object, Numeric>>] path lengths for all nodes from all nodes
46
46
  def self.all_pairs_shortest_path_length(graph, cutoff = nil)
47
47
  shortest_paths = []
48
- graph.nodes.each_key { |n| shortest_paths << [n, single_source_shortest_path_length(graph, n, cutoff)] }
48
+ graph.nodes(data: true).each_key { |n| shortest_paths << [n, single_source_shortest_path_length(graph, n, cutoff)] }
49
49
  shortest_paths
50
50
  end
51
51
 
@@ -93,7 +93,7 @@ module NetworkX
93
93
  # @return [Array<Object, Hash {Object => Array<Object> }>] paths for all nodes from all nodes
94
94
  def self.all_pairs_shortest_path(graph, cutoff = nil)
95
95
  shortest_paths = []
96
- graph.nodes.each_key { |n| shortest_paths << [n, single_source_shortest_path(graph, n, cutoff)] }
96
+ graph.nodes(data: true).each_key { |n| shortest_paths << [n, single_source_shortest_path(graph, n, cutoff)] }
97
97
  shortest_paths
98
98
  end
99
99
 
@@ -188,7 +188,7 @@ module NetworkX
188
188
  # paths and path lengths between all nodes
189
189
  def self.all_pairs_dijkstra(graph, cutoff = nil)
190
190
  path = []
191
- graph.nodes.each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
191
+ graph.nodes(data: true).each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
192
192
  path
193
193
  end
194
194
 
@@ -200,7 +200,7 @@ module NetworkX
200
200
  # @return [Array<Object, Hash{ Object => Numeric }>] path lengths between all nodes
201
201
  def self.all_pairs_dijkstra_path_length(graph, cutoff = nil)
202
202
  path_lengths = []
203
- graph.nodes.each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
203
+ graph.nodes(data: true).each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
204
204
  path_lengths
205
205
  end
206
206
 
@@ -212,7 +212,7 @@ module NetworkX
212
212
  # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths between all nodes
213
213
  def self.all_pairs_dijkstra_path(graph, cutoff = nil)
214
214
  paths = []
215
- graph.nodes.each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
215
+ graph.nodes(data: true).each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
216
216
  paths
217
217
  end
218
218
 
@@ -221,7 +221,7 @@ module NetworkX
221
221
  pred = sources.product([[]]).to_h if pred.nil?
222
222
  dist = sources.product([0]).to_h if dist.nil?
223
223
 
224
- inf, n, count, q, in_q = Float::INFINITY, graph.nodes.length, {}, sources.clone, Set.new(sources)
224
+ inf, n, count, q, in_q = Float::INFINITY, graph.nodes(data: true).length, {}, sources.clone, Set.new(sources)
225
225
  until q.empty?
226
226
  u = q.shift
227
227
  in_q.delete(u)
@@ -281,7 +281,7 @@ module NetworkX
281
281
  # TODO: Detection of selfloop edges
282
282
  dist = {source => 0}
283
283
  pred = {source => []}
284
- return [pred, dist] if graph.nodes.length == 1
284
+ return [pred, dist] if graph.nodes(data: true).length == 1
285
285
 
286
286
  dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target)
287
287
  [pred, dist]
@@ -360,7 +360,7 @@ module NetworkX
360
360
  # @return [Array<Object, Hash{ Object => Numeric }>] path lengths from source to all nodes
361
361
  def self.allpairs_bellmanford_path_length(graph, cutoff = nil)
362
362
  path_lengths = []
363
- graph.nodes.each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
363
+ graph.nodes(data: true).each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
364
364
  path_lengths
365
365
  end
366
366
 
@@ -372,13 +372,13 @@ module NetworkX
372
372
  # @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths from source to all nodes
373
373
  def self.allpairs_bellmanford_path(graph, cutoff = nil)
374
374
  paths = []
375
- graph.nodes.each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
375
+ graph.nodes(data: true).each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
376
376
  paths
377
377
  end
378
378
 
379
379
  # Helper function to get sources
380
380
  def self.get_sources(graph)
381
- graph.nodes.collect { |k, _v| k }
381
+ graph.nodes(data: true).collect { |k, _v| k }
382
382
  end
383
383
 
384
384
  # Helper function to get distances
@@ -393,7 +393,7 @@ module NetworkX
393
393
  # Helper function to set path lengths for Johnson algorithm
394
394
  def self.set_path_lengths_johnson(graph, dist_path, new_weight)
395
395
  path_lengths = []
396
- graph.nodes.each_key { |n| path_lengths << [n, dist_path.call(graph, n, new_weight)] }
396
+ graph.nodes(data: true).each_key { |n| path_lengths << [n, dist_path.call(graph, n, new_weight)] }
397
397
  path_lengths
398
398
  end
399
399
 
@@ -405,7 +405,7 @@ module NetworkX
405
405
  def self.johnson(graph)
406
406
  dist, pred = {}, {}
407
407
  sources = get_sources(graph)
408
- graph.nodes.each_key do |n|
408
+ graph.nodes(data: true).each_key do |n|
409
409
  dist[n], pred[n] = 0, []
410
410
  end
411
411
  weight = get_weight(graph)
@@ -2,14 +2,14 @@ module NetworkX
2
2
  def self.to_matrix(graph, val, multigraph_weight = 'sum')
3
3
  is_undirected = !graph.directed?
4
4
  is_multigraph = graph.multigraph?
5
- nodelen = graph.nodes.length
5
+ nodelen = graph.nodes(data: true).length
6
6
 
7
7
  m = Matrix.build(nodelen) { val }
8
8
  index = {}
9
9
  inv_index = {}
10
10
  ind = 0
11
11
 
12
- graph.nodes.each do |u, _|
12
+ graph.nodes(data: true).each do |u, _|
13
13
  index[u] = ind
14
14
  inv_index[ind] = u
15
15
  ind += 1
@@ -11,7 +11,7 @@ module NetworkX
11
11
  raise KeyError, "There exists no node names #{source} in the given graph." unless graph.node?(source)
12
12
 
13
13
  depth_limit += 1 if depth_limit
14
- depth_limit = graph.nodes.length if depth_limit.nil?
14
+ depth_limit = graph.nodes(data: true).length if depth_limit.nil?
15
15
  dfs_edges = []
16
16
  visited = [source]
17
17
  stack = [[-1, source, depth_limit, graph.neighbours(source)]]
@@ -1,3 +1,3 @@
1
1
  module NetworkX
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
data/networkx.gemspec CHANGED
@@ -13,13 +13,15 @@ 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.required_ruby_version = '>= 2.7'
24
+
23
25
  spec.add_development_dependency 'bundler', '~> 2.0'
24
26
  spec.add_development_dependency 'rake', '~> 13.0'
25
27
  spec.add_development_dependency 'rspec', '~> 3.0'
@@ -34,4 +36,6 @@ Gem::Specification.new do |spec|
34
36
 
35
37
  spec.add_runtime_dependency 'matrix', '~> 0.4'
36
38
  spec.add_runtime_dependency 'rb_heap', '~> 1.0'
39
+
40
+ spec.metadata['rubygems_mfa_required'] = 'true'
37
41
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: networkx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Athitya Kumar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-25 00:00:00.000000000 Z
11
+ date: 2022-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -207,7 +207,6 @@ files:
207
207
  - ".github/PULL_REQUEST_TEMPLATE.md"
208
208
  - ".github/workflows/ci.yml"
209
209
  - ".github/workflows/doc.yml"
210
- - ".github/workflows/gem-push.yml"
211
210
  - ".gitignore"
212
211
  - ".rspec"
213
212
  - ".rubocop.yml"
@@ -259,10 +258,11 @@ files:
259
258
  - lib/networkx/traversals/edge_dfs.rb
260
259
  - lib/networkx/version.rb
261
260
  - networkx.gemspec
262
- homepage: https://github.com/athityakumar/networkx.rb
261
+ homepage: https://github.com/SciRuby/networkx.rb
263
262
  licenses:
264
263
  - MIT
265
- metadata: {}
264
+ metadata:
265
+ rubygems_mfa_required: 'true'
266
266
  post_install_message:
267
267
  rdoc_options: []
268
268
  require_paths:
@@ -271,7 +271,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
271
271
  requirements:
272
272
  - - ">="
273
273
  - !ruby/object:Gem::Version
274
- version: '0'
274
+ version: '2.7'
275
275
  required_rubygems_version: !ruby/object:Gem::Requirement
276
276
  requirements:
277
277
  - - ">="
@@ -1,45 +0,0 @@
1
- name: Ruby Gem
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
-
9
- jobs:
10
- build:
11
- name: Build + Publish
12
- runs-on: ubuntu-latest
13
- permissions:
14
- contents: read
15
- packages: write
16
-
17
- steps:
18
- - uses: actions/checkout@v2
19
- - name: Set up Ruby 2.6
20
- uses: actions/setup-ruby@v1
21
- with:
22
- ruby-version: 2.6.x
23
-
24
- - name: Publish to GPR
25
- run: |
26
- mkdir -p $HOME/.gem
27
- touch $HOME/.gem/credentials
28
- chmod 0600 $HOME/.gem/credentials
29
- printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
30
- gem build *.gemspec
31
- gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
32
- env:
33
- GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
34
- OWNER: ${{ github.repository_owner }}
35
-
36
- - name: Publish to RubyGems
37
- run: |
38
- mkdir -p $HOME/.gem
39
- touch $HOME/.gem/credentials
40
- chmod 0600 $HOME/.gem/credentials
41
- printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
42
- gem build *.gemspec
43
- gem push *.gem
44
- env:
45
- GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"