line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
243825
|
use strict; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
30
|
|
2
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
43
|
|
3
|
1
|
|
|
1
|
|
26
|
use 5.022; |
|
1
|
|
|
|
|
2
|
|
4
|
1
|
|
|
1
|
|
6
|
use feature qw /postderef signatures/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
120
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Vote::Count::Method::CondorcetDropping; |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
400
|
use namespace::autoclean; |
|
1
|
|
|
|
|
8143
|
|
|
1
|
|
|
|
|
5
|
|
9
|
1
|
|
|
1
|
|
535
|
use Moose; |
|
1
|
|
|
|
|
397810
|
|
|
1
|
|
|
|
|
8
|
|
10
|
|
|
|
|
|
|
extends 'Vote::Count'; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION='0.007'; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
Vote::Count::Method::CondorcetDropping |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 VERSION 0.007 |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# ABSTRACT: Methods which use simple dropping rules to resolve a Winnerless Condorcet Matrix. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#buildpod |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=pod |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 Condorcet Dropping Methods |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
This module implements dropping methodologies for resolving a Condorcet Matrix with no Winner. Dropping Methodologies apply a rule to either all remaining choices or to those with the least wins to select a choice for elimination. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 Basic Dropping Methods |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Common Dropping Methods are: Boorda Count (with all the attendant weighting issues), Approval, Plurality Loser (TopCount), and Greatest Loss. Greatest Loss is not currently available, and will likely be implemented in the SSD module if and when that is ever written. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 SYNOPSIS |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=cut |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
#buildpod |
43
|
|
|
|
|
|
|
|
44
|
1
|
|
|
1
|
|
6463
|
no warnings 'experimental'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
43
|
|
45
|
1
|
|
|
1
|
|
4
|
use List::Util qw( min max ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
97
|
|
46
|
|
|
|
|
|
|
# use YAML::XS; |
47
|
|
|
|
|
|
|
|
48
|
1
|
|
|
1
|
|
559
|
use Vote::Count::Matrix; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
36
|
|
49
|
1
|
|
|
1
|
|
7
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1329
|
|
50
|
|
|
|
|
|
|
# use Try::Tiny; |
51
|
|
|
|
|
|
|
# use Text::Table::Tiny 'generate_markdown_table'; |
52
|
|
|
|
|
|
|
# use Data::Printer; |
53
|
|
|
|
|
|
|
# use Data::Dumper; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
has 'Matrix' => ( |
56
|
|
|
|
|
|
|
isa => 'Object', |
57
|
|
|
|
|
|
|
is => 'ro', |
58
|
|
|
|
|
|
|
lazy => 1, |
59
|
|
|
|
|
|
|
builder => '_newmatrix', |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# DropStyle: whether to apply drop rule against |
63
|
|
|
|
|
|
|
# all choices ('all') or the least winning ('leastwins'). |
64
|
|
|
|
|
|
|
has 'DropStyle' => ( |
65
|
|
|
|
|
|
|
isa => 'Str', |
66
|
|
|
|
|
|
|
is => 'ro', |
67
|
|
|
|
|
|
|
default => 'leastwins', |
68
|
|
|
|
|
|
|
); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
has 'DropRule' => ( |
71
|
|
|
|
|
|
|
isa => 'Str', |
72
|
|
|
|
|
|
|
is => 'ro', |
73
|
|
|
|
|
|
|
default => 'plurality', |
74
|
|
|
|
|
|
|
); |
75
|
|
|
|
|
|
|
|
76
|
30
|
|
|
30
|
0
|
60
|
sub GetRound ( $self, $active, $roundnum='' ) { |
|
30
|
|
|
|
|
113
|
|
|
30
|
|
|
|
|
52
|
|
|
30
|
|
|
|
|
43
|
|
|
30
|
|
|
|
|
36
|
|
77
|
30
|
|
|
|
|
937
|
my $rule = lc( $self->DropRule() ); |
78
|
30
|
100
|
|
|
|
243
|
if ( $rule =~ m/(plurality|topcount)/ ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
79
|
21
|
|
|
|
|
88
|
return $self->TopCount($active); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
elsif ( $rule eq 'approval' ) { |
82
|
2
|
|
|
|
|
19
|
my $round = $self->Approval($active); |
83
|
2
|
|
|
|
|
13
|
$self->logv( "Round $roundnum Approval Totals ", $round->RankTable() ); |
84
|
2
|
|
|
|
|
9
|
return $round; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
elsif ( $rule eq 'boorda' ) { |
87
|
7
|
|
|
|
|
39
|
my $round = $self->Boorda($active); |
88
|
7
|
|
|
|
|
32
|
$self->logv( "Round $roundnum Boorda Count ", $round->RankTable() ); |
89
|
7
|
|
|
|
|
27
|
return $round; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
elsif ( $rule eq 'greatestloss' ) { |
92
|
0
|
|
|
|
|
0
|
...; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
else { |
95
|
0
|
|
|
|
|
0
|
croak "undefined dropping rule $rule requested"; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
14
|
|
|
14
|
0
|
22
|
sub DropChoice ( $self, $round, @jeapardy ) { |
|
14
|
|
|
|
|
22
|
|
|
14
|
|
|
|
|
16
|
|
|
14
|
|
|
|
|
31
|
|
|
14
|
|
|
|
|
25
|
|
100
|
14
|
|
|
|
|
47
|
my %roundvotes = $round->RawCount()->%*; |
101
|
14
|
|
|
|
|
27
|
my @eliminate = (); |
102
|
14
|
|
|
|
|
39
|
my $lowest = $round->CountVotes(); |
103
|
14
|
|
|
|
|
32
|
for my $j (@jeapardy) { |
104
|
47
|
100
|
|
|
|
98
|
$lowest = $roundvotes{$j} if $roundvotes{$j} < $lowest; |
105
|
|
|
|
|
|
|
} |
106
|
14
|
|
|
|
|
36
|
for my $j (@jeapardy) { |
107
|
47
|
100
|
|
|
|
88
|
if ( $roundvotes{$j} == $lowest ) { |
108
|
16
|
|
|
|
|
28
|
push @eliminate, $j; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} |
111
|
14
|
|
|
|
|
44
|
return @eliminate; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
0
|
|
|
0
|
|
0
|
sub _newmatrix ($self) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
115
|
0
|
|
|
|
|
0
|
return Vote::Count::Matrix->new( |
116
|
|
|
|
|
|
|
'BallotSet' => $self->BallotSet() ); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
9
|
|
|
9
|
|
12
|
sub _logstart( $self, $active ) { |
|
9
|
|
|
|
|
15
|
|
|
9
|
|
|
|
|
16
|
|
|
9
|
|
|
|
|
14
|
|
120
|
9
|
|
|
|
|
20
|
my $dropdescription = 'Elimination Rule is Applied to All Active Choices.'; |
121
|
9
|
100
|
|
|
|
6073
|
if( $self->DropStyle eq 'leastwins') { |
122
|
4
|
|
|
|
|
13
|
$dropdescription = |
123
|
|
|
|
|
|
|
'Elimination Rule is Applied to only Choices with the Fewest Wins.'; |
124
|
|
|
|
|
|
|
} |
125
|
9
|
|
|
|
|
31
|
my $rule = ''; |
126
|
9
|
100
|
|
|
|
267
|
if ( $self->DropRule() =~ m/(plurality|topcount)/ ) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
127
|
6
|
|
|
|
|
13
|
$rule = "Drop the Choice With the Lowest TopCount."; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
elsif ( $self->DropRule() eq 'approval' ) { |
130
|
1
|
|
|
|
|
3
|
$rule = "Drop the Choice With the Lowest Approval."; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
elsif ( $self->DropRule() eq 'boorda' ) { |
133
|
2
|
|
|
|
|
5
|
$rule = "Drop the Choice With the Lowest Borda Score."; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
elsif ( $self->DropRule() eq 'greatestloss' ) { |
136
|
0
|
|
|
|
|
0
|
$rule = "Drop the Choice With the Greatest Loss."; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
else { |
139
|
0
|
|
|
|
|
0
|
croak "undefined dropping rule $rule requested"; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
$self->logt( 'CONDORCET SEQUENTIAL DROPPING METHOD', |
142
|
|
|
|
|
|
|
'CHOICES:', |
143
|
9
|
|
|
|
|
21
|
join( ', ', (sort keys %{$active}) ) ); |
|
9
|
|
|
|
|
115
|
|
144
|
9
|
|
|
|
|
43
|
$self->logv( "Elimination Rule: $rule", $dropdescription ); |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
9
|
|
|
9
|
0
|
757
|
sub RunCondorcetDropping ( $self, $active = undef ) { |
|
9
|
|
|
|
|
22
|
|
|
9
|
|
|
|
|
19
|
|
|
9
|
|
|
|
|
18
|
|
148
|
9
|
50
|
|
|
|
48
|
unless ( defined $active ) { $active = $self->BallotSet->{'choices'} } |
|
9
|
|
|
|
|
254
|
|
149
|
9
|
|
|
|
|
22
|
my $roundctr = 0; |
150
|
9
|
|
|
|
|
16
|
my $maxround = scalar( keys %{$active} ); |
|
9
|
|
|
|
|
26
|
|
151
|
9
|
|
|
|
|
44
|
$self->_logstart( $active); |
152
|
|
|
|
|
|
|
DROPLOOP: |
153
|
9
|
|
|
|
|
18
|
until ( 0 ) { |
154
|
30
|
|
|
|
|
3521
|
$roundctr++; |
155
|
30
|
50
|
|
|
|
75
|
die "DROPLOOP infinite stopped at $roundctr" if $roundctr > $maxround; |
156
|
30
|
|
|
|
|
108
|
my $topcount = $self->TopCount( $active ); |
157
|
30
|
|
|
|
|
105
|
my $round = $self->GetRound( $active, $roundctr ); |
158
|
30
|
|
|
|
|
143
|
$self->logv( '---', "Round $roundctr TopCount", $topcount->RankTable() ); |
159
|
30
|
|
|
|
|
140
|
my $majority = $self->EvaluateTopCountMajority( $topcount ); |
160
|
30
|
100
|
|
|
|
75
|
if ( defined $majority->{'winner'} ) { |
161
|
6
|
|
|
|
|
118
|
return $majority->{'winner'}; |
162
|
|
|
|
|
|
|
} |
163
|
24
|
|
|
|
|
618
|
my $matrix = Vote::Count::Matrix->new( |
164
|
|
|
|
|
|
|
'BallotSet' => $self->BallotSet, |
165
|
|
|
|
|
|
|
'Active' => $active ); |
166
|
24
|
|
|
|
|
253
|
$self->logv( '---', "Round $roundctr Pairings", $matrix->MatrixTable() ); |
167
|
24
|
|
100
|
|
|
137
|
my $cw = $matrix->CondorcetWinner() || 0 ; |
168
|
24
|
100
|
|
|
|
61
|
if ( $cw ) { |
169
|
2
|
|
|
|
|
5
|
my $wstr = "* Winner $cw *"; |
170
|
2
|
|
|
|
|
5
|
my $rpt = length( $wstr) ; |
171
|
2
|
|
|
|
|
14
|
$self->logt( '*'x$rpt, $wstr, '*'x$rpt ); |
172
|
2
|
|
|
|
|
15
|
return $cw; |
173
|
|
|
|
|
|
|
} |
174
|
22
|
|
|
|
|
65
|
my $eliminated = $matrix->CondorcetLoser(); |
175
|
22
|
100
|
|
|
|
62
|
if( $eliminated->{'eliminations'}) { |
176
|
|
|
|
|
|
|
# tracking active between iterations of matrix. |
177
|
8
|
|
|
|
|
184
|
$active = $matrix->Active(); |
178
|
|
|
|
|
|
|
$self->logv( "Eliminated Condorcet Losers:", |
179
|
8
|
|
|
|
|
57
|
join( ', ', $eliminated->{'eliminated'}->@* )); |
180
|
|
|
|
|
|
|
# active changed, restart loop |
181
|
8
|
|
|
|
|
56
|
next DROPLOOP; |
182
|
|
|
|
|
|
|
} |
183
|
14
|
|
|
|
|
26
|
my @jeapardy = (); |
184
|
14
|
100
|
|
|
|
425
|
if( $self->DropStyle eq 'leastwins') { |
185
|
7
|
|
|
|
|
21
|
@jeapardy = $matrix->LeastWins(); |
186
|
7
|
|
|
|
|
15
|
} else { @jeapardy = keys %{$active} } |
|
7
|
|
|
|
|
24
|
|
187
|
14
|
|
|
|
|
51
|
for my $goodbye ( $self->DropChoice( $round, @jeapardy )) { |
188
|
16
|
|
|
|
|
30
|
delete $active->{ $goodbye }; |
189
|
16
|
|
|
|
|
57
|
$self->logv( "Elimminating $goodbye"); |
190
|
|
|
|
|
|
|
} |
191
|
14
|
|
|
|
|
42
|
my @remaining = keys $active->%* ; |
192
|
14
|
50
|
|
|
|
96
|
if ( @remaining == 0) { |
|
|
100
|
|
|
|
|
|
193
|
0
|
|
|
|
|
0
|
$self->logt( "All remaining Choices would be eliminated, Tie between @jeapardy"); |
194
|
0
|
|
|
|
|
0
|
return 'tie'; |
195
|
0
|
|
|
|
|
0
|
$self->{'tied'} = \@jeapardy; |
196
|
|
|
|
|
|
|
} elsif ( @remaining == 1) { |
197
|
1
|
|
|
|
|
3
|
my $winner = $remaining[0]; |
198
|
1
|
|
|
|
|
16
|
$self->logt( "Only 1 choice remains.", "** WINNER : $winner **"); |
199
|
1
|
|
|
|
|
8
|
return $winner; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
};#infinite DROPLOOP |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
1; |