rgl 0.4.0 → 0.5.0
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.
- data/ChangeLog +19 -10
- data/Gemfile +3 -0
- data/{README → README.rdoc} +70 -98
- data/Rakefile +44 -150
- data/examples/canvas.rb +63 -64
- data/examples/examples.rb +42 -42
- data/examples/graph.dot +46 -0
- data/examples/images/example.jpg +0 -0
- data/examples/images/module_graph.jpg +0 -0
- data/examples/images/rgl_modules.png +0 -0
- data/examples/{insel-der-tausend-gefahren.rb → insel_der_tausend_gefahren.rb} +18 -19
- data/examples/north.rb +2 -2
- data/examples/north2.rb +11 -11
- data/examples/rdep-rgl.rb +218 -222
- data/lib/rgl/adjacency.rb +78 -74
- data/lib/rgl/base.rb +160 -78
- data/lib/rgl/bellman_ford.rb +115 -0
- data/lib/rgl/bidirectional.rb +17 -10
- data/lib/rgl/bipartite.rb +87 -0
- data/lib/rgl/condensation.rb +13 -4
- data/lib/rgl/connected_components.rb +38 -30
- data/lib/rgl/dijkstra.rb +158 -0
- data/lib/rgl/dijkstra_visitor.rb +42 -0
- data/lib/rgl/dot.rb +40 -32
- data/lib/rgl/edge_properties_map.rb +55 -0
- data/lib/rgl/edmonds_karp.rb +136 -0
- data/lib/rgl/enumerable_ext.rb +4 -1
- data/lib/rgl/graph_iterator.rb +15 -0
- data/lib/rgl/graph_visitor.rb +138 -0
- data/lib/rgl/graph_wrapper.rb +15 -0
- data/lib/rgl/graphxml.rb +20 -10
- data/lib/rgl/implicit.rb +68 -66
- data/lib/rgl/mutable.rb +37 -31
- data/lib/rgl/path_builder.rb +40 -0
- data/lib/rgl/prim.rb +52 -0
- data/lib/rgl/rdot.rb +411 -374
- data/lib/rgl/topsort.rb +23 -16
- data/lib/rgl/transitivity.rb +29 -27
- data/lib/rgl/traversal.rb +67 -205
- data/rakelib/dep_graph.rake +4 -3
- data/test/bellman_ford_test.rb +187 -0
- data/test/bipartite_test.rb +47 -0
- data/test/components_test.rb +80 -0
- data/test/cycles_test.rb +60 -0
- data/test/dijkstra_test.rb +148 -0
- data/test/directed_graph_test.rb +118 -0
- data/test/dot_test.rb +26 -0
- data/test/edge_properties_map_test.rb +63 -0
- data/test/edge_test.rb +35 -0
- data/test/edmonds_karp_test.rb +105 -0
- data/{tests/TestGraph.rb → test/graph_test.rb} +6 -6
- data/test/graph_xml_test.rb +57 -0
- data/test/implicit_test.rb +53 -0
- data/test/prim_test.rb +98 -0
- data/{tests/TestRdot.rb → test/rdot_test.rb} +309 -308
- data/{tests → test}/test_helper.rb +4 -1
- data/{tests/TestTransitivity.rb → test/transitivity_test.rb} +43 -43
- data/test/traversal_test.rb +221 -0
- data/test/undirected_graph_test.rb +103 -0
- metadata +226 -145
- data/examples/example.jpg +0 -0
- data/examples/module_graph.jpg +0 -0
- data/install.rb +0 -49
- data/tests/TestComponents.rb +0 -65
- data/tests/TestCycles.rb +0 -61
- data/tests/TestDirectedGraph.rb +0 -125
- data/tests/TestDot.rb +0 -18
- data/tests/TestEdge.rb +0 -34
- data/tests/TestGraphXML.rb +0 -57
- data/tests/TestImplicit.rb +0 -52
- data/tests/TestTraversal.rb +0 -220
- data/tests/TestUnDirectedGraph.rb +0 -102
data/lib/rgl/dijkstra.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'rgl/dijkstra_visitor'
|
2
|
+
require 'rgl/edge_properties_map'
|
3
|
+
require 'rgl/path_builder'
|
4
|
+
|
5
|
+
require 'delegate'
|
6
|
+
require 'algorithms'
|
7
|
+
|
8
|
+
module RGL
|
9
|
+
|
10
|
+
class DijkstraAlgorithm
|
11
|
+
|
12
|
+
# Distance combinator is a lambda that accepts the distance (usually from the source) to vertex _u_ and the weight
|
13
|
+
# of the edge connecting vertex _u_ to another vertex _v_ and returns the distance to vertex _v_ if it's reached
|
14
|
+
# through the vertex _u_. By default, the distance to vertex _u_ and the edge's weight are summed.
|
15
|
+
DEFAULT_DISTANCE_COMBINATOR = lambda { |distance, edge_weight| distance + edge_weight }
|
16
|
+
|
17
|
+
# Initializes Dijkstra's algorithm for a _graph_ with provided edges weights map.
|
18
|
+
#
|
19
|
+
def initialize(graph, edge_weights_map, visitor, distance_combinator = nil)
|
20
|
+
@graph = graph
|
21
|
+
@edge_weights_map = build_edge_weights_map(edge_weights_map)
|
22
|
+
@visitor = visitor
|
23
|
+
@distance_combinator = distance_combinator || DEFAULT_DISTANCE_COMBINATOR
|
24
|
+
end
|
25
|
+
|
26
|
+
# Finds the shortest path from the _source_ to the _target_ in the graph.
|
27
|
+
#
|
28
|
+
# Returns the shortest path, if it exists, as an Array of vertices. Otherwise, returns nil.
|
29
|
+
#
|
30
|
+
def shortest_path(source, target)
|
31
|
+
init(source)
|
32
|
+
relax_edges(target, true)
|
33
|
+
PathBuilder.new(source, @visitor.parents_map).path(target)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Finds the shortest path form the _source_ to every other vertex of the graph and builds shortest paths map.
|
37
|
+
#
|
38
|
+
# Returns the shortest paths map that contains the shortest path (if it exists) from the source to any vertex of the
|
39
|
+
# graph.
|
40
|
+
#
|
41
|
+
def shortest_paths(source)
|
42
|
+
find_shortest_paths(source)
|
43
|
+
PathBuilder.new(source, @visitor.parents_map).paths(@graph.vertices)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Finds the shortest path from the _source_ to every other vertex.
|
47
|
+
#
|
48
|
+
def find_shortest_paths(source)
|
49
|
+
init(source)
|
50
|
+
relax_edges
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def init(source)
|
56
|
+
@visitor.set_source(source)
|
57
|
+
|
58
|
+
@queue = Queue.new
|
59
|
+
@queue.push(source, 0)
|
60
|
+
end
|
61
|
+
|
62
|
+
def relax_edges(target = nil, break_on_target = false)
|
63
|
+
until @queue.empty?
|
64
|
+
u = @queue.pop
|
65
|
+
|
66
|
+
break if break_on_target && u == target
|
67
|
+
|
68
|
+
@visitor.handle_examine_vertex(u)
|
69
|
+
|
70
|
+
@graph.each_adjacent(u) do |v|
|
71
|
+
relax_edge(u, v) unless @visitor.finished_vertex?(v)
|
72
|
+
end
|
73
|
+
|
74
|
+
@visitor.color_map[u] = :BLACK
|
75
|
+
@visitor.handle_finish_vertex(u)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def relax_edge(u, v)
|
80
|
+
@visitor.handle_examine_edge(u, v)
|
81
|
+
|
82
|
+
new_v_distance = @distance_combinator.call(@visitor.distance_map[u], @edge_weights_map.edge_property(u, v))
|
83
|
+
|
84
|
+
if new_v_distance < @visitor.distance_map[v]
|
85
|
+
old_v_distance = @visitor.distance_map[v]
|
86
|
+
|
87
|
+
@visitor.distance_map[v] = new_v_distance
|
88
|
+
@visitor.parents_map[v] = u
|
89
|
+
|
90
|
+
if @visitor.color_map[v] == :WHITE
|
91
|
+
@visitor.color_map[v] = :GRAY
|
92
|
+
@queue.push(v, new_v_distance)
|
93
|
+
elsif @visitor.color_map[v] == :GRAY
|
94
|
+
@queue.decrease_key(v, old_v_distance, new_v_distance)
|
95
|
+
end
|
96
|
+
|
97
|
+
@visitor.handle_edge_relaxed(u, v)
|
98
|
+
else
|
99
|
+
@visitor.handle_edge_not_relaxed(u, v)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_edge_weights_map(edge_weights_map)
|
104
|
+
edge_weights_map.is_a?(EdgePropertiesMap) ? edge_weights_map : NonNegativeEdgePropertiesMap.new(edge_weights_map, @graph.directed?)
|
105
|
+
end
|
106
|
+
|
107
|
+
class Queue < SimpleDelegator # :nodoc:
|
108
|
+
|
109
|
+
def initialize
|
110
|
+
@heap = Containers::Heap.new { |a, b| a.distance < b.distance }
|
111
|
+
super(@heap)
|
112
|
+
end
|
113
|
+
|
114
|
+
def push(vertex, distance)
|
115
|
+
@heap.push(vertex_key(vertex, distance), vertex)
|
116
|
+
end
|
117
|
+
|
118
|
+
def decrease_key(vertex, old_distance, new_distance)
|
119
|
+
@heap.change_key(vertex_key(vertex, old_distance), vertex_key(vertex, new_distance))
|
120
|
+
end
|
121
|
+
|
122
|
+
def vertex_key(vertex, distance)
|
123
|
+
VertexKey.new(vertex, distance)
|
124
|
+
end
|
125
|
+
|
126
|
+
VertexKey = Struct.new(:vertex, :distance)
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end # class DijkstraAlgorithm
|
131
|
+
|
132
|
+
module Graph
|
133
|
+
|
134
|
+
# Finds the shortest path from the _source_ to the _target_ in the graph.
|
135
|
+
#
|
136
|
+
# If the path exists, returns it as an Array of vertices. Otherwise, returns nil.
|
137
|
+
#
|
138
|
+
# Raises ArgumentError if edge weight is negative or undefined.
|
139
|
+
#
|
140
|
+
def dijkstra_shortest_path(edge_weights_map, source, target, visitor = DijkstraVisitor.new(self))
|
141
|
+
DijkstraAlgorithm.new(self, edge_weights_map, visitor).shortest_path(source, target)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Finds the shortest paths from the _source_ to each vertex of the graph.
|
145
|
+
#
|
146
|
+
# Returns a Hash that maps each vertex of the graph to an Array of vertices that represents the shortest path
|
147
|
+
# from the _source_ to the vertex. If the path doesn't exist, the corresponding hash value is nil. For the _source_
|
148
|
+
# vertex returned hash contains a trivial one-vertex path - [source].
|
149
|
+
#
|
150
|
+
# Raises ArgumentError if edge weight is negative or undefined.
|
151
|
+
#
|
152
|
+
def dijkstra_shortest_paths(edge_weights_map, source, visitor = DijkstraVisitor.new(self))
|
153
|
+
DijkstraAlgorithm.new(self, edge_weights_map, visitor).shortest_paths(source)
|
154
|
+
end
|
155
|
+
|
156
|
+
end # module Graph
|
157
|
+
|
158
|
+
end # module RGL
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rgl/base'
|
2
|
+
require 'rgl/graph_visitor'
|
3
|
+
|
4
|
+
module RGL
|
5
|
+
|
6
|
+
# Dijkstra shortest path algorithm has the following event points:
|
7
|
+
#
|
8
|
+
# * examine_vertex
|
9
|
+
# * examine_edge
|
10
|
+
# * edge_relaxed
|
11
|
+
# * edge_not_relaxed
|
12
|
+
# * finish_vertex
|
13
|
+
#
|
14
|
+
class DijkstraVisitor
|
15
|
+
|
16
|
+
include GraphVisitor
|
17
|
+
|
18
|
+
attr_accessor :distance_map, :parents_map
|
19
|
+
|
20
|
+
def_event_handlers :edge_relaxed, :edge_not_relaxed
|
21
|
+
|
22
|
+
# Returns visitor into initial state.
|
23
|
+
#
|
24
|
+
def reset
|
25
|
+
super
|
26
|
+
|
27
|
+
@distance_map = Hash.new(INFINITY)
|
28
|
+
@parents_map = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Initializes visitor with a new source.
|
32
|
+
#
|
33
|
+
def set_source(source)
|
34
|
+
reset
|
35
|
+
|
36
|
+
color_map[source] = :GRAY
|
37
|
+
distance_map[source] = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
end # DijkstraVisitor
|
41
|
+
|
42
|
+
end # RGL
|
data/lib/rgl/dot.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# dot.rb
|
2
2
|
#
|
3
|
-
# $Id
|
3
|
+
# $Id$
|
4
4
|
#
|
5
5
|
# Minimal Dot support, based on Dave Thomas's dot module (included in rdoc).
|
6
6
|
# rdot.rb is a modified version which also contains support for undirected
|
@@ -12,50 +12,57 @@ module RGL
|
|
12
12
|
|
13
13
|
module Graph
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# Return a RGL::DOT::Digraph for directed graphs or a DOT::Graph for an
|
16
|
+
# undirected Graph. _params_ can contain any graph property specified in
|
17
|
+
# rdot.rb.
|
18
|
+
#
|
19
|
+
def to_dot_graph(params = {})
|
20
|
+
params['name'] ||= self.class.name.gsub(/:/, '_')
|
21
|
+
fontsize = params['fontsize'] ? params['fontsize'] : '8'
|
22
|
+
graph = (directed? ? DOT::Digraph : DOT::Graph).new(params)
|
23
|
+
edge_class = directed? ? DOT::DirectedEdge : DOT::Edge
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
25
|
+
each_vertex do |v|
|
26
|
+
name = v.to_s
|
27
|
+
graph << DOT::Node.new(
|
28
|
+
'name' => name,
|
29
|
+
'fontsize' => fontsize,
|
30
|
+
'label' => name
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
each_edge do |u, v|
|
35
|
+
graph << edge_class.new(
|
36
|
+
'from' => u.to_s,
|
37
|
+
'to' => v.to_s,
|
38
|
+
'fontsize' => fontsize
|
39
|
+
)
|
34
40
|
end
|
41
|
+
|
35
42
|
graph
|
36
43
|
end
|
37
44
|
|
38
45
|
# Output the DOT-graph to stream _s_.
|
39
|
-
|
40
|
-
def print_dotted_on
|
46
|
+
#
|
47
|
+
def print_dotted_on(params = {}, s = $stdout)
|
41
48
|
s << to_dot_graph(params).to_s << "\n"
|
42
49
|
end
|
43
50
|
|
44
51
|
# Call dotty[http://www.graphviz.org] for the graph which is written to the
|
45
|
-
# file 'graph.dot' in the
|
46
|
-
|
47
|
-
def dotty
|
52
|
+
# file 'graph.dot' in the current directory.
|
53
|
+
#
|
54
|
+
def dotty(params = {})
|
48
55
|
dotfile = "graph.dot"
|
49
|
-
File.open(dotfile, "w")
|
56
|
+
File.open(dotfile, "w") do |f|
|
50
57
|
print_dotted_on(params, f)
|
51
|
-
|
58
|
+
end
|
52
59
|
system("dotty", dotfile)
|
53
60
|
end
|
54
61
|
|
55
62
|
# Use dot[http://www.graphviz.org] to create a graphical representation of
|
56
|
-
# the graph.
|
57
|
-
|
58
|
-
def write_to_graphic_file
|
63
|
+
# the graph. Returns the filename of the graphics file.
|
64
|
+
#
|
65
|
+
def write_to_graphic_file(fmt='png', dotfile="graph")
|
59
66
|
src = dotfile + ".dot"
|
60
67
|
dot = dotfile + "." + fmt
|
61
68
|
|
@@ -63,9 +70,10 @@ module RGL
|
|
63
70
|
f << self.to_dot_graph.to_s << "\n"
|
64
71
|
end
|
65
72
|
|
66
|
-
system(
|
73
|
+
system("dot -T#{fmt} #{src} -o #{dot}")
|
67
74
|
dot
|
68
75
|
end
|
69
76
|
|
70
|
-
end
|
71
|
-
|
77
|
+
end # module Graph
|
78
|
+
|
79
|
+
end # module RGL
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RGL
|
2
|
+
|
3
|
+
class EdgePropertiesMap
|
4
|
+
|
5
|
+
def initialize(edge_properties_map, directed)
|
6
|
+
@edge_properties_map = edge_properties_map
|
7
|
+
@directed = directed
|
8
|
+
|
9
|
+
check_properties
|
10
|
+
end
|
11
|
+
|
12
|
+
def edge_property(u, v)
|
13
|
+
if @directed
|
14
|
+
property = @edge_properties_map[[u, v]]
|
15
|
+
else
|
16
|
+
property = @edge_properties_map[[u, v]] || @edge_properties_map[[v, u]]
|
17
|
+
end
|
18
|
+
|
19
|
+
validate_property(property, u, v)
|
20
|
+
|
21
|
+
property
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def check_properties
|
27
|
+
@edge_properties_map.each { |(u, v), property| validate_property(property, u, v) } if @edge_properties_map.respond_to?(:each)
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_property(property, u, v)
|
31
|
+
report_missing_property(property, u, v)
|
32
|
+
end
|
33
|
+
|
34
|
+
def report_missing_property(property, u, v)
|
35
|
+
raise ArgumentError.new("property of edge (#{u}, #{v}) is not defined") unless property
|
36
|
+
end
|
37
|
+
|
38
|
+
end # EdgePropertiesMap
|
39
|
+
|
40
|
+
class NonNegativeEdgePropertiesMap < EdgePropertiesMap
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def validate_property(property, u, v)
|
45
|
+
super
|
46
|
+
report_negative_property(property, u, v)
|
47
|
+
end
|
48
|
+
|
49
|
+
def report_negative_property(property, u, v)
|
50
|
+
raise ArgumentError.new("property of edge (#{u}, #{v}) is negative") if property < 0
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'rgl/edge_properties_map'
|
2
|
+
require 'rgl/traversal'
|
3
|
+
|
4
|
+
module RGL
|
5
|
+
|
6
|
+
class EdmondsKarpAlgorithm
|
7
|
+
|
8
|
+
# Initializes Edmonds-Karp algorithm for a _graph_ with provided edges capacities map.
|
9
|
+
#
|
10
|
+
def initialize(graph, edge_capacities_map)
|
11
|
+
raise NotDirectedError.new('Edmonds-Karp algorithm can only be applied to a directed graph') unless graph.directed?
|
12
|
+
|
13
|
+
@graph = graph
|
14
|
+
validate_edge_capacities(edge_capacities_map)
|
15
|
+
@edge_capacities_map = NonNegativeEdgePropertiesMap.new(edge_capacities_map, true)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Finds the maximum flow from the _source_ to the _sink_ in the graph.
|
19
|
+
#
|
20
|
+
# Returns flows map as a hash that maps each edge of the graph to a flow through that edge that is required to reach
|
21
|
+
# the maximum total flow.
|
22
|
+
#
|
23
|
+
def maximum_flow(source, sink)
|
24
|
+
raise ArgumentError.new("source and sink can't be equal") if source == sink
|
25
|
+
|
26
|
+
@flow_map = Hash.new(0)
|
27
|
+
@residual_capacity_map = lambda { |u, v| @edge_capacities_map.edge_property(u, v) - @flow_map[[u, v]] }
|
28
|
+
|
29
|
+
loop do
|
30
|
+
bfs = EdmondsKarpBFSIterator.new(@graph, source, sink, @residual_capacity_map)
|
31
|
+
|
32
|
+
bfs.move_forward_until { bfs.color_map[sink] == :GRAY }
|
33
|
+
|
34
|
+
if bfs.color_map[sink] == :WHITE
|
35
|
+
break # no more augmenting paths
|
36
|
+
else
|
37
|
+
min_residual_capacity = INFINITY
|
38
|
+
|
39
|
+
augmenting_path = [sink]
|
40
|
+
|
41
|
+
while augmenting_path.first != source
|
42
|
+
v = augmenting_path.first
|
43
|
+
u = bfs.parents_map[v]
|
44
|
+
|
45
|
+
augmenting_path.unshift(u)
|
46
|
+
min_residual_capacity = [min_residual_capacity, @residual_capacity_map[u, v]].min
|
47
|
+
end
|
48
|
+
|
49
|
+
augmenting_path.each_cons(2) do |(u, v)|
|
50
|
+
@flow_map[[u, v]] += min_residual_capacity
|
51
|
+
@flow_map[[v, u]] -= min_residual_capacity
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@flow_map
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def validate_edge_capacities(edge_capacities_map)
|
62
|
+
@graph.each_edge do |u, v|
|
63
|
+
raise ArgumentError.new("reverse edge for (#{u}, #{v}) is missing") unless @graph.has_edge?(v, u)
|
64
|
+
validate_capacity(u, v, edge_capacities_map)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_capacity(u, v, edge_capacities_map)
|
69
|
+
capacity = get_capacity(u, v, edge_capacities_map)
|
70
|
+
reverse_capacity = get_capacity(v, u, edge_capacities_map)
|
71
|
+
|
72
|
+
validate_negative_capacity(u, v, capacity)
|
73
|
+
validate_negative_capacity(v, u, reverse_capacity)
|
74
|
+
|
75
|
+
raise ArgumentError.new("either (#{u}, #{v}) or (#{v}, #{u}) should have 0 capacity") unless [capacity, reverse_capacity].include?(0)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_capacity(u, v, edge_capacities_map)
|
79
|
+
edge_capacities_map.fetch([u, v]) { raise ArgumentError.new("capacity for edge (#{u}, #{v}) is missing") }
|
80
|
+
end
|
81
|
+
|
82
|
+
def validate_negative_capacity(u, v, capacity)
|
83
|
+
raise ArgumentError.new("capacity of edge (#{u}, #{v}) is negative") unless capacity >= 0
|
84
|
+
end
|
85
|
+
|
86
|
+
class EdmondsKarpBFSIterator < BFSIterator
|
87
|
+
|
88
|
+
attr_accessor :parents_map
|
89
|
+
|
90
|
+
def initialize(graph, start, stop, residual_capacities)
|
91
|
+
super(graph, start)
|
92
|
+
@residual_capacities = residual_capacities
|
93
|
+
@stop_vertex = stop
|
94
|
+
end
|
95
|
+
|
96
|
+
def reset
|
97
|
+
super
|
98
|
+
@parents_map = {}
|
99
|
+
end
|
100
|
+
|
101
|
+
def follow_edge?(u, v)
|
102
|
+
# follow only edges with positive residual capacity
|
103
|
+
super && @residual_capacities[u, v] > 0
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_tree_edge(u, v)
|
107
|
+
super
|
108
|
+
@parents_map[v] = u
|
109
|
+
end
|
110
|
+
|
111
|
+
end # class EdmondsKarpBFSIterator
|
112
|
+
|
113
|
+
end # class EdmondsKarpAlgorithm
|
114
|
+
|
115
|
+
module Graph
|
116
|
+
|
117
|
+
# Finds the maximum flow from the _source_ to the _sink_ in the graph.
|
118
|
+
#
|
119
|
+
# Returns flows map as a hash that maps each edge of the graph to a flow through that edge that is required to reach
|
120
|
+
# the maximum total flow.
|
121
|
+
#
|
122
|
+
# For the method to work, the graph should be first altered so that for each directed edge (u, v) it contains reverse
|
123
|
+
# edge (u, v). Capacities of the primary edges should be non-negative, while reverse edges should have zero capacity.
|
124
|
+
#
|
125
|
+
# Raises ArgumentError if the graph is not directed.
|
126
|
+
#
|
127
|
+
# Raises ArgumentError if a reverse edge is missing, edge capacity is missing, an edge has negative capacity, or a
|
128
|
+
# reverse edge has positive capacity.
|
129
|
+
#
|
130
|
+
def maximum_flow(edge_capacities_map, source, sink)
|
131
|
+
EdmondsKarpAlgorithm.new(self, edge_capacities_map).maximum_flow(source, sink)
|
132
|
+
end
|
133
|
+
|
134
|
+
end # module Graph
|
135
|
+
|
136
|
+
end
|