import math
import numpy as np
from mapof.elections.distances import ilp_other
from mapof.elections.features.register import register_ordinal_election_feature
from mapof.elections.other import ordinal_rules as win
[docs]
@register_ordinal_election_feature('highest_borda_score')
def highest_borda_score(election) -> dict:
"""
Computes the highest Borda score of a given election.
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': highest Borda score
"""
if election.is_pseudo:
return {'value': None}
n = election.num_voters
m = election.num_candidates
scores = [0 for _ in range(m)]
for i in range(n):
for j in range(m):
scores[int(election.votes[i][j])] += m - j - 1
return {'value': max(scores)}
[docs]
@register_ordinal_election_feature('highest_plurality_score')
def highest_plurality_score(election) -> dict:
"""
Computes the highest Plurality score of a given election.
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': highest Plurality score
"""
if election.is_pseudo:
return {'value': None}
first_pos = election.get_frequency_matrix()[0]
return {'value': max(first_pos)}
[docs]
@register_ordinal_election_feature('highest_copeland_score')
def highest_copeland_score(election) -> dict:
"""
Computes the highest Copeland score of a given election.
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': highest Copeland score
"""
if election.is_pseudo:
return {'value': None}
election.compute_potes()
scores = np.zeros([election.num_candidates])
for i in range(election.num_candidates):
for j in range(i + 1, election.num_candidates):
result = 0
for k in range(election.num_voters):
if election.potes[k][i] < election.potes[k][j]:
result += 1
if result > election.num_voters / 2:
scores[i] += 1
elif result < election.num_voters / 2:
scores[j] += 1
else:
scores[i] += 0.5
scores[j] += 0.5
return {'value': max(scores)}
[docs]
@register_ordinal_election_feature('lowest_dodgson_score')
def lowest_dodgson_score(election):
"""
Computes the lowest Dodgson score of a given election
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': lowest Dodgson score
"""
if election.is_pseudo:
return {'value': None}
min_score = math.inf
for target_id in range(election.num_candidates):
# PREPARE N
unique_potes, N = _potes_to_unique_potes(election.get_potes())
e = np.zeros([len(N), election.num_candidates,
election.num_candidates])
# PREPARE e
for i, p in enumerate(unique_potes):
for j in range(election.num_candidates):
for k in range(election.num_candidates):
if p[target_id] <= p[k] + j:
e[i][j][k] = 1
# PREPARE D
D = [0 for _ in range(election.num_candidates)]
threshold = math.ceil((election.num_voters + 1) / 2.)
for k in range(election.num_candidates):
diff = 0
for i, p in enumerate(unique_potes):
if p[target_id] < p[k]:
diff += N[i]
if diff >= threshold:
D[k] = 0
else:
D[k] = threshold - diff
D[target_id] = 0 # always winning
score = ilp_other.solve_lp_file_dodgson_score(N=N, e=e, D=D)
if score < min_score:
min_score = score
return {'value': min_score}
[docs]
@register_ordinal_election_feature('highest_cc_score')
def highest_cc_score(election, committee_size: int = 1):
"""
Computes the highest CC score of a given election.
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': highest CC score
"""
if election.is_pseudo:
return {'value': None, 'dissat': None}
winners = win.compute_standard_voting_rule(election=election,
committee_size=committee_size,
type='borda_owa', name='cc')
return {'value': get_cc_score(election, winners), 'dissat': get_cc_dissat(election, winners)}
[docs]
@register_ordinal_election_feature('highest_hb_score')
def highest_hb_score(election, committee_size: int = 1):
"""
Computes the highest HB score of a given election.
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': highest HB score
"""
if election.is_pseudo:
return {'value': None, 'dissat': None}
winners = win.compute_standard_voting_rule(election=election,
committee_size=committee_size,
type='borda_owa', name='hb')
return {'value': get_hb_score(election, winners), 'dissat': get_hb_dissat(election, winners)}
[docs]
@register_ordinal_election_feature('highest_pav_score')
def highest_pav_score(election, committee_size: int = 1):
"""
Computes the highest PAV score of a given election.
Parameters
----------
election : OrdinalElection
Returns
-------
dict
'value': highest PAV score
"""
if election.is_pseudo:
return {'value': None, 'dissat': None}
winners = win.compute_standard_voting_rule(election=election,
committee_size=committee_size,
type='bloc_owa',
name='hb')
return {'value': get_pav_score(election, winners), 'dissat': get_pav_dissat(election, winners)}
[docs]
@register_ordinal_election_feature('borda_spread')
def borda_spread(election) -> int:
""" Compute the difference between the highest and the lowest Borda score """
c = election.num_candidates
frequency_matrix = election.get_frequency_matrix()
borda = [sum([frequency_matrix[i][pos] * (c - pos - 1) for pos in range(c)])
for i in range(c)]
return (max(borda) - min(borda)) * election.num_voters
# HELPER FUNCTIONS
def _potes_to_unique_potes(potes):
""" Remove repetitions from potes (positional votes) """
unique_potes = []
n = []
for pote in potes:
flag_new = True
for i, p in enumerate(unique_potes):
if list(pote) == list(p):
n[i] += 1
flag_new = False
if flag_new:
unique_potes.append(pote)
n.append(1)
return unique_potes, n
# GET SCORE
def get_score(election, winners, rule) -> float:
if rule == 'cc':
return get_cc_score(election, winners)
elif rule == 'hb':
return get_hb_score(election, winners)
elif rule == 'pav':
return get_pav_score(election, winners)
def get_cc_score(election, winners) -> float:
num_voters = election.num_voters
num_candidates = election.num_candidates
votes = election.votes
score = 0
for i in range(num_voters):
for j in range(num_candidates):
if votes[i][j] in winners:
score += num_candidates - j - 1
break
return score
def get_hb_score(election, winners) -> float:
num_voters = election.num_voters
num_candidates = election.num_candidates
votes = election.votes
score = 0
for i in range(num_voters):
ctr = 1.
for j in range(num_candidates):
if votes[i][j] in winners:
score += (1. / ctr) * (num_candidates - j - 1)
ctr += 1
return score
def get_pav_score(election, winners) -> float:
num_voters = election.num_voters
num_candidates = election.num_candidates
votes = election.votes
score = 0
vector = [0.] * num_candidates
for i in range(len(winners)):
vector[i] = 1.
for i in range(num_voters):
ctr = 1.
for j in range(num_candidates):
if votes[i][j] in winners:
score += (1. / ctr) * vector[j]
ctr += 1
return score
# GET DISSAT
def get_dissat(election, winners, rule) -> float:
if rule == 'cc':
return get_cc_dissat(election, winners)
elif rule == 'hb':
return get_hb_dissat(election, winners)
elif rule == 'pav':
return get_pav_dissat(election, winners)
def get_cc_dissat(election, winners) -> float:
num_voters = election.num_voters
num_candidates = election.num_candidates
dissat = 0
for i in range(num_voters):
for j in range(num_candidates):
if election.votes[i][j] in winners:
dissat += j
break
return dissat
def get_hb_dissat(election, winners) -> float:
num_voters = election.num_voters
num_candidates = election.num_candidates
dissat = 0
for i in range(num_voters):
ctr = 1.
for j in range(num_candidates):
if election.votes[i][j] in winners:
dissat += (1. / ctr) * (j)
ctr += 1
return dissat
def get_pav_dissat(election, winners) -> float:
num_voters = election.num_voters
num_candidates = election.num_candidates
dissat = 0
vector = [0. for _ in range(num_candidates)]
for i in range(len(winners), num_candidates):
vector[i] = 1.
for i in range(num_voters):
ctr = 1.
for j in range(num_candidates):
if election.votes[i][j] in winners:
dissat += ((1. / ctr) * vector[j])
ctr += 1
return dissat