File Coverage

blib/lib/Algorithm/Kuhn/Munkres.pm
Criterion Covered Total %
statement 94 94 100.0
branch 21 24 87.5
condition 6 6 100.0
subroutine 12 12 100.0
pod 2 2 100.0
total 135 138 97.8


line stmt bran cond sub pod time code
1             package Algorithm::Kuhn::Munkres;
2              
3 2     2   41407 use warnings;
  2         5  
  2         63  
4 2     2   11 use strict;
  2         3  
  2         60  
5 2     2   10 use Carp;
  2         7  
  2         162  
6 2     2   9 use List::Util qw( sum );
  2         4  
  2         226  
7 2     2   10 use base 'Exporter';
  2         3  
  2         2840  
8             our @EXPORT_OK = qw( max_weight_perfect_matching assign );
9             our $VERSION = '1.0.7';
10              
11             my @weights;
12             my $N;
13             my %S;
14             my %T;
15             my @labels_u;
16             my @labels_v;
17             my @min_slack;
18             my %matching_u;
19             my %matching_v;
20              
21              
22             sub _improve_labels {
23 22     22   29 my ($val) = @_;
24              
25 22         51 foreach my $u (keys %S) {
26 43         98 $labels_u[$u] -= $val;
27             }
28              
29 22         66 for (my $v = 0; $v < $N; $v++) {
30 98 100       174 if (exists($T{$v})) {
31 21         64 $labels_v[$v] += $val;
32             } else {
33 77         213 $min_slack[$v]->[0] -= $val;
34             }
35             }
36             }
37              
38             sub _improve_matching {
39 27     27   37 my ($v) = @_;
40 27         39 my $u = $T{$v};
41 27 100       58 if (exists($matching_u{$u})) {
42 8         21 _improve_matching($matching_u{$u});
43             }
44 27         44 $matching_u{$u} = $v;
45 27         60 $matching_v{$v} = $u;
46             }
47              
48             sub _slack {
49 186     186   268 my ($u,$v) = @_;
50 186         358 my $val = $labels_u[$u] + $labels_v[$v] - $weights[$u][$v];
51 186         411 return $val;
52             }
53              
54             sub _augment {
55              
56 19     19   23 while (1) {
57 35         38 my ($val, $u, $v);
58 35         84 for (my $x = 0; $x < $N; $x++) {
59 151 100       355 if (!exists($T{$x})) {
60 127 100 100     534 if (!defined($val) || ($min_slack[$x]->[0] < $val)) {
61 91         142 $val = $min_slack[$x]->[0];
62 91         123 $u = $min_slack[$x]->[1];
63 91         236 $v = $x;
64             }
65             }
66             }
67 35 50       86 die "u should be in S" if (!exists($S{$u}));
68 35 100       68 if ($val > 0) {
69 22         39 _improve_labels($val);
70             }
71 35 50       63 die "slack(u,v) should be 0" if (_slack($u,$v) != 0);
72 35         73 $T{$v} = $u;
73 35 100       69 if (exists($matching_v{$v})) {
74 16         28 my $u1 = $matching_v{$v};
75 16 50       44 die "u1 should not be in S" if (exists($S{$u1}));
76 16         25 $S{$u1} = 1;
77 16         38 for (my $x = 0; $x < $N; $x++) {
78 72         107 my $s = _slack($u1,$x);
79 72 100 100     360 if (!exists($T{$x}) && $min_slack[$x]->[0] > $s) {
80 27         113 $min_slack[$x] = [$s, $u1];
81             }
82             }
83             } else {
84 19         32 _improve_matching($v);
85 19         66 return;
86             }
87             }
88              
89             }
90              
91             sub max_weight_perfect_matching {
92              
93 5     5 1 14 %S = ();
94 5         12 %T = ();
95 5         8 @labels_u = ();
96 5         7 @labels_v = ();
97 5         13 @min_slack = ();
98 5         11 %matching_u = ();
99 5         12 %matching_v = ();
100              
101 5         16 @weights = @_;
102 5         10 $N = scalar @weights;
103 5         45 for (my $i = 0; $i < $N; $i++) {
104 19         55 $labels_v[$i] = 0;
105             }
106 5         13 for (my $i = 0; $i < $N; $i++) {
107 19         24 my $max = 0;
108 19         43 for (my $j = 0; $j < $N; $j++) {
109 79 100       207 if ($weights[$i][$j] > $max) {
110 48         131 $max = $weights[$i][$j];
111             }
112             }
113 19         52 $labels_u[$i] = $max;
114             }
115              
116              
117 5         15 while ($N > scalar keys %matching_u) {
118 19         23 my $free;
119 19         51 for (my $x = 0; $x < $N; $x++) {
120 49 100       144 if (!exists($matching_u{$x})) {
121 19         27 $free = $x;
122 19         31 last;
123             }
124             }
125              
126 19         46 %S = ($free => 1);
127 19         33 %T = ();
128 19         43 @min_slack = ();
129 19         74 for (my $i = 0; $i < $N; $i++) {
130 79         117 my $x = [_slack($free,$i), $free];
131 79         226 push @min_slack, $x;
132             }
133 19         34 _augment();
134             }
135              
136 5         33 my $val = sum(@labels_u) + sum(@labels_v);
137 5         20 return ($val, \%matching_u);
138              
139             }
140              
141             sub assign {
142 5     5 1 2223 max_weight_perfect_matching(@_);
143             }
144              
145             sub _show_hash {
146 1     1   482 my ($hash_ref) = @_;
147 1         2 my $output = "{";
148 1         9 foreach my $key (sort keys %$hash_ref) {
149 6         18 $output .= "$key" . ": " . $hash_ref->{$key} . ", ";
150             }
151 1         6 $output =~ s/, $//;
152 1         3 $output .= "}";
153 1         5 return $output;
154             }
155              
156             1; # Magic true value required at end of module
157             __END__