line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Graph::MaxFlow; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
require Exporter; |
4
|
1
|
|
|
1
|
|
206621
|
use Graph; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
5
|
1
|
|
|
1
|
|
6
|
use Carp 'carp'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
95
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
8
|
|
|
|
|
|
|
@EXPORT_OK = qw(max_flow); |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
$VERSION = '0.03'; |
11
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
32
|
|
13
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
797
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# Edmonds-Karp algorithm to find the maximum flow in $g between |
16
|
|
|
|
|
|
|
# $source and $sink |
17
|
|
|
|
|
|
|
sub max_flow { |
18
|
2
|
|
|
2
|
1
|
15163
|
my ($g, $source, $sink) = @_; |
19
|
|
|
|
|
|
|
|
20
|
2
|
50
|
|
|
|
22
|
if ($g->is_undirected) { |
21
|
0
|
|
|
|
|
0
|
carp "Graph must be directed"; |
22
|
0
|
|
|
|
|
0
|
return; |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
|
25
|
2
|
50
|
|
|
|
32
|
if ($g->vertices < 2) { |
26
|
0
|
|
|
|
|
0
|
carp "Graph must have at least 2 vertices"; |
27
|
0
|
|
|
|
|
0
|
return; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
2
|
|
|
|
|
138
|
my $resid = init_flow($g); |
31
|
|
|
|
|
|
|
|
32
|
2
|
|
|
|
|
4
|
while (1) { |
33
|
|
|
|
|
|
|
# find the shortest augmenting path between $source and $sink |
34
|
8
|
|
|
|
|
480
|
my $path = shortest_path($g, $resid, $source, $sink); |
35
|
8
|
100
|
|
|
|
21
|
last unless @$path; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# find min weight in path |
38
|
6
|
|
|
|
|
6
|
my $min; |
39
|
6
|
|
|
|
|
14
|
for my $i (0..$#$path - 1) { |
40
|
19
|
|
|
|
|
66
|
my $weight = residual_capacity($g, $resid, $path->[$i], $path->[$i+1]); |
41
|
19
|
100
|
100
|
|
|
2916
|
$min = $weight if !defined $min || $weight < $min; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# update the flow network |
45
|
6
|
|
|
|
|
17
|
for my $i (0..$#$path - 1) { |
46
|
19
|
|
|
|
|
970
|
add_edge_weight($resid, $path->[$i], $path->[$i+1], $min); |
47
|
19
|
|
|
|
|
1399
|
add_edge_weight($resid, $path->[$i+1], $path->[$i], -$min); |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# convert the residual flow graph into a copy of the original |
53
|
|
|
|
|
|
|
# graph, but with the edge weights set to the flow |
54
|
2
|
|
|
|
|
12
|
my $flow = $g->copy_graph; |
55
|
2
|
|
|
|
|
3861
|
for my $e ($flow->edges) { |
56
|
27
|
|
|
|
|
5610
|
my ($u, $v) = @$e; |
57
|
27
|
|
|
|
|
71
|
my $weight = $resid->get_edge_weight($u, $v); |
58
|
27
|
100
|
|
|
|
2282
|
$flow->set_edge_weight($u, $v, $weight > 0 ? $weight : 0); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
2
|
|
|
|
|
246
|
return $flow; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# init the flow so that f(u,v) = 0 for all edges |
65
|
|
|
|
|
|
|
sub init_flow { |
66
|
2
|
|
|
2
|
0
|
6
|
my $g = shift; |
67
|
2
|
|
|
|
|
9
|
my $flow = new Graph; |
68
|
|
|
|
|
|
|
|
69
|
2
|
|
|
|
|
285
|
for my $e ($g->edges) { |
70
|
27
|
|
|
|
|
4176
|
$flow->add_weighted_edge($e->[0], $e->[1], 0); |
71
|
27
|
|
|
|
|
4769
|
$flow->add_weighted_edge($e->[1], $e->[0], 0); |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
2
|
|
|
|
|
285
|
return $flow; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# do a breadth-first search over edges with positive residual capacity |
78
|
|
|
|
|
|
|
sub shortest_path { |
79
|
8
|
|
|
8
|
0
|
13
|
my ($g, $flow, $from, $to) = @_; |
80
|
|
|
|
|
|
|
|
81
|
8
|
|
|
|
|
11
|
my %parent; |
82
|
|
|
|
|
|
|
my @next; |
83
|
|
|
|
|
|
|
|
84
|
8
|
|
|
|
|
19
|
$parent{$from} = undef; |
85
|
8
|
|
|
|
|
12
|
$next[0] = $from; |
86
|
8
|
|
|
|
|
9
|
my $found = 0; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# loop until we either reach $to or run out of nodes in the @next queue |
89
|
8
|
|
|
|
|
23
|
while (@next) { |
90
|
60
|
|
|
|
|
807
|
my $u = shift @next; |
91
|
60
|
100
|
|
|
|
127
|
if ($u eq $to) { |
92
|
6
|
|
|
|
|
7
|
$found = 1; |
93
|
6
|
|
|
|
|
8
|
last; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
54
|
|
|
|
|
141
|
for my $v ($g->neighbors($u)) { |
97
|
166
|
100
|
|
|
|
8755
|
next if exists $parent{$v}; |
98
|
74
|
100
|
|
|
|
129
|
next unless residual_capacity($g, $flow, $u, $v) > 0; |
99
|
54
|
|
|
|
|
8157
|
$parent{$v} = $u; |
100
|
54
|
|
|
|
|
121
|
push @next, $v; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# reconstruct path |
105
|
8
|
|
|
|
|
170
|
my @path; |
106
|
8
|
100
|
|
|
|
16
|
if ($found) { |
107
|
6
|
|
|
|
|
9
|
my $u = $to; |
108
|
6
|
|
|
|
|
16
|
while (defined $parent{$u}) { |
109
|
19
|
|
|
|
|
29
|
unshift @path, $u; |
110
|
19
|
|
|
|
|
36
|
$u = $parent{$u}; |
111
|
|
|
|
|
|
|
} |
112
|
6
|
|
|
|
|
11
|
unshift @path, $from; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
8
|
|
|
|
|
29
|
return \@path; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# add $delta to the weight of the edge ($u, $v) |
119
|
|
|
|
|
|
|
sub add_edge_weight { |
120
|
38
|
|
|
38
|
0
|
65
|
my ($g, $u, $v, $delta) = @_; |
121
|
|
|
|
|
|
|
|
122
|
38
|
|
|
|
|
96
|
my $weight = $g->get_edge_weight($u, $v); |
123
|
38
|
|
|
|
|
2938
|
$g->set_edge_weight($u, $v, $weight + $delta); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# returns the residual capacity between $u and $v |
127
|
|
|
|
|
|
|
sub residual_capacity { |
128
|
93
|
|
|
93
|
0
|
135
|
my ($g, $flow, $u, $v) = @_; |
129
|
|
|
|
|
|
|
|
130
|
93
|
100
|
|
|
|
222
|
if ($g->has_edge($u, $v)) { |
131
|
84
|
|
|
|
|
1584
|
return $g->get_edge_weight($u, $v) - $flow->get_edge_weight($u, $v); |
132
|
|
|
|
|
|
|
} else { |
133
|
9
|
|
|
|
|
170
|
return -$flow->get_edge_weight($u, $v); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
1; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head1 NAME |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
Graph::MaxFlow - compute maximum flow between 2 vertices in a graph |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head1 SYNOPSIS |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
use Graph::MaxFlow qw(max_flow); |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $g = new Graph; |
149
|
|
|
|
|
|
|
# construct graph |
150
|
|
|
|
|
|
|
my $flow = max_flow($g, "source", "sink"); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head1 DESCRIPTION |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Computes the maximum flow in a graph, represented using Jarkko |
155
|
|
|
|
|
|
|
Hietaniemi's Graph.pm module. |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=head1 FUNCTIONS |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
This module provides the following function: |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=over 4 |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item max_flow($g, $s, $t) |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Computes the maximum flow in the graph $g between vertices $s and $t |
166
|
|
|
|
|
|
|
using the Edmonds-Karp algorithm. $g must be a Graph.pm object, and |
167
|
|
|
|
|
|
|
must be a directed graph where the edge weights indicate the capacity |
168
|
|
|
|
|
|
|
of each edge. The edge weights must be nonnegative. $s and $t must |
169
|
|
|
|
|
|
|
be vertices in the graph. The graph $g must be connected, and for |
170
|
|
|
|
|
|
|
every vertex v besides $s and $t there must be a path from $s to $t |
171
|
|
|
|
|
|
|
that passes through v. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
The return value is a new directed graph which has the same vertices |
174
|
|
|
|
|
|
|
and edges as $g, but where the edge weights have been adjusted to |
175
|
|
|
|
|
|
|
indicate the flow along each edge. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head1 AUTHOR |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Walt Mankowski, Ewaltman@cpan.orgE |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Copyright 2010 by Walt Mankowski |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
188
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
The algorithms are adapted from Introduction to Algorithms, Second |
193
|
|
|
|
|
|
|
Edition, Cormen-Leiserson-Rivest-Stein, MIT Press. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |