Module simulation
Expand source code
from random import random
from math import sqrt, pow, fsum, fabs, ceil
import matplotlib.pyplot as plt
import argparse
import sys
from voting import *
#############
# FUNCTIONS #
#############
def distance(pointA, pointB):
"""
Computes the Euclidean distance between two points in a vector space of any dimension.
"""
return sqrt(fsum([pow(fabs(pointA[i]-pointB[i]), 2) for i in range (0, len(pointA))]))
def iniate_dict(number, dimension):
"""
Initialises a dictionary with integers between 0 and *number* for keys and lists of *dimension* random numbers between 0 and 1 for values.
"""
dict = {}
for i in range(0, number):
element = []
for j in range(0, dimension):
element.append(random())
dict[i] = element
return (dict)
def plot_grid(electors, candidates):
"""
Displays the position of voters and candidates **in the first two dimensions only** in the `img/positions.png` file.
"""
fig, ax = plt.subplots()
ax.scatter([electors[v][0] for v in electors], [electors[v][1] for v in electors],
s=10, c="black")
for c in candidates:
ax.scatter(candidates[c][0], candidates[c][1])
ax.annotate(str(c), (candidates[c][0], candidates[c][1]))
ax.grid(True)
fig.suptitle("elector and candidate positions")
plt.savefig("img/positions.png")
def progress_bar(count,total,size=100,full='#',empty='.',prefix=""):
"""
Displays a progress bar.
"""
x = int(size*count/total)
sys.stdout.write("\r" + prefix + '[' + full*x + empty*(size-x) + '] ' + str(count).rjust(len(str(total)),' ')+"/"+str(total))
if count==total:
sys.stdout.write("\n")
########
# MAIN #
########
def main():
if args.output != None:
output_string = ""
for i in range (0, args.repeat):
electors = iniate_dict(args.electors, args.dimension)
candidates = iniate_dict(args.candidates, args.dimension)
if not args.noplot:
plot_grid(electors, candidates)
distances = {elector: { candidate: distance(electors[elector], candidates[candidate]) for candidate in candidates} for elector in electors }
ranked_preferences = {elector: [candidate for candidate, distance in sorted(distances[elector].items(), key=lambda item: item[1])] for elector in electors}
if args.output == None:
print("plurality:\t\t", N_rounds(ranked_preferences, 1))
print("two round:\t\t", N_rounds(ranked_preferences, 2))
print("instant runoff:\t\t", N_rounds(ranked_preferences, args.candidates-1))
print("condorcet:\t\t", condorcet(ranked_preferences))
print("borda:\t\t\t", borda(ranked_preferences))
print("approval:\t\t", approval(distances, args.threshold))
print("majority judgement:\t", majority_judgement(distances, args.threshold))
else:
output_string += (str) (N_rounds(ranked_preferences, 1)) + ','
output_string += (str) (N_rounds(ranked_preferences, 1)) + ','
output_string += (str) (N_rounds(ranked_preferences, 2)) + ','
output_string += (str) (N_rounds(ranked_preferences, args.candidates-1)) + ','
output_string += (str) (condorcet(ranked_preferences)) + ','
output_string += (str) (borda(ranked_preferences)) + ','
output_string += (str) (approval(distances, args.threshold)) + ','
output_string += (str) (majority_judgement(distances, args.threshold)) + '\n'
if args.output != None:
f = open(args.output, "w", newline='\n')
f.write(output_string)
f.close()
###############
# VOTING TEST #
###############
def test(n):
"""
Computes the undecidability rate of the current method (to be modified in the source code).
"""
count = 0
display_rate = 40
for i in range (1, n+1):
electors = iniate_dict(args.electors, args.dimension)
candidates = iniate_dict(args.candidates, args.dimension)
distances = {elector: { candidate: distance(electors[elector], candidates[candidate]) for candidate in candidates} for elector in electors }
ranked_preferences = {elector: [candidate for candidate, distance in sorted(distances[elector].items(), key=lambda item: item[1])] for elector in electors}
if (condorcet(ranked_preferences) == None):
count+=1
if i % display_rate == 0:
progress_bar(i,n, size=30, prefix="rate : " + str(count*100/i).ljust(18,'0')+" ")
print(count*100/n)
if __name__ == '__main__':
##############
# PARAMETERS #
##############
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--version", action="version", version='1.0')
parser.add_argument("-d", "--dimension", type=int, default=2, help="number of dimensions to use")
parser.add_argument("-e", "--electors", type=int, default=50, help="number of electors for the simulation")
parser.add_argument("-c", "--candidates", type=int, default=10, help="number of candidates for the simulation")
parser.add_argument("-t", "--threshold", type=float, default=0.5, help="rejection threshold for scoring methods")
parser.add_argument("--noplot", action='store_true', help="creates the positions image")
parser.add_argument("-r", "--repeat", type=int, default=1, help="number of repetitions of the simulation")
parser.add_argument("-o", "--output", type=str, help="output file to write the results")
parser.add_argument("--test", type=int, help="number of times to test the method given in the test() function")
args = parser.parse_args()
if args.test != None:
test(args.test)
else:
main()
Functions
def distance(pointA, pointB)
-
Computes the Euclidean distance between two points in a vector space of any dimension.
Expand source code
def distance(pointA, pointB): """ Computes the Euclidean distance between two points in a vector space of any dimension. """ return sqrt(fsum([pow(fabs(pointA[i]-pointB[i]), 2) for i in range (0, len(pointA))]))
def iniate_dict(number, dimension)
-
Initialises a dictionary with integers between 0 and number for keys and lists of dimension random numbers between 0 and 1 for values.
Expand source code
def iniate_dict(number, dimension): """ Initialises a dictionary with integers between 0 and *number* for keys and lists of *dimension* random numbers between 0 and 1 for values. """ dict = {} for i in range(0, number): element = [] for j in range(0, dimension): element.append(random()) dict[i] = element return (dict)
def main()
-
Expand source code
def main(): if args.output != None: output_string = "" for i in range (0, args.repeat): electors = iniate_dict(args.electors, args.dimension) candidates = iniate_dict(args.candidates, args.dimension) if not args.noplot: plot_grid(electors, candidates) distances = {elector: { candidate: distance(electors[elector], candidates[candidate]) for candidate in candidates} for elector in electors } ranked_preferences = {elector: [candidate for candidate, distance in sorted(distances[elector].items(), key=lambda item: item[1])] for elector in electors} if args.output == None: print("plurality:\t\t", N_rounds(ranked_preferences, 1)) print("two round:\t\t", N_rounds(ranked_preferences, 2)) print("instant runoff:\t\t", N_rounds(ranked_preferences, args.candidates-1)) print("condorcet:\t\t", condorcet(ranked_preferences)) print("borda:\t\t\t", borda(ranked_preferences)) print("approval:\t\t", approval(distances, args.threshold)) print("majority judgement:\t", majority_judgement(distances, args.threshold)) else: output_string += (str) (N_rounds(ranked_preferences, 1)) + ',' output_string += (str) (N_rounds(ranked_preferences, 1)) + ',' output_string += (str) (N_rounds(ranked_preferences, 2)) + ',' output_string += (str) (N_rounds(ranked_preferences, args.candidates-1)) + ',' output_string += (str) (condorcet(ranked_preferences)) + ',' output_string += (str) (borda(ranked_preferences)) + ',' output_string += (str) (approval(distances, args.threshold)) + ',' output_string += (str) (majority_judgement(distances, args.threshold)) + '\n' if args.output != None: f = open(args.output, "w", newline='\n') f.write(output_string) f.close()
def plot_grid(electors, candidates)
-
Displays the position of voters and candidates in the first two dimensions only in the
img/positions.png
file.Expand source code
def plot_grid(electors, candidates): """ Displays the position of voters and candidates **in the first two dimensions only** in the `img/positions.png` file. """ fig, ax = plt.subplots() ax.scatter([electors[v][0] for v in electors], [electors[v][1] for v in electors], s=10, c="black") for c in candidates: ax.scatter(candidates[c][0], candidates[c][1]) ax.annotate(str(c), (candidates[c][0], candidates[c][1])) ax.grid(True) fig.suptitle("elector and candidate positions") plt.savefig("img/positions.png")
def progress_bar(count, total, size=100, full='#', empty='.', prefix='')
-
Displays a progress bar.
Expand source code
def progress_bar(count,total,size=100,full='#',empty='.',prefix=""): """ Displays a progress bar. """ x = int(size*count/total) sys.stdout.write("\r" + prefix + '[' + full*x + empty*(size-x) + '] ' + str(count).rjust(len(str(total)),' ')+"/"+str(total)) if count==total: sys.stdout.write("\n")
def random()
-
random() -> x in the interval [0, 1).
def test(n)
-
Computes the undecidability rate of the current method (to be modified in the source code).
Expand source code
def test(n): """ Computes the undecidability rate of the current method (to be modified in the source code). """ count = 0 display_rate = 40 for i in range (1, n+1): electors = iniate_dict(args.electors, args.dimension) candidates = iniate_dict(args.candidates, args.dimension) distances = {elector: { candidate: distance(electors[elector], candidates[candidate]) for candidate in candidates} for elector in electors } ranked_preferences = {elector: [candidate for candidate, distance in sorted(distances[elector].items(), key=lambda item: item[1])] for elector in electors} if (condorcet(ranked_preferences) == None): count+=1 if i % display_rate == 0: progress_bar(i,n, size=30, prefix="rate : " + str(count*100/i).ljust(18,'0')+" ") print(count*100/n)