Skip to content

Commit

Permalink
Multiwinner: Add Warren's Psi (digamma) voting method.
Browse files Browse the repository at this point in the history
  • Loading branch information
kristomu committed Sep 1, 2024
1 parent 333b48c commit 182d211
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/main/multiwinner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "multiwinner/exhaustive/birational.h"
#include "multiwinner/exhaustive/isoelastic.h"
#include "multiwinner/exhaustive/lpv.h"
#include "multiwinner/exhaustive/psi.h"

#include "multiwinner/auction.h"
#include "multiwinner/compat_qbuck.h"
Expand Down Expand Up @@ -631,6 +632,10 @@ std::vector<multiwinner_stats> get_multiwinner_methods() {
e_methods.push_back(multiwinner_stats(std::make_shared<log_penalty>()));
e_methods.push_back(multiwinner_stats(std::make_shared<isoelastic>()));

e_methods.push_back(multiwinner_stats(std::make_shared<psi_voting>(0)));
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)));

// 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
105 changes: 105 additions & 0 deletions src/multiwinner/exhaustive/psi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "scored_method.h"
#include "tools/tools.h"

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

double delta;

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

bool maximize() const {
return true;
}

psi_voting_eval() {
delta = 0.5;
}

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

delta = delta_in;
}
};

// Lightly adapted from
// https://github.com/lteacy/decBRL-cpp/blob/master/src/polygamma/digamma.cpp

double digamma(double x) {
double c = 8.5;
double d1 = -0.5772156649;
double r;
double s = 0.00001;
double s3 = 0.08333333333;
double s4 = 0.0083333333333;
double s5 = 0.003968253968;
double value;
double y;

// Check the input.

if (x <= 0.0) {
throw std::invalid_argument("digamma: x < 0 is not supported");
}

// Initialize.

y = x;
value = 0.0;

// Use approximation if argument <= S.

if (y <= s) {
value = d1 - 1.0 / y;
return value;
}

// Reduce to DIGAMA(X + N) where (X + N) >= C.

while (y < c) {
value = value - 1.0 / y;
y = y + 1.0;
}

// Use Stirling's (actually de Moivre's) expansion if argument > C.

r = 1.0 / y;
value = value + log(y) - 0.5 * r;
r = r * r;
value = value - r * (s3 - r * (s4 - r * s5));

return value;
}

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

// Sum over voters v: digamma( delta +
// sum over elected candidates c: v's score of c)

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

double norm_rating_sum = 0;

for (auto pos = start; pos != end; ++pos) {
norm_rating_sum += this_ballot.get_norm_score(*pos);
}

return digamma(delta + norm_rating_sum) * this_ballot.weight;
}

typedef exhaustive_method_runner<psi_voting_eval> psi_voting;
1 change: 1 addition & 0 deletions src/singlewinner/sets/unsuccessful/all.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "resistant_winner.h"

0 comments on commit 182d211

Please sign in to comment.