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,17 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# Performs the complement operation on the graph
|
3
|
+
#
|
4
|
+
# @param [Graph, DiGraph, MultiGraph, MultiDiGraph] graph
|
5
|
+
#
|
6
|
+
# @return [Graph, DiGraph, MultiGraph, MultiDiGraph] the complement of the graph
|
7
|
+
def self.complement(graph)
|
8
|
+
result = Marshal.load(Marshal.dump(graph))
|
9
|
+
result.clear
|
10
|
+
|
11
|
+
result.add_nodes(graph.nodes.map { |u, attrs| [u, attrs] })
|
12
|
+
graph.adj.each do |u, u_edges|
|
13
|
+
graph.nodes.each { |v, attrs| result.add_edge(u, v, attrs) if !u_edges.key?(v) && u != v }
|
14
|
+
end
|
15
|
+
result
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method complexity and method length
|
3
|
+
|
4
|
+
# Returns path using astar algorithm between source and target
|
5
|
+
#
|
6
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
7
|
+
# @param source [Object] a node to start astar from
|
8
|
+
# @param target [Object] a node to end astar
|
9
|
+
# @param heuristic [] a lambda to compute heuristic b/w two nodes
|
10
|
+
#
|
11
|
+
# @return [Array<Object>] an array of nodes forming a path between source
|
12
|
+
# and target
|
13
|
+
def self.astar_path(graph, source, target, heuristic=nil)
|
14
|
+
warn 'A* is not implemented for MultiGraph and MultiDiGraph'
|
15
|
+
raise ArgumentError, 'Either source or target is not in graph' unless graph.node?(source) && graph.node?(target)
|
16
|
+
count = ->(i) { i + 1 }
|
17
|
+
i = -1
|
18
|
+
heuristic ||= (->(_u, _v) { 0 })
|
19
|
+
heap = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) }
|
20
|
+
heap << [0, count.call(i), source, 0, nil]
|
21
|
+
enqueued, explored = {}, {}
|
22
|
+
|
23
|
+
until heap.empty?
|
24
|
+
_, _, curnode, dist, parent = heap.pop
|
25
|
+
if curnode == target
|
26
|
+
path = [curnode]
|
27
|
+
node = parent
|
28
|
+
until node.nil?
|
29
|
+
path << node
|
30
|
+
node = explored[node]
|
31
|
+
end
|
32
|
+
path.reverse
|
33
|
+
return path
|
34
|
+
end
|
35
|
+
|
36
|
+
next if explored.key?(curnode)
|
37
|
+
|
38
|
+
explored[curnode] = parent
|
39
|
+
|
40
|
+
graph.adj[curnode].each do |u, attrs|
|
41
|
+
next if explored.key?(u)
|
42
|
+
ncost = dist + (attrs[:weight] || 1)
|
43
|
+
if enqueued.key?(u)
|
44
|
+
qcost, = enqueued[u]
|
45
|
+
next if qcost <= ncost
|
46
|
+
else
|
47
|
+
h = heuristic.call(u, target)
|
48
|
+
enqueued[u] = ncost, h
|
49
|
+
heap << [ncost + h, count.call(i), u, ncost, curnode]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
raise ArgumentError, 'Target not reachable!'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns astar path length b/w source and target
|
57
|
+
#
|
58
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
59
|
+
# @param source [Object] a node to start astar from
|
60
|
+
# @param target [Object] a node to end astar
|
61
|
+
# @param heuristic [] a lambda to compute heuristic b/w two nodes
|
62
|
+
#
|
63
|
+
# @return [Numeric] the length of the path
|
64
|
+
def self.astar_path_length(graph, source, target, heuristic=nil)
|
65
|
+
raise ArgumentError, 'Either source or target is not in graph' unless graph.node?(source) && graph.node?(target)
|
66
|
+
path = astar_path(graph, source, target, heuristic)
|
67
|
+
path_length = 0
|
68
|
+
(1..(path.length - 1)).each { |i| path_length += (graph.adj[path[i - 1]][path[i]][:weight] || 1) }
|
69
|
+
path_length
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method length and method complexity
|
3
|
+
|
4
|
+
# Returns the all pair distance between all the nodes
|
5
|
+
#
|
6
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
7
|
+
#
|
8
|
+
# @return [Hash{ Object => { Object => { Numeric }}}] a hash containing distances
|
9
|
+
# b/w all pairs of nodes
|
10
|
+
def self.floyd_warshall(graph)
|
11
|
+
a, index = to_matrix(graph, Float::INFINITY, 'min')
|
12
|
+
nodelen = graph.nodes.length
|
13
|
+
(0..(nodelen - 1)).each { |i| a[i, i] = 0 }
|
14
|
+
(0..(nodelen - 1)).each do |k|
|
15
|
+
(0..(nodelen - 1)).each do |i|
|
16
|
+
(0..(nodelen - 1)).each do |j|
|
17
|
+
a[i, j] = [a[i, j], a[i, k] + a[k, j]].min
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
as_hash = {}
|
23
|
+
(0..(nodelen - 1)).each do |i|
|
24
|
+
(0..(nodelen - 1)).each do |j|
|
25
|
+
as_hash[index[i]] = {} unless as_hash.key?(index[i])
|
26
|
+
as_hash[index[i]][index[j]] = a[i, j]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
as_hash
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module NetworkX
|
2
|
+
# TODO: Reduce method length
|
3
|
+
|
4
|
+
# Helper function for single source shortest path length
|
5
|
+
def self.help_single_shortest_path_length(adj, firstlevel, cutoff)
|
6
|
+
iterator = Enumerator.new do |e|
|
7
|
+
seen = {}
|
8
|
+
level = 0
|
9
|
+
nextlevel = firstlevel
|
10
|
+
|
11
|
+
while !nextlevel.empty? && cutoff >= level
|
12
|
+
thislevel = nextlevel
|
13
|
+
nextlevel = {}
|
14
|
+
thislevel.each do |u, _attrs|
|
15
|
+
next if seen.key?(u)
|
16
|
+
seen[u] = level
|
17
|
+
nextlevel.merge!(adj[u])
|
18
|
+
e.yield [u, level]
|
19
|
+
end
|
20
|
+
level += 1
|
21
|
+
end
|
22
|
+
seen.clear
|
23
|
+
end
|
24
|
+
iterator
|
25
|
+
end
|
26
|
+
|
27
|
+
# Computes shortest path values to all nodes from a given node
|
28
|
+
#
|
29
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
30
|
+
# @param source [Object] source to compute path length from
|
31
|
+
# @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
|
32
|
+
#
|
33
|
+
# @return [Array<Object, Numeric>] path lengths for all nodes
|
34
|
+
def self.single_source_shortest_path_length(graph, source, cutoff=nil)
|
35
|
+
raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source)
|
36
|
+
cutoff = Float::INFINITY if cutoff.nil?
|
37
|
+
nextlevel = {source => 1}
|
38
|
+
help_single_shortest_path_length(graph.adj, nextlevel, cutoff).take(graph.nodes.length)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Computes shortest path values to all nodes from all nodes
|
42
|
+
#
|
43
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
44
|
+
# @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
|
45
|
+
#
|
46
|
+
# @return [Array<Object, Array<Object, Numeric>>] path lengths for all nodes from all nodes
|
47
|
+
def self.all_pairs_shortest_path_length(graph, cutoff=nil)
|
48
|
+
shortest_paths = []
|
49
|
+
graph.nodes.each_key { |n| shortest_paths << [n, single_source_shortest_path_length(graph, n, cutoff)] }
|
50
|
+
shortest_paths
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO: Reduce method length
|
54
|
+
|
55
|
+
# Helper function for finding single source shortest path
|
56
|
+
def self.help_single_shortest_path(adj, firstlevel, paths, cutoff)
|
57
|
+
level = 0
|
58
|
+
nextlevel = firstlevel
|
59
|
+
while !nextlevel.empty? && cutoff > level
|
60
|
+
thislevel = nextlevel
|
61
|
+
nextlevel = {}
|
62
|
+
thislevel.each_key do |u|
|
63
|
+
adj[u].each_key do |v|
|
64
|
+
unless paths.key?(v)
|
65
|
+
paths[v] = paths[u] + [v]
|
66
|
+
nextlevel[v] = 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
level += 1
|
71
|
+
end
|
72
|
+
paths
|
73
|
+
end
|
74
|
+
|
75
|
+
# Computes single source shortest path from a node to every other node
|
76
|
+
#
|
77
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
78
|
+
# @param source [Object] source from which shortest paths are needed
|
79
|
+
# @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
|
80
|
+
#
|
81
|
+
# @return [Array<Object, Array<Object, Array<Object>>>] path lengths for all nodes from all nodes
|
82
|
+
def self.single_source_shortest_path(graph, source, cutoff=nil)
|
83
|
+
raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source)
|
84
|
+
cutoff = Float::INFINITY if cutoff.nil?
|
85
|
+
nextlevel = {source => 1}
|
86
|
+
paths = {source => [source]}
|
87
|
+
help_single_shortest_path(graph.adj, nextlevel, paths, cutoff)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Computes shortest paths to all nodes from all nodes
|
91
|
+
#
|
92
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
93
|
+
# @param cutoff [Numeric, nil] cutoff for the shortest path algorithm
|
94
|
+
#
|
95
|
+
# @return [Array<Object, Hash {Object => Array<Object> }>] paths for all nodes from all nodes
|
96
|
+
def self.all_pairs_shortest_path(graph, cutoff=nil)
|
97
|
+
shortest_paths = []
|
98
|
+
graph.nodes.each_key { |n| shortest_paths << [n, single_source_shortest_path(graph, n, cutoff)] }
|
99
|
+
shortest_paths
|
100
|
+
end
|
101
|
+
|
102
|
+
# TODO: Reduce method length and method complexity
|
103
|
+
|
104
|
+
# Computes shortest paths to all nodes from all nodes
|
105
|
+
#
|
106
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
107
|
+
# @param source [Object] source for which predecessors are needed
|
108
|
+
# @param cutoff [Numeric, nil] cutoff for finding predecessor
|
109
|
+
# @param return_seen [Boolean] if true, returns seen dict
|
110
|
+
#
|
111
|
+
# @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>,
|
112
|
+
# Hash{ Object => Array<Object> }]
|
113
|
+
# predecessors of a given node and/or seen dict
|
114
|
+
def self.predecessor(graph, source, cutoff=nil, return_seen=false)
|
115
|
+
raise ArgumentError, 'Source not found in the Graph!' unless graph.node?(source)
|
116
|
+
level = 0
|
117
|
+
nextlevel = [source]
|
118
|
+
seen = {source => level}
|
119
|
+
pred = {source => []}
|
120
|
+
until nextlevel.empty?
|
121
|
+
level += 1
|
122
|
+
thislevel = nextlevel
|
123
|
+
nextlevel = []
|
124
|
+
thislevel.each do |u|
|
125
|
+
graph.adj[u].each_key do |v|
|
126
|
+
if !seen.key?(v)
|
127
|
+
pred[v] = [u]
|
128
|
+
seen[v] = level
|
129
|
+
nextlevel << v
|
130
|
+
elsif seen[v] == level
|
131
|
+
pred[v] << u
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
break if cutoff.nil? && cutoff <= level
|
136
|
+
end
|
137
|
+
return_seen ? [pred, seen] : pred
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,408 @@
|
|
1
|
+
# TODO: Reduce module length
|
2
|
+
|
3
|
+
module NetworkX
|
4
|
+
# Helper function to extract weight from a adjecency hash
|
5
|
+
def self.get_weight(graph)
|
6
|
+
weight_get = lambda do |_, _, attrs|
|
7
|
+
return attrs[:weight] || 1 unless graph.multigraph?
|
8
|
+
attrs.group_by { |_k, vals| vals[:weight] || 1 }.keys.max
|
9
|
+
end
|
10
|
+
weight_get
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: Reduce method length and method complexity
|
14
|
+
|
15
|
+
# Helper function for multisource dijkstra
|
16
|
+
def self.help_multisource_dijkstra(graph, sources, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
|
17
|
+
count = ->(i) { i + 1 }
|
18
|
+
i = -1
|
19
|
+
dist = {}
|
20
|
+
seen = {}
|
21
|
+
fringe = Heap.new { |x, y| x[0] < y[0] || (x[0] == y[0] && x[1] < y[1]) }
|
22
|
+
sources.each do |s|
|
23
|
+
seen[s] = 0
|
24
|
+
fringe << [0, count.call(i), s]
|
25
|
+
end
|
26
|
+
|
27
|
+
until fringe.empty?
|
28
|
+
d, _, v = fringe.pop
|
29
|
+
next if dist.key?(v)
|
30
|
+
dist[v] = d
|
31
|
+
break if v == target
|
32
|
+
graph.adj[v].each do |u, attrs|
|
33
|
+
cost = weight.call(v, u, attrs)
|
34
|
+
next if cost.nil?
|
35
|
+
vu_dist = dist[v] + cost
|
36
|
+
next if !cutoff.nil? && vu_dist > cutoff
|
37
|
+
if dist.key?(u)
|
38
|
+
raise ValueError, 'Contradictory weights found!' if vu_dist < dist[u]
|
39
|
+
elsif !seen.key?(u) || vu_dist < seen[u]
|
40
|
+
seen[u] = vu_dist
|
41
|
+
fringe << [vu_dist, count.call(i), u]
|
42
|
+
paths[u] = paths[v] + [u] unless paths.nil?
|
43
|
+
pred[u] = [v] unless pred.nil?
|
44
|
+
elsif vu_dist == seen[u]
|
45
|
+
pred[u] << v unless pred.nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
dist
|
50
|
+
end
|
51
|
+
|
52
|
+
# Helper function for single source dijkstra
|
53
|
+
def self.help_dijkstra(graph, source, weight, pred=nil, paths=nil, cutoff=nil, target=nil)
|
54
|
+
help_multisource_dijkstra(graph, [source], weight, pred, paths, cutoff, target)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Computes shortest paths and path lengths to a target from one of the nodes
|
58
|
+
#
|
59
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
60
|
+
# @param sources [Array<Object>] Array of sources
|
61
|
+
# @param target [Object, nil] target node for the dijkstra algorithm
|
62
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
63
|
+
#
|
64
|
+
# @return [Numeric, Array<Object>] path lengths for all nodes
|
65
|
+
def self.multisource_dijkstra(graph, sources, target=nil, cutoff=nil)
|
66
|
+
raise ValueError, 'Sources cannot be empty' if sources.empty?
|
67
|
+
return [0, [target]] if sources.include?(target)
|
68
|
+
paths = {}
|
69
|
+
weight = get_weight(graph)
|
70
|
+
sources.each { |source| paths[source] = [source] }
|
71
|
+
dist = help_multisource_dijkstra(graph, sources, weight, nil, paths, cutoff, target)
|
72
|
+
return [dist, paths] if target.nil?
|
73
|
+
raise KeyError, "No path to #{target}!" unless dist.key?(target)
|
74
|
+
[dist[target], paths[target]]
|
75
|
+
end
|
76
|
+
|
77
|
+
# Computes shortest path lengths to any from the given nodes
|
78
|
+
#
|
79
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
80
|
+
# @param sources [Array<Object>] Array of sources
|
81
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
82
|
+
#
|
83
|
+
# @return [Hash{ Object => Numeric }] path lengths for any nodes from given nodes
|
84
|
+
def self.multisource_dijkstra_path_length(graph, sources, cutoff=nil)
|
85
|
+
raise ValueError, 'Sources cannot be empty' if sources.empty?
|
86
|
+
weight = get_weight(graph)
|
87
|
+
help_multisource_dijkstra(graph, sources, weight, nil, nil, cutoff)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Computes shortest paths to any from the given nodes
|
91
|
+
#
|
92
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
93
|
+
# @param sources [Array<Object>] Array of sources
|
94
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
95
|
+
#
|
96
|
+
# @return [Hash{ Object => Array<Object> }] paths for any nodes from given nodes
|
97
|
+
def self.multisource_dijkstra_path(graph, sources, cutoff=nil)
|
98
|
+
_, path = multisource_dijkstra(graph, sources, nil, cutoff)
|
99
|
+
path
|
100
|
+
end
|
101
|
+
|
102
|
+
# Computes shortest paths and path distances to all nodes/target from the given node
|
103
|
+
#
|
104
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
105
|
+
# @param source [Object] source
|
106
|
+
# @param target [Object] target
|
107
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
108
|
+
#
|
109
|
+
# @return [Hash{ Object => Array<Object> }, Array<Object>] paths for all nodes/target node from given node
|
110
|
+
def self.singlesource_dijkstra(graph, source, target=nil, cutoff=nil)
|
111
|
+
multisource_dijkstra(graph, [source], target, cutoff)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Computes shortest path lengths to all nodes from the given node
|
115
|
+
#
|
116
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
117
|
+
# @param source [Object] source
|
118
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
119
|
+
#
|
120
|
+
# @return [Hash{ Object => Numeric }] path lengths for all nodes from given node
|
121
|
+
def self.singlesource_dijkstra_path_length(graph, source, cutoff=nil)
|
122
|
+
multisource_dijkstra_path_length(graph, [source], cutoff)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Computes shortest paths to all nodes from the given node
|
126
|
+
#
|
127
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
128
|
+
# @param source [Object] source
|
129
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
130
|
+
#
|
131
|
+
# @return [Hash{ Object => Array<Object> }] paths for all nodes from given node
|
132
|
+
def self.singlesource_dijkstra_path(graph, source, cutoff=nil)
|
133
|
+
multisource_dijkstra_path(graph, [source], cutoff)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Computes shortest path length to target from the given node
|
137
|
+
#
|
138
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
139
|
+
# @param source [Object] source
|
140
|
+
# @param target [Object] target
|
141
|
+
#
|
142
|
+
# @return [Numeric] path length for target node from given node
|
143
|
+
def self.dijkstra_path_length(graph, source, target)
|
144
|
+
return 0 if source == target
|
145
|
+
weight = get_weight(graph)
|
146
|
+
length = help_dijkstra(graph, source, weight, nil, nil, nil, target)
|
147
|
+
raise KeyError, 'Node not reachable!' unless length.key?(target)
|
148
|
+
length[target]
|
149
|
+
end
|
150
|
+
|
151
|
+
# Computes shortest path to target from the given node
|
152
|
+
#
|
153
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
154
|
+
# @param source [Object] source
|
155
|
+
# @param target [Object] target
|
156
|
+
#
|
157
|
+
# @return [Numeric] path for target node from given node
|
158
|
+
def self.dijkstra_path(graph, source, target)
|
159
|
+
_, path = singlesource_dijkstra(graph, source, target)
|
160
|
+
path
|
161
|
+
end
|
162
|
+
|
163
|
+
# Computes weighted shortest path length and predecessors
|
164
|
+
#
|
165
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
166
|
+
# @param source [Object] source
|
167
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
168
|
+
#
|
169
|
+
# @return [<Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>]
|
170
|
+
# predcessor hash and distance hash
|
171
|
+
def self.dijkstra_predecessor_distance(graph, source, cutoff=nil)
|
172
|
+
weight = get_weight(graph)
|
173
|
+
pred = {source => []}
|
174
|
+
[pred, help_dijkstra(graph, source, weight, pred, nil, cutoff)]
|
175
|
+
end
|
176
|
+
|
177
|
+
# Finds shortest weighted paths and lengths between all nodes
|
178
|
+
#
|
179
|
+
# @param graph [Graph, DiGrhelp_dijkaph, MultiGraph, MultiDiGraph] a graph
|
180
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
181
|
+
#
|
182
|
+
# @return [Array<Object, Array<Hash{ Object => Numeric }, Hash{ Object => Array<Object> }>>]
|
183
|
+
# paths and path lengths between all nodes
|
184
|
+
def self.all_pairs_dijkstra(graph, cutoff=nil)
|
185
|
+
path = []
|
186
|
+
graph.nodes.each_key { |n| path << [n, singlesource_dijkstra(graph, n, nil, cutoff)] }
|
187
|
+
path
|
188
|
+
end
|
189
|
+
|
190
|
+
# Finds shortest weighted path length between all nodes
|
191
|
+
#
|
192
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
193
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
194
|
+
#
|
195
|
+
# @return [Array<Object, Hash{ Object => Numeric }>] path lengths between all nodes
|
196
|
+
def self.all_pairs_dijkstra_path_length(graph, cutoff=nil)
|
197
|
+
path_lengths = []
|
198
|
+
graph.nodes.each_key { |n| path_lengths << [n, singlesource_dijkstra_path_length(graph, n, cutoff)] }
|
199
|
+
path_lengths
|
200
|
+
end
|
201
|
+
|
202
|
+
# Finds shortest weighted paths between all nodes
|
203
|
+
#
|
204
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
205
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
206
|
+
#
|
207
|
+
# @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths between all nodes
|
208
|
+
def self.all_pairs_dijkstra_path(graph, cutoff=nil)
|
209
|
+
paths = []
|
210
|
+
graph.nodes.each_key { |n| paths << singlesource_dijkstra_path(graph, n, cutoff) }
|
211
|
+
paths
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO: Reduce method length and method complexity
|
215
|
+
|
216
|
+
# Helper function for bellman ford
|
217
|
+
def self.help_bellman_ford(graph, sources, weight, pred=nil, paths=nil, dist=nil, cutoff=nil, target=nil)
|
218
|
+
pred = sources.product([[]]).to_h if pred.nil?
|
219
|
+
dist = sources.product([0]).to_h if dist.nil?
|
220
|
+
|
221
|
+
inf, n, count, q, in_q = Float::INFINITY, graph.nodes.length, {}, sources.clone, Set.new(sources)
|
222
|
+
until q.empty?
|
223
|
+
u = q.shift
|
224
|
+
in_q.delete(u)
|
225
|
+
skip = false
|
226
|
+
pred[u].each { |k| skip = true if in_q.include?(k) }
|
227
|
+
next if skip
|
228
|
+
dist_u = dist[u]
|
229
|
+
graph.adj[u].each do |v, e|
|
230
|
+
dist_v = dist_u + weight.call(u, v, e)
|
231
|
+
next if !cutoff.nil? && dist_v > cutoff
|
232
|
+
next if !target.nil? && dist_v > (dist[target] || inf)
|
233
|
+
if dist_v < (dist[v] || inf)
|
234
|
+
unless in_q.include?(v)
|
235
|
+
q << v
|
236
|
+
in_q.add(v)
|
237
|
+
count_v = (count[v] || 0) + 1
|
238
|
+
raise ArgumentError, 'Negative edge cycle detected!' if count_v == n
|
239
|
+
count[v] = count_v
|
240
|
+
end
|
241
|
+
dist[v] = dist_v
|
242
|
+
pred[v] = [u]
|
243
|
+
elsif dist.key?(v) && dist_v == dist[v]
|
244
|
+
pred[v] << u
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
unless paths.nil?
|
249
|
+
dsts = target.nil? ? pred : [target]
|
250
|
+
dsts.each_key do |dst|
|
251
|
+
path, cur = [dst], dst
|
252
|
+
until pred[cur][0].nil?
|
253
|
+
cur = pred[cur][0]
|
254
|
+
path << cur
|
255
|
+
end
|
256
|
+
path.reverse
|
257
|
+
paths[dst] = path
|
258
|
+
end
|
259
|
+
end
|
260
|
+
dist
|
261
|
+
end
|
262
|
+
|
263
|
+
# Finds shortest weighted path lengths and predecessors on shortest paths
|
264
|
+
#
|
265
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
266
|
+
# @param source [Object] source
|
267
|
+
# @param target [Object, nil] target
|
268
|
+
# @param cutoff [Numeric, nil] cutoff for the dijkstra algorithm
|
269
|
+
#
|
270
|
+
# @return [Array<Hash{ Object => Array<Object> }, Hash{ Object => Numeric }>] predecessors and distances
|
271
|
+
def self.bellmanford_predecesor_distance(graph, source, target=nil, cutoff=nil)
|
272
|
+
raise ArgumentError, 'Node not found!' unless graph.node?(source)
|
273
|
+
weight = get_weight(graph)
|
274
|
+
# TODO: Detection of selfloop edges
|
275
|
+
dist = {source => 0}
|
276
|
+
pred = {source => []}
|
277
|
+
return [pred, dist] if graph.nodes.length == 1
|
278
|
+
dist = help_bellman_ford(graph, [source], weight, pred, nil, dist, cutoff, target)
|
279
|
+
[pred, dist]
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.singlesource_bellmanford(graph, source, target=nil, cutoff=nil)
|
283
|
+
return [0, [source]] if source == target
|
284
|
+
weight = get_weight(graph)
|
285
|
+
paths = {source => [source]}
|
286
|
+
dist = help_bellman_ford(graph, [source], weight, nil, paths, nil, cutoff, target)
|
287
|
+
return [dist, paths] if target.nil?
|
288
|
+
raise ArgumentError, 'Node not reachable!' unless dist.key?(target)
|
289
|
+
[dist[target], paths[target]]
|
290
|
+
end
|
291
|
+
|
292
|
+
# Length of shortest path from source to target using Bellman Ford algorithm
|
293
|
+
#
|
294
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
295
|
+
# @param source [Object] source
|
296
|
+
# @param target [Object] target
|
297
|
+
#
|
298
|
+
# @return [Numeric] distance between source and target
|
299
|
+
def self.bellmanford_path_length(graph, source, target)
|
300
|
+
return 0 if source == target
|
301
|
+
weight = get_weight(graph)
|
302
|
+
length = help_bellman_ford(graph, [source], weight, nil, nil, nil, nil, target=target)
|
303
|
+
raise ArgumentError, 'Node not reachable!' unless length.key?(target)
|
304
|
+
length[target]
|
305
|
+
end
|
306
|
+
|
307
|
+
# Shortest path from source to target using Bellman Ford algorithm
|
308
|
+
#
|
309
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
310
|
+
# @param source [Object] source
|
311
|
+
# @param target [Object] target
|
312
|
+
#
|
313
|
+
# @return [Array<Object>] path from source to target
|
314
|
+
def self.bellmanford_path(graph, source, target)
|
315
|
+
_, path = singlesource_bellmanford(graph, source, target)
|
316
|
+
path
|
317
|
+
end
|
318
|
+
|
319
|
+
# Shortest path from source to all nodes using Bellman Ford algorithm
|
320
|
+
#
|
321
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
322
|
+
# @param source [Object] source
|
323
|
+
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
324
|
+
#
|
325
|
+
# @return [Hash{ Object => Array<Object> }] path from source to all nodes
|
326
|
+
def self.singlesource_bellmanford_path(graph, source, cutoff=nil)
|
327
|
+
_, path = singlesource_bellmanford(graph, source, cutoff)
|
328
|
+
path
|
329
|
+
end
|
330
|
+
|
331
|
+
# Shortest path length from source to all nodes using Bellman Ford algorithm
|
332
|
+
#
|
333
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
334
|
+
# @param source [Object] source
|
335
|
+
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
336
|
+
#
|
337
|
+
# @return [Hash{ Object => Numeric }] path lengths from source to all nodes
|
338
|
+
def self.singlesource_bellmanford_path_length(graph, source, cutoff=nil)
|
339
|
+
weight = get_weight(graph)
|
340
|
+
help_bellman_ford(graph, [source], weight, nil, nil, nil, cutoff)
|
341
|
+
end
|
342
|
+
|
343
|
+
# Shortest path lengths between all nodes using Bellman Ford algorithm
|
344
|
+
#
|
345
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
346
|
+
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
347
|
+
#
|
348
|
+
# @return [Array<Object, Hash{ Object => Numeric }>] path lengths from source to all nodes
|
349
|
+
def self.allpairs_bellmanford_path_length(graph, cutoff=nil)
|
350
|
+
path_lengths = []
|
351
|
+
graph.nodes.each_key { |n| path_lengths << [n, singlesource_bellmanford_path_length(graph, n, cutoff)] }
|
352
|
+
path_lengths
|
353
|
+
end
|
354
|
+
|
355
|
+
# Shortest paths between all nodes using Bellman Ford algorithm
|
356
|
+
#
|
357
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
358
|
+
# @param cutoff [Numeric, nil] cutoff for Bellman Ford algorithm
|
359
|
+
#
|
360
|
+
# @return [Array<Object, Hash{ Object => Array<Object> }>] path lengths from source to all nodes
|
361
|
+
def self.allpairs_bellmanford_path(graph, cutoff=nil)
|
362
|
+
paths = []
|
363
|
+
graph.nodes.each_key { |n| paths << [n, singlesource_bellmanford_path(graph, n, cutoff)] }
|
364
|
+
paths
|
365
|
+
end
|
366
|
+
|
367
|
+
# Helper function to get sources
|
368
|
+
def self.get_sources(graph)
|
369
|
+
graph.nodes.collect { |k, _v| k }
|
370
|
+
end
|
371
|
+
|
372
|
+
# Helper function to get distances
|
373
|
+
def self.dist_path_lambda(_graph, _new_weight)
|
374
|
+
lambda do |graph, v, new_weight|
|
375
|
+
paths = {v => [v]}
|
376
|
+
_ = help_dijkstra(graph, v, new_weight, nil, paths)
|
377
|
+
paths
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# Helper function to set path lengths for Johnson algorithm
|
382
|
+
def self.set_path_lengths_johnson(graph, dist_path, new_weight)
|
383
|
+
path_lengths = []
|
384
|
+
graph.nodes.each_key { |n| path_lengths << [n, dist_path.call(graph, n, new_weight)] }
|
385
|
+
path_lengths
|
386
|
+
end
|
387
|
+
|
388
|
+
# TODO: Reduce method length and method complexity
|
389
|
+
|
390
|
+
# Returns shortest path between all pairs of nodes
|
391
|
+
#
|
392
|
+
# @param graph [Graph, DiGraph, MultiGraph, MultiDiGraph] a graph
|
393
|
+
#
|
394
|
+
# @return [Array<Object, Hash { Object => Array<Object> }] shortest paths between all pairs of nodes
|
395
|
+
def self.johnson(graph)
|
396
|
+
dist, pred = {}, {}
|
397
|
+
sources = get_sources(graph)
|
398
|
+
graph.nodes.each_key do |n|
|
399
|
+
dist[n], pred[n] = 0, []
|
400
|
+
end
|
401
|
+
weight = get_weight(graph)
|
402
|
+
dist_bellman = help_bellman_ford(graph, sources, weight, pred, nil, dist=dist)
|
403
|
+
new_weight = ->(u, v, d) { weight.call(u, v, d) + dist_bellman[u] - dist_bellman[v] }
|
404
|
+
dist_path = dist_path_lambda(graph, new_weight)
|
405
|
+
path_lengths = set_path_lengths_johnson(graph, dist_path, new_weight)
|
406
|
+
path_lengths
|
407
|
+
end
|
408
|
+
end
|