Skip to content

Commit

Permalink
formatting, text
Browse files Browse the repository at this point in the history
  • Loading branch information
martinlackner committed Feb 16, 2022
1 parent 0a1a742 commit 03673bf
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The following apportionment methods are implemented:
- Adams
* the quota method [1]

This module supports 3.7+.
This module supports Python 3.7+.

## How-to

Expand Down
20 changes: 20 additions & 0 deletions apportionment/examples/quota-ties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import apportionment.methods as app

"""
Dominik's remark:
It is actually not without loss of generality to focus just on ties that still appear in the end.
Here is an example: votes = [720, 720, 120, 120], house size h = 8. Then the quota method selects
exactly the following: 3 seats go to each of the big parties, and then choose 1 big party and 1
small party and give those a seat each. This last structure can't be captured by ties just at the
end. (In contrast, for divisor methods, the ties are always of the form "assign necessary seats
(say there are t of them), and then choose an arbitrary subset of size h - t from a specified
set S of parties".)
"""

votes = [720, 720, 120, 120]
seats = 8

print("votes: ", votes)
print(seats, "seats")

result = app.compute("quota", votes, seats, verbose=True)
65 changes: 50 additions & 15 deletions apportionment/methods.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Apportionment methods
"""
Apportionment methods
"""

from fractions import Fraction
import math
Expand Down Expand Up @@ -29,13 +31,15 @@ def compute(
parties=string.ascii_letters,
threshold=None,
tiesallowed=True,
verbose=True
verbose=True,
):
filtered_votes = apply_threshold(votes, threshold)
if method == "quota":
return quota(filtered_votes, seats, fractions, parties, tiesallowed, verbose)
elif method in ["lrm", "hamilton", "largest_remainder"]:
return largest_remainder(filtered_votes, seats, fractions, parties, tiesallowed, verbose)
return largest_remainder(
filtered_votes, seats, fractions, parties, tiesallowed, verbose
)
elif method in [
"dhondt",
"jefferson",
Expand All @@ -52,7 +56,9 @@ def compute(
"majorfractions",
"greatestdivisors",
]:
return divisor(filtered_votes, seats, method, fractions, parties, tiesallowed, verbose)
return divisor(
filtered_votes, seats, method, fractions, parties, tiesallowed, verbose
)
else:
raise NotImplementedError("apportionment method " + method + " not known")

Expand Down Expand Up @@ -117,15 +123,22 @@ def within_quota(votes, representatives, parties=string.ascii_letters, verbose=T

# Largest remainder method (Hamilton method)
def largest_remainder(
votes, seats, fractions=False, parties=string.ascii_letters, tiesallowed=True, verbose=True
votes,
seats,
fractions=False,
parties=string.ascii_letters,
tiesallowed=True,
verbose=True,
):
# votes = np.array(votes)
if verbose:
print("\nLargest remainder method with Hare quota (Hamilton)")
if fractions:
q = Fraction(int(sum(votes)), seats)
quotas = [Fraction(int(p), q) for p in votes]
representatives = np.array([int(qu.numerator // qu.denominator) for qu in quotas])
representatives = np.array(
[int(qu.numerator // qu.denominator) for qu in quotas]
)
else:
votes = np.array(votes)
quotas = (votes * seats) / np.sum(votes)
Expand Down Expand Up @@ -167,7 +180,13 @@ def largest_remainder(

# Divisor methods
def divisor(
votes, seats, method, fractions=False, parties=string.ascii_letters, tiesallowed=True, verbose=True
votes,
seats,
method,
fractions=False,
parties=string.ascii_letters,
tiesallowed=True,
verbose=True,
):
votes = np.array(votes)
representatives = np.zeros(len(votes), dtype=int)
Expand Down Expand Up @@ -214,18 +233,25 @@ def divisor(
else:
representatives = np.array([1 if p > 0 else 0 for p in votes])
if fractions:
divisors = np.array([
Fraction(2 * (i + 1) * (i + 2), 2 * (i + 1) + 1) for i in range(seats)
])
divisors = np.array(
[
Fraction(2 * (i + 1) * (i + 2), 2 * (i + 1) + 1)
for i in range(seats)
]
)
else:
divisors = np.arange(seats)
divisors = (2 * (divisors + 1) * (divisors + 2)) / (2 * (divisors + 1) + 1)
divisors = (2 * (divisors + 1) * (divisors + 2)) / (
2 * (divisors + 1) + 1
)
else:
raise NotImplementedError("divisor method " + method + " not known")
# assigning representatives
if seats > np.sum(representatives):
if fractions and method not in ["huntington", "hill", "modified_saintelague"]:
weights = np.array([[Fraction(int(p), d) for d in divisors.tolist()] for p in votes])
weights = np.array(
[[Fraction(int(p), d) for d in divisors.tolist()] for p in votes]
)
flatweights = sorted([w for l in weights for w in l])
else:
weights = np.array([p / divisors for p in votes])
Expand Down Expand Up @@ -296,7 +322,14 @@ def __divzero_fewerseatsthanparties(votes, seats, parties, tiesallowed, verbose)
return representatives


def quota(votes, seats, fractions=False, parties=string.ascii_letters, tiesallowed=True, verbose=True):
def quota(
votes,
seats,
fractions=False,
parties=string.ascii_letters,
tiesallowed=True,
verbose=True,
):
"""The quota method
see Balinski, M. L., & Young, H. P. (1975).
The quota method of apportionment.
Expand All @@ -316,8 +349,10 @@ def quota(votes, seats, fractions=False, parties=string.ascii_letters, tiesallow

while np.sum(representatives) < seats:
if fractions:
quotas = [Fraction(int(votes[i]), int(representatives[i]) + 1)
for i in range(len(votes))]
quotas = [
Fraction(int(votes[i]), int(representatives[i]) + 1)
for i in range(len(votes))
]
else:
quotas = votes / (representatives + 1)
# check if upper quota is violated
Expand Down

0 comments on commit 03673bf

Please sign in to comment.