Skip to content

Commit

Permalink
Reorganize multiwinner directory structure; extract out PR measures.
Browse files Browse the repository at this point in the history
  • Loading branch information
kristomu committed Sep 21, 2024
1 parent 3169596 commit 49bb554
Show file tree
Hide file tree
Showing 80 changed files with 346 additions and 275 deletions.
38 changes: 20 additions & 18 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -189,25 +189,27 @@ add_library(qe_singlewinner_methods
target_link_libraries(qe_singlewinner_methods ${GLPK_LIBRARIES})

add_library(qe_multiwinner_methods
src/multiwinner/auction.cc
src/multiwinner/exhaustive/scored_method.cc
src/multiwinner/helper/errors.cc
src/multiwinner/compat_qbuck.cc
src/multiwinner/dhwl.cc
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
src/multiwinner/qpq.cc
src/multiwinner/qrange_stv.cc
src/multiwinner/randballots.cc
src/multiwinner/range_stv.cc
src/multiwinner/rusty/auxiliary/dsc.cc
src/multiwinner/shuntsstv.cc
src/multiwinner/stv.cc
src/multiwinner/methods/auction.cc
src/multiwinner/methods/exhaustive/scored_method.cc
src/multiwinner/methods/compat_qbuck.cc
src/multiwinner/methods/dhwl.cc
src/multiwinner/methods/dhwl_mat.cc
src/multiwinner/methods/meek_stv.cc
src/multiwinner/methods/methods.cc
src/multiwinner/methods/prbucklin.cc
src/multiwinner/methods/psc.cc
src/multiwinner/methods/quotas.cc
src/multiwinner/methods/qbuck.cc
src/multiwinner/methods/qpq.cc
src/multiwinner/methods/qrange_stv.cc
src/multiwinner/methods/randballots.cc
src/multiwinner/methods/range_stv.cc
src/multiwinner/methods/rusty/auxiliary/dsc.cc
src/multiwinner/methods/shuntsstv.cc
src/multiwinner/methods/stv.cc
src/multiwinner/pr_measures/clustering.cc
src/multiwinner/pr_measures/normal_fit.cc
src/stats/multiwinner/convex_hull.cc
src/stats/multiwinner/mwstats.cc)

Expand Down
226 changes: 10 additions & 216 deletions src/main/multiwinner.cc
Original file line number Diff line number Diff line change
@@ -1,226 +1,15 @@
// More multiwinner hacks/clustering tests.

#include "multiwinner/methods.h"
#include "multiwinner/qpq.h"

#include "multiwinner/methods/all.h"
#include "generator/spatial/all.h"

#include "random/random.h"
#include "tools/tools.h"

#include "multiwinner/helper/errors.h"

// TODO: Make error type parametric, or just inherit the function/
// template it or something.

class proportionality_measure {
public:
virtual void prepare(const positions_election & p_e) = 0;
virtual double get_error(const std::list<size_t> & outcome) = 0;
};

// Clustering-based proportionality measure

class cluster_proportionality : public proportionality_measure {
private:
size_t num_clusters;
std::vector<std::vector<double> > clusters;
std::vector<size_t> cluster_for_candidate;

// Fraction of voters covered by each cluster.
std::vector<double> cluster_voter_proportions;

// ... and fractions of winning candidates.
std::vector<double> cluster_winner_proportions;

size_t get_optimum_cluster(const std::vector<double> & point,
const std::vector<std::vector<double> > & centers) const;

public:
void set_num_clusters(size_t num_clusters_in) {
num_clusters = num_clusters_in;
clusters.resize(num_clusters);
cluster_voter_proportions.resize(num_clusters);
cluster_winner_proportions.resize(num_clusters);

}
cluster_proportionality(size_t num_clusters_in) {
set_num_clusters(num_clusters_in);
}

void prepare(const positions_election & p_e);
double get_error(const std::list<size_t> & outcome);
};

size_t cluster_proportionality::get_optimum_cluster(
const std::vector<double> & point,
const std::vector<std::vector<double> > & centers) const {

size_t record_cluster = 0;
double record_distance = -1;
bool set_record = false;

for (size_t cluster_idx = 0; cluster_idx < centers.size();
++cluster_idx) {

double current_distance = lp_distance(2, point,
centers[cluster_idx]);

if (!set_record || current_distance < record_distance) {
set_record = true;
record_cluster = cluster_idx;
record_distance = current_distance;
}
}

return record_cluster;
}

void cluster_proportionality::prepare(const positions_election & p_e) {

size_t num_voters = p_e.voters_pos.size(),
num_candidates = p_e.candidates_pos.size();

// Pick the k first voter coordinates as the cluster centers.
if (num_voters < num_clusters) {
throw std::invalid_argument("Clustering proportionality:"
" too few voters - need to be more than the number of clusters.");
}

clusters = std::vector<std::vector<double> >(
p_e.voters_pos.begin(), p_e.voters_pos.begin()+num_clusters);

std::fill(cluster_voter_proportions.begin(),
cluster_voter_proportions.end(), 0);

for (size_t voter = 0; voter < num_voters; ++voter) {
size_t opt = get_optimum_cluster(
p_e.voters_pos[voter], clusters);
cluster_voter_proportions[opt] += 1.0/num_voters;
}

cluster_for_candidate = std::vector<size_t>(num_candidates, 0);

for (size_t cand = 0; cand < num_candidates; ++cand) {
cluster_for_candidate[cand] = get_optimum_cluster(
p_e.candidates_pos[cand], clusters);
}
}

double cluster_proportionality::get_error(
const std::list<size_t> & outcome) {

size_t num_seats = outcome.size();

std::fill(cluster_winner_proportions.begin(),
cluster_winner_proportions.end(), 0);

for (size_t winner: outcome) {
cluster_winner_proportions[cluster_for_candidate[winner]]++;
}

for (double & cluster_prop: cluster_winner_proportions) {
cluster_prop /= (double)num_seats;
}

return sli(cluster_winner_proportions,
cluster_voter_proportions);
}

// Check the error between a normal distribution fitted to the winners
// and the normal reference distribution.

class normal_proportionality : public proportionality_measure {
private:
gaussian_generator reference;
std::vector<std::vector<double> > candidate_positions;
rng rnd;

public:
// TODO, use a different seed
normal_proportionality(gaussian_generator ref_in) : rnd(1) {
reference = ref_in;
}

void prepare(const positions_election & p_e);
double get_error(const std::list<size_t> & outcome);
};

void normal_proportionality::prepare(const positions_election & p_e) {
candidate_positions = p_e.candidates_pos;
}

double normal_proportionality::get_error(
const std::list<size_t> & outcome) {

size_t num_seats = outcome.size();

// Standard deviation is undefined if we have only one winner.
if (num_seats < 2) {
throw std::invalid_argument("normal_proportionality: "
"Need at least two winners");
}

size_t dim, dims = reference.get_num_dimensions();

std::vector<double> mu_estimate(dims, 0), sigma_estimate(dims, 0);

// Get means first.
for (size_t winner: outcome) {
for (dim = 0; dim < dims; ++dim) {
mu_estimate[dim] += candidate_positions[winner][dim];
}
for (dim = 0; dim < dims; ++dim) {
mu_estimate[dim] /= (double)num_seats;
}
}

// Get standard deviations.
for (size_t winner: outcome) {
for (dim = 0; dim < dims; ++dim) {
sigma_estimate[dim] += square(
candidate_positions[winner][dim] - mu_estimate[dim]);
}
for (dim = 0; dim < dims; ++dim) {
// Setting this to 1 makes it large-biased, because a bunch of
// candidates clustered around the center gives a more accurate mean
// than if they're dispersed.
sigma_estimate[dim] = sqrt(sigma_estimate[dim] / (double)num_seats);
}
}

/*std::cout << "mu: ";
std::copy(mu_estimate.begin(), mu_estimate.end(), std::ostream_iterator<double>(std::cout, " "));
std::cout << "\n";
std::cout << "sigma: ";
std::copy(sigma_estimate.begin(), sigma_estimate.end(),
std::ostream_iterator<double>(std::cout, " "));
std::cout << std::endl;*/

gaussian_generator estimated;
estimated.set_params(dims, false);
estimated.set_center(mu_estimate);
estimated.set_dispersion(sigma_estimate);

// Pick random points from the reference distribution and
// get the pdf from both. Use Sainte-Laguë index-like measure.

// TODO: KL divergence? Closed form integral instead of this
// slow Monte Carlo stuff?

size_t iters = 500;
double error_sum = 0;

for (size_t i = 0; i < iters; ++i) {
std::vector<double> q = reference.rnd_vector(dims, rnd);

double observed = estimated.pdf(q),
expected = reference.pdf(q);

error_sum += square(observed-expected)/expected;
}

return error_sum / (double)iters;
}
#include "multiwinner/pr_measures/clustering.h"
#include "multiwinner/pr_measures/normal_fit.h"

// normsum, etc.

Expand All @@ -236,6 +25,8 @@ int main() {

size_t num_clusters = 2; // say

size_t maxiters = 5000;

std::cout << gauss.pdf(std::vector<double>(5, 0.1)) << "\n";

cluster_proportionality test(num_clusters);
Expand All @@ -244,7 +35,9 @@ int main() {
for (double delta = 0.1; delta <= 1; delta += 0.1) {

double error = 0;
for (int i = 0; i < 5000; ++i) {
for (int i = 0; i < maxiters; ++i) {

std::cerr << i << "/" << maxiters << " \r" << std::flush;

positions_election p_e = gauss.generate_election_result(
num_voters, num_candidates, false, rnd);
Expand All @@ -260,6 +53,7 @@ int main() {
error += ntest.get_error(qpq_council);
}

std::cerr << "\n";
std::cout << delta << "\t" << error << std::endl;
}
}
60 changes: 30 additions & 30 deletions src/main/multiwinner_spatial.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,36 @@

#include "multiwinner/helper/errors.cc"

#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"
#include "multiwinner/exhaustive/quota_helper.h"

#include "multiwinner/rusty/fc_kemeny.h"
#include "multiwinner/rusty/mono_webst_640.h"
#include "multiwinner/rusty/mono_webst_c37.h"
#include "multiwinner/rusty/mono_webst_f03.h"
#include "multiwinner/rusty/mw_kemeny2_34e.h"
#include "multiwinner/rusty/mw_kemeny_db0.h"

#include "multiwinner/auction.h"
#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"
#include "multiwinner/shuntsstv.h"
#include "multiwinner/stv.h"

#include "multiwinner/randballots.h"
#include "multiwinner/qpq.h"
#include "multiwinner/qrange_stv.h"
#include "multiwinner/range_stv.h"
#include "multiwinner/methods/methods.h"
#include "multiwinner/methods/exhaustive/birational.h"
#include "multiwinner/methods/exhaustive/harmonic.h"
#include "multiwinner/methods/exhaustive/isoelastic.h"
#include "multiwinner/methods/exhaustive/lpv.h"
#include "multiwinner/methods/exhaustive/psi.h"
#include "multiwinner/methods/exhaustive/quota_helper.h"

#include "multiwinner/methods/rusty/fc_kemeny.h"
#include "multiwinner/methods/rusty/mono_webst_640.h"
#include "multiwinner/methods/rusty/mono_webst_c37.h"
#include "multiwinner/methods/rusty/mono_webst_f03.h"
#include "multiwinner/methods/rusty/mw_kemeny2_34e.h"
#include "multiwinner/methods/rusty/mw_kemeny_db0.h"

#include "multiwinner/methods/auction.h"
#include "multiwinner/methods/compat_qbuck.h"
#include "multiwinner/methods/dhwl.h"
#include "multiwinner/methods/meek_stv.h"
#include "multiwinner/methods/prbucklin.h"
#include "multiwinner/methods/psc.h"
#include "multiwinner/methods/qbuck.h"
#include "multiwinner/methods/set_webster.h"
#include "multiwinner/methods/shuntsstv.h"
#include "multiwinner/methods/stv.h"

#include "multiwinner/methods/randballots.h"
#include "multiwinner/methods/qpq.h"
#include "multiwinner/methods/qrange_stv.h"
#include "multiwinner/methods/range_stv.h"

#include "hack/msvc_random.h"

Expand Down
18 changes: 18 additions & 0 deletions src/multiwinner/methods/all.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "auction.h"
#include "compat_qbuck.h"
#include "cpo_stv.h"
#include "dhwl.h"
#include "dhwl_mat.h"
#include "meek_stv.h"
#include "methods.h"
#include "prbucklin.h"
#include "psc.h"
#include "qbuck.h"
#include "qpq.h"
#include "qrange_stv.h"
#include "quotas.h"
#include "randballots.h"
#include "range_stv.h"
#include "set_webster.h"
#include "shuntsstv.h"
#include "stv.h"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 49bb554

Please sign in to comment.