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,36 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Returns the eccentricity of a particular node or all nodes
|
3
|
+
#
|
4
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
5
|
+
# @param node [Object] node to find the eccentricity of
|
6
|
+
#
|
7
|
+
# @return [Array<Numeric>, Numeric] eccentricity/eccentricites of all nodes
|
8
|
+
def self.eccentricity(graph, node=nil)
|
9
|
+
e = {}
|
10
|
+
graph.nodes.each do |u, _|
|
11
|
+
length = single_source_shortest_path_length(graph, u)
|
12
|
+
l = length.length
|
13
|
+
raise ArgumentError, 'Found infinite path length!' unless l == graph.nodes.length
|
14
|
+
e[u] = length.max_by { |a| a[1] }[1]
|
15
|
+
end
|
16
|
+
node.nil? ? e : e[node]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the diameter of a graph
|
20
|
+
#
|
21
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
22
|
+
#
|
23
|
+
# @return [Numeric] diameter of the graph
|
24
|
+
def self.diameter(graph)
|
25
|
+
eccentricity(graph).values.max
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the radius of a graph
|
29
|
+
#
|
30
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
31
|
+
#
|
32
|
+
# @return [Numeric] radius of the graph
|
33
|
+
def self.radius(graph)
|
34
|
+
eccentricity(graph).values.min
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method complexity and method length
|
3
|
+
|
4
|
+
# Returns the maximal independent set of a graph
|
5
|
+
#
|
6
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
7
|
+
# @param nodes [Object] nodes to be considered in the MIS
|
8
|
+
#
|
9
|
+
# @return [Numeric] radius of the graph
|
10
|
+
def self.maximal_independent_set(graph, nodes)
|
11
|
+
raise 'The array containing the nodes should be a subset of the graph!' if (graph.nodes.keys - nodes).empty?
|
12
|
+
neighbours = []
|
13
|
+
nodes.each { |u| graph.adj[u].each { |v, _| neighbours |= [v] } }
|
14
|
+
raise 'Nodes is not an independent set of graph!' if (neighbours - nodes).empty?
|
15
|
+
available_nodes = graph.nodes.keys - (neighbours | nodes)
|
16
|
+
until available_nodes.empty?
|
17
|
+
node = available_nodes.sample
|
18
|
+
nodes << node
|
19
|
+
available_nodes -= (graph.adj[node].keys + [node])
|
20
|
+
end
|
21
|
+
nodes
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Helper function for the minimum spanning tree
|
3
|
+
#
|
4
|
+
def self.get_edges_weights(graph)
|
5
|
+
edges = []
|
6
|
+
graph.adj.each do |u, u_edges|
|
7
|
+
u_edges.each do |v, uv_attrs|
|
8
|
+
edges << [[u, v], uv_attrs[:weight] || Float::INFINITY]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
edges
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: Reduce method complexity and method length
|
15
|
+
|
16
|
+
# Returns the minimum spanning tree of a graph
|
17
|
+
#
|
18
|
+
# @param graph [Graph, DiGraph] a graph
|
19
|
+
#
|
20
|
+
# @return [DiGraph, Graph] a minimum spanning tree of the graph
|
21
|
+
def self.minimum_spanning_tree(graph)
|
22
|
+
mst = Marshal.load(Marshal.dump(graph))
|
23
|
+
mst.clear
|
24
|
+
edges = get_edges_weights(graph).sort_by { |a| a[1] }
|
25
|
+
union_find = UnionFind.new(graph.nodes.keys)
|
26
|
+
while edges.any? && mst.nodes.length <= graph.nodes.length
|
27
|
+
edge = edges.shift
|
28
|
+
unless union_find.connected?(edge[0][0], edge[0][1])
|
29
|
+
union_find.union(edge[0][0], edge[0][1])
|
30
|
+
mst.add_edge(edge[0][0], edge[0][1], graph.adj[edge[0][0]][edge[0][1]])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
mst
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NetworkX
|
2
|
+
class UnionFind
|
3
|
+
def initialize(nodes)
|
4
|
+
@unions = {}
|
5
|
+
nodes.each_with_index do |node, index|
|
6
|
+
@unions[node] = index
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def connected?(node_1, node_2)
|
11
|
+
@unions[node_1] == @unions[node_2]
|
12
|
+
end
|
13
|
+
|
14
|
+
def union(node_1, node_2)
|
15
|
+
return if connected?(node_1, node_2)
|
16
|
+
node1_id = @unions[node_1]
|
17
|
+
node2_id = @unions[node_2]
|
18
|
+
|
19
|
+
@unions.each do |node, id|
|
20
|
+
@unions[node] = node1_id if id == node2_id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Returns the closeness vitality of a node
|
3
|
+
#
|
4
|
+
# @param graph [Graph, DiGraph] a graph
|
5
|
+
# @param node [Object] node to compute closeness vitality of
|
6
|
+
#
|
7
|
+
# @return [Numeric] closeness vitality of the given node
|
8
|
+
def self.closeness_vitality(graph, node)
|
9
|
+
before = wiener_index(graph)
|
10
|
+
after = wiener_index(graph.subgraph(graph.nodes.keys - [node]))
|
11
|
+
before - after
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Returns the wiener index of the graph
|
3
|
+
#
|
4
|
+
# @param graph [Graph, DiGraph] a graph
|
5
|
+
#
|
6
|
+
# @return [Numeric] wiener index of the graph
|
7
|
+
def self.wiener_index(graph)
|
8
|
+
total = all_pairs_shortest_path_length(graph)
|
9
|
+
wiener_ind = 0
|
10
|
+
Hash[total].each { |_, distances| Hash[distances].each { |_, val| wiener_ind += val } }
|
11
|
+
graph.directed? ? wiener_ind : wiener_ind / 2
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method length and method complexity
|
3
|
+
|
4
|
+
# Saves the graph in a csv file
|
5
|
+
#
|
6
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
7
|
+
# @param filename [String] filename of the graph
|
8
|
+
def self.graph_to_csv(graph, filename='graph.csv')
|
9
|
+
CSV.open(filename, 'wb') do |csv|
|
10
|
+
csv << [graph.class.name]
|
11
|
+
csv << ['graph_values']
|
12
|
+
csv << graph.graph.keys
|
13
|
+
csv << graph.graph.values
|
14
|
+
csv << ['graph_nodes']
|
15
|
+
graph.nodes.each do |u, attrs|
|
16
|
+
node_attrs = [u]
|
17
|
+
attrs.each do |k, v|
|
18
|
+
node_attrs << k
|
19
|
+
node_attrs << v
|
20
|
+
end
|
21
|
+
csv << node_attrs
|
22
|
+
end
|
23
|
+
csv << ['graph_edges']
|
24
|
+
graph.adj.each do |u, u_edges|
|
25
|
+
u_edges.each do |v, uv_attrs|
|
26
|
+
if graph.multigraph?
|
27
|
+
uv_attrs.each do |key, attrs|
|
28
|
+
node_attrs = [u, v, key]
|
29
|
+
attrs.each do |k, k_attrs|
|
30
|
+
node_attrs << k
|
31
|
+
node_attrs << k_attrs
|
32
|
+
end
|
33
|
+
csv << node_attrs
|
34
|
+
end
|
35
|
+
else
|
36
|
+
node_attrs = [u, v]
|
37
|
+
uv_attrs.each do |k, vals|
|
38
|
+
node_attrs << k
|
39
|
+
node_attrs << vals
|
40
|
+
end
|
41
|
+
csv << node_attrs
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Returns a JSON object of the given graph
|
3
|
+
#
|
4
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
5
|
+
#
|
6
|
+
# @return [JSON] json encoded graph
|
7
|
+
def self.graph_to_json(graph)
|
8
|
+
json_hash = {}
|
9
|
+
json_hash[:class] = graph.class.name
|
10
|
+
json_hash[:graph] = graph.graph
|
11
|
+
json_hash[:nodes] = graph.nodes
|
12
|
+
json_hash[:adj] = graph.adj
|
13
|
+
json_hash.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO: Reduce method length
|
17
|
+
|
18
|
+
# Returns a graph from the json encoded graph
|
19
|
+
#
|
20
|
+
# @param json_str [JSON] json encoded string
|
21
|
+
#
|
22
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] a decoded graph
|
23
|
+
def self.json_to_graph(json_str)
|
24
|
+
graph_hash = JSON.parse(json_str)
|
25
|
+
case json_str['class']
|
26
|
+
when 'NetworkX::Graph'
|
27
|
+
graph = NetworkX::Graph.new(graph_hash.graph)
|
28
|
+
when 'NetworkX::MultiGraph'
|
29
|
+
graph = NetworkX::MultiGraph.new(graph_hash.graph)
|
30
|
+
when 'NetworkX::DiGraph'
|
31
|
+
graph = NetworkX::DiGraph.new(graph_hash.graph)
|
32
|
+
when 'NetworkX::MultiDiGraph'
|
33
|
+
graph = NetworkX::MultiDiGraph.new(graph_hash.graph)
|
34
|
+
end
|
35
|
+
graph.adj = graph_hash['adj']
|
36
|
+
graph.nodes = graph_hash['nodes']
|
37
|
+
graph
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Describes the class for making Directed Graphs
|
3
|
+
#
|
4
|
+
# @attr_reader adj [Hash{ Object => Hash{ Object => Hash{ Object => Object } } }]
|
5
|
+
# Stores the edges and their attributes in an adjencency list form
|
6
|
+
# @attr_reader pred [Hash{ Object => Hash{ Object => 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 DiGraph < Graph
|
11
|
+
attr_reader :adj, :nodes, :graph, :pred
|
12
|
+
|
13
|
+
# Constructor for initializing graph
|
14
|
+
#
|
15
|
+
# @example Initialize a graph with attributes 'type' and 'name'
|
16
|
+
# graph = NetworkX::Graph.new(name: "Social Network", type: "undirected")
|
17
|
+
#
|
18
|
+
# @param graph_attrs [Hash{ Object => Object }] the graph attributes in a hash format
|
19
|
+
def initialize(**graph_attrs)
|
20
|
+
super(graph_attrs)
|
21
|
+
|
22
|
+
@pred = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds the respective edge
|
26
|
+
#
|
27
|
+
# @example Add an edge with attribute name
|
28
|
+
# graph.add_edge(node1, node2, name: "Edge1")
|
29
|
+
#
|
30
|
+
# @example Add an edge with no attribute
|
31
|
+
# graph.add_edge("Bangalore", "Chennai")
|
32
|
+
#
|
33
|
+
# @param node_1 [Object] the first node of the edge
|
34
|
+
# @param node_2 [Object] the second node of the edge
|
35
|
+
# @param edge_attrs [Hash{ Object => Object }] the hash of the edge attributes
|
36
|
+
def add_edge(node_1, node_2, **edge_attrs)
|
37
|
+
add_node(node_1)
|
38
|
+
add_node(node_2)
|
39
|
+
|
40
|
+
edge_attrs = (@adj[node_1][node_2] || {}).merge(edge_attrs)
|
41
|
+
@adj[node_1][node_2] = edge_attrs
|
42
|
+
@pred[node_2][node_1] = edge_attrs
|
43
|
+
end
|
44
|
+
|
45
|
+
# Adds a node and its attributes to the graph
|
46
|
+
#
|
47
|
+
# @example Add a node with attribute 'type'
|
48
|
+
# graph.add_node("Noida", type: "city")
|
49
|
+
#
|
50
|
+
# @param node [Object] the node object
|
51
|
+
# @param node_attrs [Hash{ Object => Object }] the hash of the attributes of the node
|
52
|
+
def add_node(node, **node_attrs)
|
53
|
+
super(node, node_attrs)
|
54
|
+
|
55
|
+
@pred[node] = {} unless @pred.key?(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Removes node from the graph
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# graph.remove_node("Noida")
|
62
|
+
#
|
63
|
+
# @param node [Object] the node to be removed
|
64
|
+
def remove_node(node)
|
65
|
+
raise KeyError, "Error in deleting node #{node} from Graph." unless @nodes.key?(node)
|
66
|
+
|
67
|
+
neighbours = @adj[node]
|
68
|
+
neighbours.each_key { |k| @pred[k].delete(node) }
|
69
|
+
@pred[node].each_key do |k|
|
70
|
+
@adj[k].delete(node)
|
71
|
+
end
|
72
|
+
|
73
|
+
@pred.delete(node)
|
74
|
+
@adj.delete(node)
|
75
|
+
@nodes.delete(node)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Removes edge from the graph
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# graph.remove_edge('Noida', 'Bangalore')
|
82
|
+
#
|
83
|
+
# @param node_1 [Object] the first node of the edge
|
84
|
+
# @param node_2 [Object] the second node of the edge
|
85
|
+
def remove_edge(node_1, node_2)
|
86
|
+
raise KeyError, "#{node_1} is not a valid node." unless @nodes.key?(node_1)
|
87
|
+
raise KeyError, "#{node_2} is not a valid node" unless @nodes.key?(node_2)
|
88
|
+
raise KeyError, 'The given edge is not a valid one.' unless @adj[node_1].key?(node_2)
|
89
|
+
|
90
|
+
@adj[node_1].delete(node_2)
|
91
|
+
@pred[node_2].delete(node_1)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Clears the graph
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# graph.clear
|
98
|
+
def clear
|
99
|
+
super
|
100
|
+
|
101
|
+
@pred.clear
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns number of edges
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# graph.number_of_edges
|
108
|
+
def number_of_edges
|
109
|
+
@adj.values.map(&:length).inject(:+)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns the size of graph
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# graph.size(true)
|
116
|
+
#
|
117
|
+
# @param is_weighted [Bool] if true, method returns sum of weights of all edges
|
118
|
+
# else returns number of edges
|
119
|
+
def size(is_weighted=false)
|
120
|
+
if is_weighted
|
121
|
+
graph_size = 0
|
122
|
+
@adj.each do |_, hash_val|
|
123
|
+
hash_val.each { |_, v| graph_size += v[:weight] if v.key?(:weight) }
|
124
|
+
end
|
125
|
+
return graph_size
|
126
|
+
end
|
127
|
+
number_of_edges
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns in-degree of a given node
|
131
|
+
#
|
132
|
+
# @example
|
133
|
+
# graph.in_degree(node)
|
134
|
+
#
|
135
|
+
# @param node [Object] the node whose in degree is to be calculated
|
136
|
+
def in_degree(node)
|
137
|
+
@pred[node].length
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns out-degree of a given node
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
# graph.out_degree(node)
|
144
|
+
#
|
145
|
+
# @param node [Object] the node whose out degree is to be calculated
|
146
|
+
def out_degree(node)
|
147
|
+
@adj[node].length
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the reversed version of the graph
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# graph.reverse
|
154
|
+
def reverse
|
155
|
+
new_graph = NetworkX::DiGraph.new(@graph)
|
156
|
+
@nodes.each { |u, attrs| new_graph.add_node(u, attrs) }
|
157
|
+
@adj.each do |u, edges|
|
158
|
+
edges.each { |v, attrs| new_graph.add_edge(v, u, attrs) }
|
159
|
+
end
|
160
|
+
new_graph
|
161
|
+
end
|
162
|
+
|
163
|
+
# Returns the undirected version of the graph
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
# graph.to_undirected
|
167
|
+
def to_undirected
|
168
|
+
new_graph = NetworkX::Graph.new(@graph)
|
169
|
+
@nodes.each { |u, attrs| new_graph.add_node(u, attrs) }
|
170
|
+
@adj.each do |u, edges|
|
171
|
+
edges.each { |v, attrs| new_graph.add_edge(u, v, attrs) }
|
172
|
+
end
|
173
|
+
new_graph
|
174
|
+
end
|
175
|
+
|
176
|
+
# TODO: Reduce method complexity and method length
|
177
|
+
|
178
|
+
# Returns subgraph consisting of given array of nodes
|
179
|
+
#
|
180
|
+
# @example
|
181
|
+
# graph.subgraph(%w[Mumbai Nagpur])
|
182
|
+
#
|
183
|
+
# @param nodes [Array<Object>] the nodes to be included in the subgraph
|
184
|
+
def subgraph(nodes)
|
185
|
+
case nodes
|
186
|
+
when Array, Set
|
187
|
+
sub_graph = NetworkX::DiGraph.new(@graph)
|
188
|
+
nodes.each do |u|
|
189
|
+
raise KeyError, "#{u} does not exist in the current graph!" unless node?(u)
|
190
|
+
sub_graph.add_node(u, @nodes[u])
|
191
|
+
@adj[u].each do |v, uv_attrs|
|
192
|
+
sub_graph.add_edge(u, v, uv_attrs) if @adj[u].key?(v) && nodes.include?(v)
|
193
|
+
end
|
194
|
+
return sub_graph
|
195
|
+
end
|
196
|
+
else
|
197
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of nodes, '\
|
198
|
+
"received #{nodes.class.name} instead."
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# TODO: Reduce method complexity and method length
|
203
|
+
|
204
|
+
# Returns subgraph consisting of given edges
|
205
|
+
#
|
206
|
+
# @example
|
207
|
+
# graph.edge_subgraph([%w[Nagpur Wardha], %w[Nagpur Mumbai]])
|
208
|
+
#
|
209
|
+
# @param edges [Array<Object, Object>] the edges to be included in the subraph
|
210
|
+
def edge_subgraph(edges)
|
211
|
+
case edges
|
212
|
+
when Array, Set
|
213
|
+
sub_graph = NetworkX::DiGraph.new(@graph)
|
214
|
+
edges.each do |u, v|
|
215
|
+
raise KeyError, "Edge between #{u} and #{v} does not exist in the graph!" unless @nodes.key?(u)\
|
216
|
+
&& @adj[u].key?(v)
|
217
|
+
sub_graph.add_node(u, @nodes[u])
|
218
|
+
sub_graph.add_node(v, @nodes[v])
|
219
|
+
sub_graph.add_edge(u, v, @adj[u][v])
|
220
|
+
end
|
221
|
+
return sub_graph
|
222
|
+
else
|
223
|
+
raise ArgumentError, 'Expected Argument to be Array or Set of edges, '\
|
224
|
+
"received #{edges.class.name} instead."
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|