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.
- checksums.yaml +4 -4
- data/.rubocop.yml +18 -11
- data/CONTRIBUTING.md +2 -2
- data/Guardfile +1 -1
- data/ISSUE_TEMPLATE.md +15 -0
- data/PULL_REQUEST_TEMPLATE.md +12 -0
- data/README.md +4 -4
- data/RELEASE_POLICY.md +20 -0
- data/lib/networkx.rb +37 -1
- data/lib/networkx/auxillary_functions/cliques.rb +65 -0
- data/lib/networkx/auxillary_functions/cycles.rb +104 -0
- data/lib/networkx/auxillary_functions/dag.rb +54 -0
- data/lib/networkx/auxillary_functions/eccentricity.rb +36 -0
- data/lib/networkx/auxillary_functions/mis.rb +23 -0
- data/lib/networkx/auxillary_functions/mst.rb +35 -0
- data/lib/networkx/auxillary_functions/union_find.rb +24 -0
- data/lib/networkx/auxillary_functions/vitality.rb +13 -0
- data/lib/networkx/auxillary_functions/wiener.rb +13 -0
- data/lib/networkx/converters/to_csv.rb +47 -0
- data/lib/networkx/converters/to_json.rb +39 -0
- data/lib/networkx/digraph.rb +228 -0
- data/lib/networkx/flow/capacityscaling.rb +255 -0
- data/lib/networkx/flow/edmondskarp.rb +113 -0
- data/lib/networkx/flow/preflowpush.rb +252 -0
- data/lib/networkx/flow/shortestaugmentingpath.rb +157 -0
- data/lib/networkx/flow/utils.rb +160 -0
- data/lib/networkx/graph.rb +341 -0
- data/lib/networkx/link_analysis/hits.rb +59 -0
- data/lib/networkx/link_analysis/pagerank.rb +47 -0
- data/lib/networkx/multidigraph.rb +240 -0
- data/lib/networkx/multigraph.rb +171 -0
- data/lib/networkx/operators/all.rb +61 -0
- data/lib/networkx/operators/binary.rb +244 -0
- data/lib/networkx/operators/product.rb +204 -0
- data/lib/networkx/operators/unary.rb +17 -0
- data/lib/networkx/shortest_path/astar.rb +71 -0
- data/lib/networkx/shortest_path/dense.rb +31 -0
- data/lib/networkx/shortest_path/unweighted.rb +139 -0
- data/lib/networkx/shortest_path/weighted.rb +408 -0
- data/lib/networkx/to_matrix.rb +52 -0
- data/lib/networkx/traversals/bfs.rb +58 -0
- data/lib/networkx/traversals/dfs.rb +79 -0
- data/lib/networkx/traversals/edge_dfs.rb +90 -0
- data/lib/networkx/version.rb +1 -1
- data/networkx.gemspec +4 -1
- metadata +70 -4
@@ -0,0 +1,59 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method length and method complexity
|
3
|
+
|
4
|
+
# Computes hits and authority scores for all the graphs
|
5
|
+
#
|
6
|
+
# @param graph [Graph, DiGraph] a graph
|
7
|
+
# @param max_iter [Integer] max iterations to run the hits algorithm
|
8
|
+
# @param tol [Numeric] tolerences to cut off the loop
|
9
|
+
# @param nstart [Array<Numeric>] starting hub values for the nodes
|
10
|
+
#
|
11
|
+
# @return [Array<Numeric, Numeric>] hits and authority scores
|
12
|
+
def self.hits(graph, max_iter=100, tol=1e-8, nstart)
|
13
|
+
return [{}, {}] if graph.nodes.empty?
|
14
|
+
h = nstart
|
15
|
+
sum = h.values.inject(:+)
|
16
|
+
h.each_key { |k| h[k] /= (sum * 1.0) }
|
17
|
+
i = 0
|
18
|
+
a = {}
|
19
|
+
|
20
|
+
loop do
|
21
|
+
hlast = Marshal.load(Marshal.dump(h))
|
22
|
+
h, a = {}, {}
|
23
|
+
hlast.each do |k, _v|
|
24
|
+
h[k] = 0
|
25
|
+
a[k] = 0
|
26
|
+
end
|
27
|
+
h.each_key { |k| graph.adj[k].each { |nbr, attrs| a[k] += hlast[nbr] * (attrs[:weight] || 1) } }
|
28
|
+
h.each_key { |k| graph.adj[k].each { |nbr, attrs| h[k] += a[nbr] * (attrs[:weight] || 1) } }
|
29
|
+
smax = h.values.max
|
30
|
+
h.each_key { |k| h[k] /= smax }
|
31
|
+
smax = a.values.max
|
32
|
+
a.each_key { |k| a[k] /= smax }
|
33
|
+
break if h.keys.map { |k| (h[k] - hlast[k]).abs }.inject(:+) < tol
|
34
|
+
raise ArgumentError, 'Power Iteration failed to converge!' if i > max_iter
|
35
|
+
i += 1
|
36
|
+
end
|
37
|
+
[h, a]
|
38
|
+
end
|
39
|
+
|
40
|
+
# Computes authority matrix for the graph
|
41
|
+
#
|
42
|
+
# @param graph [Graph, DiGraph] a graph
|
43
|
+
#
|
44
|
+
# @return [NMatrix] authority matrix for the graph
|
45
|
+
def self.authority_matrix(graph)
|
46
|
+
matrix, = to_matrix(graph, 0)
|
47
|
+
matrix.transpose.dot matrix
|
48
|
+
end
|
49
|
+
|
50
|
+
# Computes hub matrix for the graph
|
51
|
+
#
|
52
|
+
# @param graph [Graph, DiGraph] a graph
|
53
|
+
#
|
54
|
+
# @return [NMatrix] hub matrix for the graph
|
55
|
+
def self.hub_matrix(graph)
|
56
|
+
matrix, = to_matrix(graph, 0)
|
57
|
+
matrix.dot matrix.transpose
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method length and method complexity
|
3
|
+
|
4
|
+
# Computes pagerank values for the graph
|
5
|
+
#
|
6
|
+
# @param graph [Graph] a graph
|
7
|
+
# @param init [Array<Numeric>] initial pagerank values for the nodes
|
8
|
+
# @param alpha [Numeric] the alpha value to compute the pagerank
|
9
|
+
# @param eps [Numeric] tolerence to check for convergence
|
10
|
+
# @param max_iter [Integer] max iterations for the pagerank algorithm to run
|
11
|
+
#
|
12
|
+
# @return [Array<Numeric>] pagerank values of the graph
|
13
|
+
def self.pagerank(graph, init, alpha=0.85, eps=1e-4, max_iter=100)
|
14
|
+
dim = graph.nodes.length
|
15
|
+
raise ArgumentError, 'Init array needs to have same length as number of graph nodes!'\
|
16
|
+
unless dim == init.length
|
17
|
+
matrix = []
|
18
|
+
elem_ind = {}
|
19
|
+
p = []
|
20
|
+
curr = init.values
|
21
|
+
init.keys.each_with_index { |n, i| elem_ind[n] = i }
|
22
|
+
graph.adj.each do |_u, u_edges|
|
23
|
+
adj_arr = Array.new(dim, 0)
|
24
|
+
u_edges.each do |v, _|
|
25
|
+
adj_arr[elem_ind[v]] = 1
|
26
|
+
end
|
27
|
+
matrix << adj_arr
|
28
|
+
end
|
29
|
+
(0..(dim - 1)).each do |i|
|
30
|
+
p[i] = []
|
31
|
+
(0..(dim - 1)).each { |j| p[i][j] = matrix[i][j] / (matrix[i].inject(:+) * 1.0) }
|
32
|
+
end
|
33
|
+
|
34
|
+
max_iter.times do |_|
|
35
|
+
prev = curr.clone
|
36
|
+
dim.times do |i|
|
37
|
+
ip = 0
|
38
|
+
dim.times { |j| ip += p.transpose[i][j] * prev[j] }
|
39
|
+
curr[i] = (alpha * ip) + (1 - alpha) / (dim * 1.0)
|
40
|
+
end
|
41
|
+
err = 0
|
42
|
+
dim.times { |i| err += (prev[i] - curr[i]).abs }
|
43
|
+
return curr if err < eps
|
44
|
+
end
|
45
|
+
raise ArgumentError, 'PageRank failed to converge!'
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,240 @@
|
|
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 node_1 [Object] the first node of a given edge
|
14
|
+
# @param node_2 [Object] the second node of a given edge
|
15
|
+
def new_edge_key(node_1, node_2)
|
16
|
+
return 0 if @adj[node_1][node_2].nil?
|
17
|
+
key = @adj[node_1][node_2].length
|
18
|
+
key += 1 while @adj[node_1][node_2].key?(key)
|
19
|
+
key
|
20
|
+
end
|
21
|
+
|
22
|
+
# Adds the respective edge
|
23
|
+
#
|
24
|
+
# @example Add an edge with attribute name
|
25
|
+
# graph.add_edge(node1, node2, name: "Edge1")
|
26
|
+
#
|
27
|
+
# @example Add an edge with no attribute
|
28
|
+
# graph.add_edge("Bangalore", "Chennai")
|
29
|
+
#
|
30
|
+
# @param node_1 [Object] the first node of the edge
|
31
|
+
# @param node_2 [Object] the second node of the edge
|
32
|
+
# @param edge_attrs [Hash{ Object => Object }] the hash of the edge attributes
|
33
|
+
def add_edge(node_1, node_2, **edge_attrs)
|
34
|
+
add_node(node_1)
|
35
|
+
add_node(node_2)
|
36
|
+
key = new_edge_key(node_1, node_2)
|
37
|
+
all_edge_attrs = @adj[node_1][node_2] || {}
|
38
|
+
all_edge_attrs[key] = edge_attrs
|
39
|
+
@adj[node_1][node_2] = all_edge_attrs
|
40
|
+
@pred[node_2][node_1] = all_edge_attrs
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO: Reduce method complexity
|
44
|
+
|
45
|
+
# Removes edge from the graph
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# graph.remove_edge('Noida', 'Bangalore')
|
49
|
+
#
|
50
|
+
# @param node_1 [Object] the first node of the edge
|
51
|
+
# @param node_2 [Object] the second node of the edge
|
52
|
+
def remove_edge(node_1, node_2, key=nil)
|
53
|
+
if key.nil?
|
54
|
+
super(node_1, node_2)
|
55
|
+
return
|
56
|
+
end
|
57
|
+
raise KeyError, "#{node_1} is not a valid node." unless @nodes.key?(node_1)
|
58
|
+
raise KeyError, "#{node_2} is not a valid node" unless @nodes.key?(node_2)
|
59
|
+
raise KeyError, 'The given edge is not a valid one.' unless @adj[node_1].key?(node_2)
|
60
|
+
raise KeyError, 'The given edge is not a valid one.' unless @adj[node_1][node_2].key?(key)
|
61
|
+
@adj[node_1][node_2].delete(key)
|
62
|
+
@pred[node_2][node_1].delete(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?(node_1, node_2)
|
69
|
+
#
|
70
|
+
# @param node_1 [Object] the first node of the edge to be checked
|
71
|
+
# @param node_2 [Object] the second node of the edge to be checked
|
72
|
+
# @param key [Integer] the key of the given edge
|
73
|
+
def edge?(node_1, node_2, key=nil)
|
74
|
+
super(node_1, node_2) if key.nil?
|
75
|
+
node?(node_1) && @adj[node_1].key?(node_2) && @adj[node_1][node_2].key?(key)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the undirected version of the graph
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# graph.to_undirected
|
82
|
+
def to_undirected
|
83
|
+
graph = NetworkX::Graph.new(@graph)
|
84
|
+
@nodes.each { |node, node_attr| graph.add_node(node, node_attr) }
|
85
|
+
@adj.each do |node_1, node_1_edges|
|
86
|
+
node_1_edges.each do |node_2, node_1_node_2|
|
87
|
+
edge_attrs = {}
|
88
|
+
node_1_node_2.each { |_key, attrs| edge_attrs.merge!(attrs) }
|
89
|
+
graph.add_edge(node_1, node_2, edge_attrs)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
graph
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the directed version of the graph
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# graph.to_directed
|
99
|
+
def to_directed
|
100
|
+
graph = NetworkX::DiGraph.new(@graph)
|
101
|
+
@nodes.each { |node, node_attr| graph.add_node(node, node_attr) }
|
102
|
+
@adj.each do |node_1, node_1_edges|
|
103
|
+
node_1_edges.each do |node_2, node_1_node_2|
|
104
|
+
edge_attrs = {}
|
105
|
+
node_1_node_2.each { |_key, attrs| edge_attrs.merge!(attrs) }
|
106
|
+
graph.add_edge(node_1, node_2, edge_attrs)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
graph
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns the multigraph version of the graph
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# graph.to_multigraph
|
116
|
+
def to_multigraph
|
117
|
+
graph = NetworkX::MultiGraph.new(@graph)
|
118
|
+
@nodes.each { |node, node_attr| graph.add_node(node, node_attr) }
|
119
|
+
@adj.each do |node_1, node_1_edges|
|
120
|
+
node_1_edges.each_key do |node_2, node_1_node_2|
|
121
|
+
edge_attrs = {}
|
122
|
+
node_1_node_2.each { |_key, attrs| graph.add_edge(node_1, node_2, attrs) }
|
123
|
+
graph.add_edge(node_1, node_2, edge_attrs)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
graph
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the reversed version of the graph
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# graph.reverse
|
133
|
+
def reverse
|
134
|
+
new_graph = NetworkX::MultiDiGraph.new(@graph)
|
135
|
+
@nodes.each { |node, attrs| new_graph.add_node(node, attrs) }
|
136
|
+
@adj.each do |u, u_edges|
|
137
|
+
u_edges.each { |v, uv_attrs| uv_attrs.each { |_k, edge_attrs| new_graph.add_edge(v, u, edge_attrs) } }
|
138
|
+
end
|
139
|
+
new_graph
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns in-degree of a given node
|
143
|
+
#
|
144
|
+
# @example
|
145
|
+
# graph.in_degree(node)
|
146
|
+
#
|
147
|
+
# @param node [Object] the node whose in degree is to be calculated
|
148
|
+
def in_degree(node)
|
149
|
+
@pred[node].values.map(:length).inject(:+)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns out-degree of a given node
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# graph.out_degree(node)
|
156
|
+
#
|
157
|
+
# @param node [Object] the node whose out degree is to be calculated
|
158
|
+
def out_degree(node)
|
159
|
+
@adj[node].values.map(:length).inject(:+)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns number of edges
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# graph.number_of_edges
|
166
|
+
def number_of_edges
|
167
|
+
@adj.values.flat_map(&:values).map(&:length).inject(:+)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the size of the graph
|
171
|
+
#
|
172
|
+
# @example
|
173
|
+
# graph.size(true)
|
174
|
+
#
|
175
|
+
# @param is_weighted [Bool] if true, method returns sum of weights of all edges
|
176
|
+
# else returns number of edges
|
177
|
+
def size(is_weighted=false)
|
178
|
+
if is_weighted
|
179
|
+
graph_size = 0
|
180
|
+
@adj.each do |_, hash_val|
|
181
|
+
hash_val.each { |_, v| v.each { |_, attrs| graph_size += attrs[:weight] if attrs.key?(:weight) } }
|
182
|
+
end
|
183
|
+
return graph_size
|
184
|
+
end
|
185
|
+
number_of_edges
|
186
|
+
end
|
187
|
+
|
188
|
+
# TODO: Reduce method length and method complexity
|
189
|
+
|
190
|
+
# Returns subgraph consisting of given array of nodes
|
191
|
+
#
|
192
|
+
# @example
|
193
|
+
# graph.subgraph(%w[Mumbai Nagpur])
|
194
|
+
#
|
195
|
+
# @param nodes [Array<Object>] the nodes to be included in the subgraph
|
196
|
+
def subgraph(nodes)
|
197
|
+
case nodes
|
198
|
+
when Array, Set
|
199
|
+
sub_graph = NetworkX::MultiDiGraph.new(@graph)
|
200
|
+
nodes.each do |u, _|
|
201
|
+
raise KeyError, "#{u} does not exist in the current graph!" unless @nodes.key?(u)
|
202
|
+
sub_graph.add_node(u, @nodes[u])
|
203
|
+
@adj[u].each do |v, edge_val|
|
204
|
+
edge_val.each { |_, keyval| sub_graph.add_edge(u, v, keyval) if @adj[u].key?(v) && nodes.include?(v) }
|
205
|
+
end
|
206
|
+
return sub_graph
|
207
|
+
end
|
208
|
+
else
|
209
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of nodes, '\
|
210
|
+
"received #{nodes.class.name} instead."
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO: Reduce method length and method complexity
|
215
|
+
|
216
|
+
# Returns subgraph conisting of given edges
|
217
|
+
#
|
218
|
+
# @example
|
219
|
+
# graph.edge_subgraph([%w[Nagpur Wardha], %w[Nagpur Mumbai]])
|
220
|
+
#
|
221
|
+
# @param edges [Array<Object, Object>] the edges to be included in the subraph
|
222
|
+
def edge_subgraph(edges)
|
223
|
+
case edges
|
224
|
+
when Array, Set
|
225
|
+
sub_graph = NetworkX::MultiDiGraph.new(@graph)
|
226
|
+
edges.each do |u, v|
|
227
|
+
raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.key?(u)\
|
228
|
+
&& @adj[u].key?(v)
|
229
|
+
sub_graph.add_node(u, @nodes[u])
|
230
|
+
sub_graph.add_node(v, @nodes[v])
|
231
|
+
@adj[u][v].each { |_, keyval| sub_graph.add_edge(u, v, keyval) }
|
232
|
+
end
|
233
|
+
return sub_graph
|
234
|
+
else
|
235
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of edges, '\
|
236
|
+
"received #{edges.class.name} instead."
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,171 @@
|
|
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 node_1 [Object] the first node of a given edge
|
12
|
+
# @param node_2 [Object] the second node of a given edge
|
13
|
+
def new_edge_key(node_1, node_2)
|
14
|
+
return 0 if @adj[node_1][node_2].nil?
|
15
|
+
key = @adj[node_1][node_2].length
|
16
|
+
key += 1 while @adj[node_1][node_2].key?(key)
|
17
|
+
key
|
18
|
+
end
|
19
|
+
|
20
|
+
# Adds the respective edge
|
21
|
+
#
|
22
|
+
# @example Add an edge with attribute name
|
23
|
+
# graph.add_edge(node1, node2, name: "Edge1")
|
24
|
+
#
|
25
|
+
# @example Add an edge with no attribute
|
26
|
+
# graph.add_edge("Bangalore", "Chennai")
|
27
|
+
#
|
28
|
+
# @param node_1 [Object] the first node of the edge
|
29
|
+
# @param node_2 [Object] the second node of the edge
|
30
|
+
# @param edge_attrs [Hash{ Object => Object }] the hash of the edge attributes
|
31
|
+
def add_edge(node_1, node_2, **edge_attrs)
|
32
|
+
add_node(node_1)
|
33
|
+
add_node(node_2)
|
34
|
+
key = new_edge_key(node_1, node_2)
|
35
|
+
all_edge_attrs = @adj[node_1][node_2] || {}
|
36
|
+
all_edge_attrs[key] = edge_attrs
|
37
|
+
@adj[node_1][node_2] = all_edge_attrs
|
38
|
+
@adj[node_2][node_1] = all_edge_attrs
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO: Reduce method complexity
|
42
|
+
|
43
|
+
# Removes edge from the graph
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# graph.remove_edge('Noida', 'Bangalore')
|
47
|
+
#
|
48
|
+
# @param node_1 [Object] the first node of the edge
|
49
|
+
# @param node_2 [Object] the second node of the edge
|
50
|
+
def remove_edge(node_1, node_2, key=nil)
|
51
|
+
if key.nil?
|
52
|
+
super(node_1, node_2)
|
53
|
+
return
|
54
|
+
end
|
55
|
+
raise KeyError, "#{node_1} is not a valid node." unless @nodes.key?(node_1)
|
56
|
+
raise KeyError, "#{node_2} is not a valid node" unless @nodes.key?(node_2)
|
57
|
+
raise KeyError, 'The given edge is not a valid one.' unless @adj[node_1].key?(node_2)
|
58
|
+
raise KeyError, 'The given edge is not a valid one.' unless @adj[node_1][node_2].key?(key)
|
59
|
+
@adj[node_1][node_2].delete(key)
|
60
|
+
@adj[node_2][node_1].delete(key)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the size of the graph
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# graph.size(true)
|
67
|
+
#
|
68
|
+
# @param is_weighted [Bool] if true, method returns sum of weights of all edges
|
69
|
+
# else returns number of edges
|
70
|
+
def size(is_weighted=false)
|
71
|
+
if is_weighted
|
72
|
+
graph_size = 0
|
73
|
+
@adj.each do |_, hash_val|
|
74
|
+
hash_val.each { |_, v| v.each { |_, attrs| graph_size += attrs[:weight] if attrs.key?(:weight) } }
|
75
|
+
end
|
76
|
+
return graph_size / 2
|
77
|
+
end
|
78
|
+
number_of_edges
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns number of edges
|
82
|
+
#
|
83
|
+
# @example
|
84
|
+
# graph.number_of_edges
|
85
|
+
def number_of_edges
|
86
|
+
@adj.values.flat_map(&:values).map(&:length).inject(:+) / 2
|
87
|
+
end
|
88
|
+
|
89
|
+
# Checks if the the edge consisting of two nodes is present in the graph
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# graph.edge?(node_1, node_2)
|
93
|
+
#
|
94
|
+
# @param node_1 [Object] the first node of the edge to be checked
|
95
|
+
# @param node_2 [Object] the second node of the edge to be checked
|
96
|
+
# @param key [Integer] the key of the given edge
|
97
|
+
def edge?(node_1, node_2, key=nil)
|
98
|
+
super(node_1, node_2) if key.nil?
|
99
|
+
node?(node_1) && @adj[node_1].key?(node_2) && @adj[node_1][node_2].key?(key)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the undirected version of the graph
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
# graph.to_undirected
|
106
|
+
def to_undirected
|
107
|
+
graph = NetworkX::Graph.new(@graph)
|
108
|
+
@nodes.each { |node, node_attr| graph.add_node(node, node_attr) }
|
109
|
+
@adj.each do |node_1, node_1_edges|
|
110
|
+
node_1_edges.each do |node_2, node_1_node_2|
|
111
|
+
edge_attrs = {}
|
112
|
+
node_1_node_2.each { |_key, attrs| edge_attrs.merge!(attrs) }
|
113
|
+
graph.add_edge(node_1, node_2, edge_attrs)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
graph
|
117
|
+
end
|
118
|
+
|
119
|
+
# TODO: Reduce method complexity and method length
|
120
|
+
|
121
|
+
# Returns subgraph consisting of given array of nodes
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# graph.subgraph(%w[Mumbai Nagpur])
|
125
|
+
#
|
126
|
+
# @param nodes [Array<Object>] the nodes to be included in the subgraph
|
127
|
+
def subgraph(nodes)
|
128
|
+
case nodes
|
129
|
+
when Array, Set
|
130
|
+
sub_graph = NetworkX::MultiGraph.new(@graph)
|
131
|
+
nodes.each do |u, _|
|
132
|
+
raise KeyError, "#{u} does not exist in the current graph!" unless @nodes.key?(u)
|
133
|
+
sub_graph.add_node(u, @nodes[u])
|
134
|
+
@adj[u].each do |v, edge_val|
|
135
|
+
edge_val.each { |_, keyval| sub_graph.add_edge(u, v, keyval) if @adj[u].key?(v) && nodes.include?(v) }
|
136
|
+
end
|
137
|
+
return sub_graph
|
138
|
+
end
|
139
|
+
else
|
140
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of nodes, '\
|
141
|
+
"received #{nodes.class.name} instead."
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# TODO: Reduce method complexity and method length
|
146
|
+
|
147
|
+
# Returns subgraph conisting of given edges
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# graph.edge_subgraph([%w[Nagpur Wardha], %w[Nagpur Mumbai]])
|
151
|
+
#
|
152
|
+
# @param edges [Array<Object, Object>] the edges to be included in the subraph
|
153
|
+
def edge_subgraph(edges)
|
154
|
+
case edges
|
155
|
+
when Array, Set
|
156
|
+
sub_graph = NetworkX::MultiGraph.new(@graph)
|
157
|
+
edges.each do |u, v|
|
158
|
+
raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.key?(u)\
|
159
|
+
&& @adj[u].key?(v)
|
160
|
+
sub_graph.add_node(u, @nodes[u])
|
161
|
+
sub_graph.add_node(v, @nodes[v])
|
162
|
+
@adj[u][v].each { |_, keyval| sub_graph.add_edge(u, v, keyval) }
|
163
|
+
end
|
164
|
+
return sub_graph
|
165
|
+
else
|
166
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of edges, '\
|
167
|
+
"received #{edges.class.name} instead."
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|