networkx 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) 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} +21 -11
  4. data/.github/ISSUE_TEMPLATE.md +15 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  6. data/.github/workflows/ci.yml +17 -0
  7. data/.github/workflows/doc.yml +23 -0
  8. data/.github/workflows/gem-push.yml +45 -0
  9. data/.rspec +0 -1
  10. data/.rubocop.yml +56 -63
  11. data/.yardopts +0 -1
  12. data/README.md +27 -27
  13. data/Rakefile +2 -3
  14. data/lib/networkx/auxillary_functions/cliques.rb +62 -0
  15. data/lib/networkx/auxillary_functions/cycles.rb +114 -0
  16. data/lib/networkx/auxillary_functions/dag.rb +59 -0
  17. data/lib/networkx/auxillary_functions/eccentricity.rb +37 -0
  18. data/lib/networkx/auxillary_functions/mis.rb +23 -0
  19. data/lib/networkx/auxillary_functions/mst.rb +33 -0
  20. data/lib/networkx/auxillary_functions/union_find.rb +104 -0
  21. data/lib/networkx/auxillary_functions/vitality.rb +13 -0
  22. data/lib/networkx/auxillary_functions/wiener.rb +13 -0
  23. data/lib/networkx/converters/to_csv.rb +45 -0
  24. data/lib/networkx/converters/to_json.rb +37 -0
  25. data/lib/networkx/digraph.rb +234 -0
  26. data/lib/networkx/flow/capacityscaling.rb +249 -0
  27. data/lib/networkx/flow/edmondskarp.rb +115 -0
  28. data/lib/networkx/flow/preflowpush.rb +249 -0
  29. data/lib/networkx/flow/shortestaugmentingpath.rb +154 -0
  30. data/lib/networkx/flow/utils.rb +139 -0
  31. data/lib/networkx/graph.rb +448 -0
  32. data/lib/networkx/link_analysis/hits.rb +59 -0
  33. data/lib/networkx/link_analysis/pagerank.rb +89 -0
  34. data/lib/networkx/multidigraph.rb +249 -0
  35. data/lib/networkx/multigraph.rb +199 -0
  36. data/lib/networkx/operators/all.rb +65 -0
  37. data/lib/networkx/operators/binary.rb +222 -0
  38. data/lib/networkx/operators/product.rb +201 -0
  39. data/lib/networkx/operators/unary.rb +17 -0
  40. data/lib/networkx/others/bridges.rb +30 -0
  41. data/lib/networkx/others/generators.rb +237 -0
  42. data/lib/networkx/others/grid_2d_graph.rb +38 -0
  43. data/lib/networkx/others/info.rb +11 -0
  44. data/lib/networkx/others/number_connected_components.rb +17 -0
  45. data/lib/networkx/others/reads.rb +52 -0
  46. data/lib/networkx/shortest_path/astar.rb +73 -0
  47. data/lib/networkx/shortest_path/dense.rb +29 -0
  48. data/lib/networkx/shortest_path/unweighted.rb +136 -0
  49. data/lib/networkx/shortest_path/weighted.rb +417 -0
  50. data/lib/networkx/to_matrix.rb +51 -0
  51. data/lib/networkx/traversals/bfs.rb +110 -0
  52. data/lib/networkx/traversals/dfs.rb +135 -0
  53. data/lib/networkx/traversals/edge_dfs.rb +114 -0
  54. data/lib/networkx/version.rb +1 -1
  55. data/lib/networkx.rb +43 -1
  56. data/networkx.gemspec +14 -12
  57. metadata +118 -62
  58. data/.rspec_formatter.rb +0 -24
  59. data/.travis.yml +0 -18
  60. data/Guardfile +0 -7
@@ -0,0 +1,249 @@
1
+ module NetworkX
2
+ # Describes the class for making MultiDiGraphs
3
+ #
4
+ # @attr_reader adj [Hash{ Object => Hash{ Object => Hash{ Integer => Hash{ Object => Object } } } }]
5
+ # Stores the edges and their attributes in an adjencency list form
6
+ # @attr_reader pred [Hash{ Object => Hash{ Object => Hash{ Integer => Hash{ Object => Object } } } }]
7
+ # Stores the reverse edges and their attributes in an adjencency list form
8
+ # @attr_reader nodes [Hash{ Object => Hash{ Object => Object } }] Stores the nodes and their attributes
9
+ # @attr_reader graph [Hash{ Object => Object }] Stores the attributes of the graph
10
+ class MultiDiGraph < DiGraph
11
+ # Returns a new key
12
+ #
13
+ # @param node1 [Object] the first node of a given edge
14
+ # @param node2 [Object] the second node of a given edge
15
+ def new_edge_key(node1, node2)
16
+ return 0 if @adj[node1][node2].nil?
17
+
18
+ key = @adj[node1][node2].length
19
+ key += 1 while @adj[node1][node2].has_key?(key)
20
+ key
21
+ end
22
+
23
+ # Adds the respective edge
24
+ #
25
+ # @example Add an edge with attribute name
26
+ # graph.add_edge(node1, node2, name: "Edge1")
27
+ #
28
+ # @example Add an edge with no attribute
29
+ # graph.add_edge("Bangalore", "Chennai")
30
+ #
31
+ # @param node1 [Object] the first node of the edge
32
+ # @param node2 [Object] the second node of the edge
33
+ # @param edge_attrs [Hash{ Object => Object }] the hash of the edge attributes
34
+ def add_edge(node1, node2, **edge_attrs)
35
+ add_node(node1)
36
+ add_node(node2)
37
+ key = new_edge_key(node1, node2)
38
+ all_edge_attrs = @adj[node1][node2] || {}
39
+ all_edge_attrs[key] = edge_attrs
40
+ @adj[node1][node2] = all_edge_attrs
41
+ @pred[node2][node1] = all_edge_attrs
42
+ end
43
+
44
+ # Removes edge from the graph
45
+ #
46
+ # @example
47
+ # graph.remove_edge('Noida', 'Bangalore')
48
+ #
49
+ # @param node1 [Object] the first node of the edge
50
+ # @param node2 [Object] the second node of the edge
51
+ def remove_edge(node1, node2, key = nil)
52
+ return super(node1, node2) if key.nil?
53
+
54
+ raise KeyError, "#{node1} is not a valid node." unless @nodes.has_key?(node1)
55
+ raise KeyError, "#{node2} is not a valid node" unless @nodes.has_key?(node2)
56
+ raise KeyError, 'The given edge is not a valid one.' unless @adj[node1].has_key?(node2)
57
+ if @adj[node1][node2].none? { |_index, data| data[:key] == key }
58
+ raise KeyError, 'The given edge is not a valid one'
59
+ end
60
+
61
+ @adj[node1][node2].delete_if { |_indx, data| data[:key] == key }
62
+ @pred[node2][node1].delete_if { |_indx, data| data[:key] == key }
63
+ end
64
+
65
+ # Checks if the the edge consisting of two nodes is present in the graph
66
+ #
67
+ # @example
68
+ # graph.edge?(node1, node2)
69
+ #
70
+ # @param node1 [Object] the first node of the edge to be checked
71
+ # @param node2 [Object] the second node of the edge to be checked
72
+ # @param key [Integer] the key of the given edge
73
+ def edge?(node1, node2, key = nil)
74
+ return super(node1, node2) if key.nil?
75
+
76
+ node?(node1) && @adj[node1].has_key?(node2) && @adj[node1][node2].has_key?(key)
77
+ end
78
+
79
+ def has_edge?(node1, node2, key = nil)
80
+ return super(node1, node2) if key.nil?
81
+
82
+ return false unless node?(node1) && @adj[node1].has_key?(node2)
83
+
84
+ @adj[node1][node2].any? { |_index, data| data[:key] == key }
85
+ end
86
+
87
+ # Returns the undirected version of the graph
88
+ #
89
+ # @example
90
+ # graph.to_undirected
91
+ def to_undirected
92
+ graph = NetworkX::Graph.new(**@graph)
93
+ @nodes.each { |node, node_attr| graph.add_node(node, **node_attr) }
94
+ @adj.each do |node1, node1_edges|
95
+ node1_edges.each do |node2, node1_node2|
96
+ edge_attrs = {}
97
+ node1_node2.each { |_key, attrs| edge_attrs.merge!(attrs) }
98
+ graph.add_edge(node1, node2, **edge_attrs)
99
+ end
100
+ end
101
+ graph
102
+ end
103
+
104
+ # Returns the directed version of the graph
105
+ #
106
+ # @example
107
+ # graph.to_directed
108
+ def to_directed
109
+ graph = NetworkX::DiGraph.new(**@graph)
110
+ @nodes.each { |node, node_attr| graph.add_node(node, **node_attr) }
111
+ @adj.each do |node1, node1_edges|
112
+ node1_edges.each do |node2, node1_node2|
113
+ edge_attrs = {}
114
+ node1_node2.each { |_key, attrs| edge_attrs.merge!(attrs) }
115
+ graph.add_edge(node1, node2, **edge_attrs)
116
+ end
117
+ end
118
+ graph
119
+ end
120
+
121
+ # Returns the multigraph version of the graph
122
+ #
123
+ # @example
124
+ # graph.to_multigraph
125
+ def to_multigraph
126
+ graph = NetworkX::MultiGraph.new(**@graph)
127
+ @nodes.each { |node, node_attr| graph.add_node(node, **node_attr) }
128
+ @adj.each do |node1, node2_edges|
129
+ node2_edges.each do |node2, edges|
130
+ edges.each { |_key, attrs| graph.add_edge(node1, node2, **attrs) }
131
+ end
132
+ end
133
+ graph
134
+ end
135
+
136
+ # Returns the reversed version of the graph
137
+ #
138
+ # @example
139
+ # graph.reverse
140
+ def reverse
141
+ new_graph = NetworkX::MultiDiGraph.new(**@graph)
142
+ @nodes.each { |node, attrs| new_graph.add_node(node, **attrs) }
143
+ @adj.each do |u, u_edges|
144
+ u_edges.each { |v, uv_attrs| uv_attrs.each { |_k, edge_attrs| new_graph.add_edge(v, u, **edge_attrs) } }
145
+ end
146
+ new_graph
147
+ end
148
+
149
+ # Returns in-degree of a given node
150
+ #
151
+ # @example
152
+ # graph.in_degree(node)
153
+ #
154
+ # @param node [Object] the node whose in degree is to be calculated
155
+ def in_degree(node)
156
+ @pred[node].values.map(&:length).sum
157
+ end
158
+
159
+ # Returns out-degree of a given node
160
+ #
161
+ # @example
162
+ # graph.out_degree(node)
163
+ #
164
+ # @param node [Object] the node whose out degree is to be calculated
165
+ def out_degree(node)
166
+ @adj[node].values.map(&:length).sum
167
+ end
168
+
169
+ # Returns number of edges
170
+ #
171
+ # @example
172
+ # graph.number_of_edges
173
+ def number_of_edges
174
+ @adj.values.flat_map(&:values).map(&:length).sum
175
+ end
176
+
177
+ # Returns the size of the graph
178
+ #
179
+ # @example
180
+ # graph.size(true)
181
+ #
182
+ # @param is_weighted [Bool] if true, method returns sum of weights of all edges
183
+ # else returns number of edges
184
+ def size(is_weighted = false)
185
+ if is_weighted
186
+ graph_size = 0
187
+ @adj.each do |_, hash_val|
188
+ hash_val.each { |_, v| v.each { |_, attrs| graph_size += attrs[:weight] if attrs.has_key?(:weight) } }
189
+ end
190
+ return graph_size
191
+ end
192
+ number_of_edges
193
+ end
194
+
195
+ # Returns subgraph consisting of given array of nodes
196
+ #
197
+ # @example
198
+ # graph.subgraph(%w[Mumbai Nagpur])
199
+ #
200
+ # @param nodes [Array<Object>] the nodes to be included in the subgraph
201
+ def subgraph(nodes)
202
+ case nodes
203
+ when Array, Set
204
+ sub_graph = NetworkX::MultiDiGraph.new(**@graph)
205
+ nodes.each do |u, _|
206
+ raise KeyError, "#{u} does not exist in the current graph!" unless @nodes.has_key?(u)
207
+
208
+ sub_graph.add_node(u, **@nodes[u])
209
+ @adj[u].each do |v, edge_val|
210
+ edge_val.each { |_, keyval| sub_graph.add_edge(u, v, **keyval) if @adj[u].has_key?(v) && nodes.include?(v) }
211
+ end
212
+ end
213
+ sub_graph
214
+ else
215
+ raise ArgumentError, 'Expected Argument to be Array or Set of nodes, ' \
216
+ "received #{nodes.class.name} instead."
217
+ end
218
+ end
219
+
220
+ # Returns subgraph conisting of given edges
221
+ #
222
+ # @example
223
+ # graph.edge_subgraph([%w[Nagpur Wardha], %w[Nagpur Mumbai]])
224
+ #
225
+ # @param edges [Array<Object, Object>] the edges to be included in the subraph
226
+ def edge_subgraph(edges)
227
+ case edges
228
+ when Array, Set
229
+ sub_graph = NetworkX::MultiDiGraph.new(**@graph)
230
+ edges.each do |u, v|
231
+ raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.has_key?(u) \
232
+ && @adj[u].has_key?(v)
233
+
234
+ sub_graph.add_node(u, **@nodes[u])
235
+ sub_graph.add_node(v, **@nodes[v])
236
+ @adj[u][v].each { |_, keyval| sub_graph.add_edge(u, v, **keyval) }
237
+ end
238
+ sub_graph
239
+ else
240
+ raise ArgumentError, 'Expected Argument to be Array or Set of edges, ' \
241
+ "received #{edges.class.name} instead."
242
+ end
243
+ end
244
+
245
+ def multigraph?
246
+ true
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,199 @@
1
+ module NetworkX
2
+ # Describes the class for making MultiGraphs
3
+ #
4
+ # @attr_reader adj [Hash{ Object => Hash{ Object => Hash{ Integer => Hash{ Object => Object } } } }]
5
+ # Stores the edges and their attributes in an adjencency list form
6
+ # @attr_reader nodes [Hash{ Object => Hash{ Object => Object } }] Stores the nodes and their attributes
7
+ # @attr_reader graph [Hash{ Object => Object }] Stores the attributes of the gra
8
+ class MultiGraph < Graph
9
+ # Returns a new key
10
+ #
11
+ # @param node1 [Object] the first node of a given edge
12
+ # @param node2 [Object] the second node of a given edge
13
+ def new_edge_key(node1, node2)
14
+ return 0 if @adj[node1][node2].nil?
15
+
16
+ key = @adj[node1][node2].length
17
+ key += 1 while @adj[node1][node2].has_key?(key)
18
+ key
19
+ end
20
+
21
+ # Adds the respective edge
22
+ #
23
+ # @example Add an edge with attribute name
24
+ # graph.add_edge(node1, node2, name: "Edge1")
25
+ #
26
+ # @example Add an edge with no attribute
27
+ # graph.add_edge("Bangalore", "Chennai")
28
+ #
29
+ # @param node1 [Object] the first node of the edge
30
+ # @param node2 [Object] the second node of the edge
31
+ # @param edge_attrs [Hash{ Object => Object }] the hash of the edge attributes
32
+ def add_edge(node1, node2, **edge_attrs)
33
+ add_node(node1)
34
+ add_node(node2)
35
+ key = new_edge_key(node1, node2)
36
+ all_edge_attrs = @adj[node1][node2] || {}
37
+ all_edge_attrs[key] = edge_attrs
38
+ @adj[node1][node2] = all_edge_attrs
39
+ @adj[node2][node1] = all_edge_attrs
40
+ end
41
+
42
+ # Removes edge from the graph
43
+ #
44
+ # @example
45
+ # graph.remove_edge('Noida', 'Bangalore')
46
+ #
47
+ # @param node1 [Object] the first node of the edge
48
+ # @param node2 [Object] the second node of the edge
49
+ def remove_edge(node1, node2, key = nil)
50
+ return super(node1, node2) if key.nil?
51
+
52
+ raise KeyError, "#{node1} is not a valid node." unless @nodes.has_key?(node1)
53
+ raise KeyError, "#{node2} is not a valid node" unless @nodes.has_key?(node2)
54
+ raise KeyError, 'The given edge is not a valid one.' unless @adj[node1].has_key?(node2)
55
+
56
+ if @adj[node1][node2].none? { |_index, data| data[:key] == key }
57
+ raise KeyError, 'The given edge is not a valid one'
58
+ end
59
+
60
+ @adj[node1][node2].delete_if { |_indx, data| data[:key] == key }
61
+ @adj[node2][node1].delete_if { |_indx, data| data[:key] == key }
62
+ end
63
+
64
+ # Returns the size of the graph
65
+ #
66
+ # @example
67
+ # graph.size(true)
68
+ #
69
+ # @param is_weighted [Bool] if true, method returns sum of weights of all edges
70
+ # else returns number of edges
71
+ def size(is_weighted = false)
72
+ if is_weighted
73
+ graph_size = 0
74
+ @adj.each do |_, hash_val|
75
+ hash_val.each { |_, v| v.each { |_, attrs| graph_size += attrs[:weight] if attrs.has_key?(:weight) } }
76
+ end
77
+ return graph_size / 2
78
+ end
79
+ number_of_edges
80
+ end
81
+
82
+ # Returns number of edges
83
+ #
84
+ # @example
85
+ # graph.number_of_edges
86
+ def number_of_edges
87
+ @adj.values.flat_map(&:values).map(&:length).sum / 2
88
+ end
89
+
90
+ # Checks if the the edge consisting of two nodes is present in the graph
91
+ #
92
+ # @example
93
+ # graph.edge?(node1, node2)
94
+ #
95
+ # @param node1 [Object] the first node of the edge to be checked
96
+ # @param node2 [Object] the second node of the edge to be checked
97
+ # @param key [Integer] the key of the given edge
98
+ def edge?(node1, node2, key = nil)
99
+ return super(node1, node2) if key.nil?
100
+
101
+ node?(node1) && @adj[node1].has_key?(node2) && @adj[node1][node2].has_key?(key)
102
+ end
103
+
104
+ def has_edge?(node1, node2, key = nil)
105
+ return super(node1, node2) if key.nil?
106
+
107
+ return false unless node?(node1) && @adj[node1].has_key?(node2)
108
+
109
+ @adj[node1][node2].any? { |_index, data| data[:key] == key }
110
+ end
111
+
112
+ def each_edge(data: false)
113
+ return enum_for(:each_edge, data: data) unless block_given?
114
+
115
+ @adj.each do |v, ws|
116
+ ws.each do |w, key_and_info|
117
+ next if v > w
118
+
119
+ key_and_info.each do |key, info|
120
+ data ? yield(v, w, key, info) : yield(v, w, key)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ # Returns the undirected version of the graph
127
+ #
128
+ # @example
129
+ # graph.to_undirected
130
+ def to_undirected
131
+ graph = NetworkX::Graph.new(**@graph)
132
+ @nodes.each { |node, node_attr| graph.add_node(node, **node_attr) }
133
+ @adj.each do |node1, node1_edges|
134
+ node1_edges.each do |node2, node1_node2|
135
+ edge_attrs = {}
136
+ node1_node2.each { |_key, attrs| edge_attrs.merge!(attrs) }
137
+ graph.add_edge(node1, node2, **edge_attrs)
138
+ end
139
+ end
140
+ graph
141
+ end
142
+
143
+ # Returns subgraph consisting of given array of nodes
144
+ #
145
+ # @example
146
+ # graph.subgraph(%w[Mumbai Nagpur])
147
+ #
148
+ # @param nodes [Array<Object>] the nodes to be included in the subgraph
149
+ def subgraph(nodes)
150
+ case nodes
151
+ when Array, Set
152
+ sub_graph = NetworkX::MultiGraph.new(**@graph)
153
+ nodes.each do |u, _|
154
+ raise KeyError, "#{u} does not exist in the current graph!" unless @nodes.has_key?(u)
155
+
156
+ sub_graph.add_node(u, **@nodes[u])
157
+ @adj[u].each do |v, edge_val|
158
+ next if u > v
159
+
160
+ edge_val.each { |_, keyval| sub_graph.add_edge(u, v, **keyval) if @adj[u].has_key?(v) && nodes.include?(v) }
161
+ end
162
+ end
163
+ sub_graph
164
+ else
165
+ raise ArgumentError, 'Expected Argument to be Array or Set of nodes, ' \
166
+ "received #{nodes.class.name} instead."
167
+ end
168
+ end
169
+
170
+ # Returns subgraph conisting of given edges
171
+ #
172
+ # @example
173
+ # graph.edge_subgraph([%w[Nagpur Wardha], %w[Nagpur Mumbai]])
174
+ #
175
+ # @param edges [Array<Object, Object>] the edges to be included in the subraph
176
+ def edge_subgraph(edges)
177
+ case edges
178
+ when Array, Set
179
+ sub_graph = NetworkX::MultiGraph.new(**@graph)
180
+ edges.each do |u, v|
181
+ raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.has_key?(u) \
182
+ && @adj[u].has_key?(v)
183
+
184
+ sub_graph.add_node(u, **@nodes[u])
185
+ sub_graph.add_node(v, **@nodes[v])
186
+ @adj[u][v].each { |_, keyval| sub_graph.add_edge(u, v, **keyval) }
187
+ end
188
+ sub_graph
189
+ else
190
+ raise ArgumentError, 'Expected Argument to be Array or Set of edges, ' \
191
+ "received #{edges.class.name} instead."
192
+ end
193
+ end
194
+
195
+ def multigraph?
196
+ true
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,65 @@
1
+ module NetworkX
2
+ # Performs the union of many graphs
3
+ #
4
+ # @param graphs [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
+
10
+ result = graphs.shift
11
+
12
+ graphs.each do |graph|
13
+ result = NetworkX.union(result, graph)
14
+ end
15
+ result
16
+ end
17
+
18
+ # Performs the disjoint union of many graphs
19
+ #
20
+ # @param graphs [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
21
+ #
22
+ # @return [Graph, DiGraph, MultiGraph, MultiDiGraph] disjoint union of all the graphs
23
+ def self.disjoint_union_all(graphs)
24
+ raise ArgumentError, 'Argument array is empty' if graphs.empty?
25
+
26
+ result = graphs.shift
27
+
28
+ graphs.each do |graph|
29
+ result = NetworkX.disjoint_union(result, graph)
30
+ end
31
+ result
32
+ end
33
+
34
+ # Performs the intersection of many graphs
35
+ #
36
+ # @param graphs [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
37
+ #
38
+ # @return [Graph, DiGraph, MultiGraph, MultiDiGraph] intersection of all the graphs
39
+ def self.intersection_all(graphs)
40
+ raise ArgumentError, 'Argument array is empty' if graphs.empty?
41
+
42
+ result = graphs.shift
43
+
44
+ graphs.each do |graph|
45
+ result = NetworkX.intersection(result, graph)
46
+ end
47
+ result
48
+ end
49
+
50
+ # Performs the composition of many graphs
51
+ #
52
+ # @param graphs [Array<Graph>, Array<DiGraph>, Array<MultiGraph>, Array<MultiDiGraph>] Array of graphs
53
+ #
54
+ # @return [Graph, DiGraph, MultiGraph, MultiDiGraph] composition of all the graphs
55
+ def self.compose_all(graphs)
56
+ raise ArgumentError, 'Argument array is empty' if graphs.empty?
57
+
58
+ result = graphs.shift
59
+
60
+ graphs.each do |graph|
61
+ result = NetworkX.compose(result, graph)
62
+ end
63
+ result
64
+ end
65
+ end