Skip to content

Commit

Permalink
Port "Set PR Bucklin" from an earlier program.
Browse files Browse the repository at this point in the history
This seems to be another high (utilitarian) VSE, low proportionality
method, though somewhat more proportional than PSC-CLE.

Also add multiplicative reweighting methods to the default test list
in main/multiwinner.cc
  • Loading branch information
kristomu committed Sep 2, 2024
1 parent 5748768 commit 9ddfbac
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ add_library(qe_multiwinner_methods
src/multiwinner/dhwl_mat.cc
src/multiwinner/meek_stv.cc
src/multiwinner/methods.cc
src/multiwinner/prbucklin.cc
src/multiwinner/psc.cc
src/multiwinner/quotas.cc
src/multiwinner/qbuck.cc
Expand Down
6 changes: 6 additions & 0 deletions src/main/multiwinner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "multiwinner/compat_qbuck.h"
#include "multiwinner/dhwl.h"
#include "multiwinner/meek_stv.h"
#include "multiwinner/prbucklin.h"
#include "multiwinner/psc.h"
#include "multiwinner/qbuck.h"
#include "multiwinner/set_webster.h"
Expand Down Expand Up @@ -567,6 +568,9 @@ std::vector<multiwinner_stats> get_multiwinner_methods() {
e_methods.push_back(multiwinner_stats(
std::make_shared<addt_ballot_reweighting>(
positional_methods[counter])));
e_methods.push_back(multiwinner_stats(
std::make_shared<mult_ballot_reweighting>(
positional_methods[counter])));
}

// Maybe: IRV-SNTV
Expand Down Expand Up @@ -601,6 +605,8 @@ std::vector<multiwinner_stats> get_multiwinner_methods() {
e_methods.push_back(multiwinner_stats(std::make_shared<PSC>(0.5)));
e_methods.push_back(multiwinner_stats(std::make_shared<PSC>(0)));

e_methods.push_back(multiwinner_stats(std::make_shared<set_pr_bucklin>()));

// Not as good as STV. "Plurality PSC".
e_methods.push_back(multiwinner_stats(
std::make_shared<coalition_elimination>(positional_methods[0])));
Expand Down
112 changes: 112 additions & 0 deletions src/multiwinner/prbucklin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include "prbucklin.h"

std::list<size_t> set_pr_bucklin::count(
const std::vector<std::vector<int> > & ballots,
const std::vector<double> & weights, size_t numcands, size_t seats) const {

std::vector<double> score_so_far(numcands, 0);
std::vector<double> adjust(numcands, 0);
std::vector<bool> selected(numcands, false);
size_t num_selected = 0;

std::list<size_t> council;

size_t sum_weights = 0, counter, sec;

for (counter = 0; counter < weights.size(); ++counter) {
sum_weights += weights[counter];
}

double quota = sum_weights / (double)(seats+1);

for (size_t cur_round = 0; cur_round < numcands
&& num_selected < seats; ++cur_round) {

for (sec = 0; sec < ballots.size(); ++sec) {
if (ballots[sec].size() <= cur_round) {
continue;
}

score_so_far[ballots[sec][cur_round]] += weights[sec];
}

std::vector<std::pair<double, int> > rank(numcands);

for (sec = 0; sec < numcands; ++sec)
if (!selected[sec]) {
// Pushing
rank.push_back(std::pair<double, int>(
score_so_far[sec],
sec));
} else {
// Opt variant: push score_so_far[sec] - adjust[sec].
rank.push_back(std::pair<double, int>(
score_so_far[sec] - adjust[sec], sec));
}

bool electable = true;

while (num_selected < seats && electable) {

sort(rank.rbegin(), rank.rend()); // tiebreak?

electable = (rank[cur_round].first >=
quota);// && !selected[rank[cur_round].second]);

if (!electable) {
continue;
}

// pick greatest unelected candidate
bool done = false;
size_t necount = 0;
for (sec = 0; necount <= cur_round && !done && sec < rank.size(); ++sec) {

assert(rank[sec].first >= 0);
if (rank[sec].first < quota) {
continue;
}

if (selected[rank[sec].second]) {
continue;
}

council.push_back(rank[sec].second);
selected[rank[sec].second] = true;
rank[sec].first -= quota;
adjust[rank[sec].second] += quota;
done = true;
++num_selected;
++necount;
}
electable = necount > 0;
}
}

assert(council.size() == seats);

return (council);
}

std::list<size_t> set_pr_bucklin::get_council(size_t council_size,
size_t num_candidates,
const election_t & ballots) const {

// Doesn't handle equal rank, etc.

std::vector<double> weights;
std::vector<std::vector<int> > support;

for (election_t::const_iterator pos = ballots.begin(); pos !=
ballots.end(); ++pos) {
weights.push_back(pos->get_weight());
std::vector<int> this_support;
for (ordering::const_iterator spos = pos->contents.begin();
spos != pos->contents.end(); ++spos) {
this_support.push_back(spos->get_candidate_num());
}
support.push_back(this_support);
}

return (count(support, weights, num_candidates, council_size));
}
24 changes: 24 additions & 0 deletions src/multiwinner/prbucklin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>

#include <assert.h>

#include "methods.h"

class set_pr_bucklin : public multiwinner_method {
private:
std::list<size_t> count(const std::vector<std::vector<int> > & ballots,
const std::vector<double> & weights, size_t numcands,
size_t seats) const;

public:
std::list<size_t> get_council(size_t council_size, size_t num_candidates,
const election_t & ballots) const;

std::string name() const {
return ("Set PR Bucklin");
}
};

0 comments on commit 9ddfbac

Please sign in to comment.