Skip to content

Commit

Permalink
Multiwinner: Add Warren's Harmonic voting method.
Browse files Browse the repository at this point in the history
  • Loading branch information
kristomu committed Sep 2, 2024
1 parent 32ab1f4 commit 5748768
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/main/multiwinner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "multiwinner/methods.h"
#include "multiwinner/exhaustive/birational.h"
#include "multiwinner/exhaustive/harmonic.h"
#include "multiwinner/exhaustive/isoelastic.h"
#include "multiwinner/exhaustive/lpv.h"
#include "multiwinner/exhaustive/psi.h"
Expand Down Expand Up @@ -643,6 +644,14 @@ std::vector<multiwinner_stats> get_multiwinner_methods() {
e_methods.push_back(multiwinner_stats(std::make_shared<psi_voting>(0.5)));
e_methods.push_back(multiwinner_stats(std::make_shared<psi_voting>(1)));

e_methods.push_back(multiwinner_stats(std::make_shared<harmonic_voting>
(0)));
e_methods.push_back(multiwinner_stats(std::make_shared<harmonic_voting>
(0.5)));
e_methods.push_back(multiwinner_stats(std::make_shared<harmonic_voting>
(1)));


// It's possible to parameterize, but this way of passing parameters is
// not very elegant.
e_methods.push_back(multiwinner_stats(std::make_shared<isoelastic>(
Expand Down
76 changes: 76 additions & 0 deletions src/multiwinner/exhaustive/harmonic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "scored_method.h"
#include "tools/tools.h"

class harmonic_voting_eval : public scored_method {
private:
double evaluate(combo::it & start, combo::it & end,
const scored_ballot & this_ballot);

double delta;

std::vector<double> elected_ratings;

public:
std::string name() const {
if (delta == 0.5) {
return "Cardinal: Harmonic (Sainte-Laguë)";
}
if (delta == 1) {
return "Cardinal: Harmonic (D'Hondt)";
}
return "Cardinal: Harmonic (delta = " + dtos(delta) + ")";
}

bool maximize() const {
return true;
}

harmonic_voting_eval() {
delta = 0.5;
}

harmonic_voting_eval(double delta_in) {
if (delta_in < 0) {
throw std::invalid_argument("Harmonic voting: "
"delta can't be negative");
}

delta = delta_in;
}
};


double harmonic_voting_eval::evaluate(combo::it & start, combo::it & end,
const scored_ballot & this_ballot) {

// Sort the ratings given to the candidates proposed for election
// by this voter in descending order. Then the quality contributed
// to the proposed seat assignment by the voter is

// sum i=1..|S| : (ith greatest rating of candidate
// for someone to be elected)/ (i-1 + delta).

// From https://rangevoting.org/QualityMulti.html

size_t council_size = end - start;

elected_ratings.resize(council_size);

double quality = 0;
size_t idx = 0;

for (auto pos = start; pos != end; ++pos) {
elected_ratings[idx++] = this_ballot.get_norm_score(*pos);
}

std::sort(elected_ratings.begin(), elected_ratings.begin()+idx,
std::greater<double>());

for (size_t i = 0; i < council_size; ++i) {
quality += elected_ratings[i] / (i + delta);
}

return quality;
}

typedef exhaustive_method_runner<harmonic_voting_eval> harmonic_voting;

0 comments on commit 5748768

Please sign in to comment.