You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@steve.apache.org by gs...@apache.org on 2022/06/02 02:52:00 UTC
[steve] branch trunk updated (61cfe1b -> 295b28f)
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a change to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
from 61cfe1b gather stv data for testing
new b5e7298 shift to a main() function; particularly to clarify the VERBOSE global
new 6354b41 Rejigger entrypoints in stv_tool.py
new a991c7f small tweak to collecting a list of unique rando values
new 8de31df use sum() to replace a couple loops
new 295b28f shift calc_aheads() into CandidateList. note potential error.
The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
monitoring/stv_tool.py | 116 +++++++++++++++++++++++++++++--------------------
1 file changed, 69 insertions(+), 47 deletions(-)
[steve] 05/05: shift calc_aheads() into CandidateList. note potential error.
Posted by gs...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
commit 295b28f5052bbc645b16fce87008f787862ca49c
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Wed Jun 1 21:51:48 2022 -0500
shift calc_aheads() into CandidateList. note potential error.
---
monitoring/stv_tool.py | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/monitoring/stv_tool.py b/monitoring/stv_tool.py
index a946a5b..12609e7 100755
--- a/monitoring/stv_tool.py
+++ b/monitoring/stv_tool.py
@@ -202,7 +202,7 @@ class CandidateList(object):
dbg('%-20s %15s %15.9f', 'Non-transferable', ' ', excess)
dbg('%-20s %15s %15.9f', 'Total', ' ', total)
- def sorted(self):
+ def calc_aheads(self):
def compare(c1, c2):
if c1.ahead < c2.ahead:
return -1
@@ -210,7 +210,15 @@ class CandidateList(object):
### expression for removed cmp() function
return (c1.vote > c2.vote) - (c1.vote < c2.vote)
return 1
- return sorted(self.l, key=functools.cmp_to_key(compare))
+
+ c_sorted = sorted(self.l, key=functools.cmp_to_key(compare))
+ last = 0
+ for i in range(1, len(c_sorted)+1):
+ ### STV.java:L233 ... This may need .vote on the latter compare
+ if i == len(c_sorted) or c_sorted[last] != c_sorted[i]:
+ for c in c_sorted[last:i]:
+ c.ahead = (i - 1) + last
+ last = i
class Candidate(object):
@@ -254,7 +262,11 @@ def iterate_one(quota, votes, candidates, num_seats):
def recalc(votes, candidates, num_seats):
excess = calc_totals(votes, candidates)
- calc_aheads(candidates)
+ a1 = [c.ahead for c in candidates.l]
+ candidates.calc_aheads()
+ a2 = [c.ahead for c in candidates.l]
+ if a1 != a2:
+ pass #print(f'CHANGE:\n {a1}\n {a2}')
candidates.dbg_display_tables(excess)
quota = calc_quota(len(votes), excess, num_seats)
any_changed = elect(quota, candidates, num_seats)
@@ -284,14 +296,9 @@ def calc_totals(votes, candidates):
return excess
+#@deprecated
def calc_aheads(candidates):
- c_sorted = candidates.sorted()
- last = 0
- for i in range(1, len(c_sorted)+1):
- if i == len(c_sorted) or c_sorted[last] != c_sorted[i]:
- for c in c_sorted[last:i]:
- c.ahead = (i - 1) + last
- last = i
+ candidates.calc_aheads()
def calc_quota(num_voters, excess, num_seats):
[steve] 02/05: Rejigger entrypoints in stv_tool.py
Posted by gs...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
commit 6354b4179e7cf64f41d3ca58ccfb7b9526ff7bcd
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Wed Jun 1 19:11:48 2022 -0500
Rejigger entrypoints in stv_tool.py
The old (unpublished?) API used load_votes() and read_nominees().
Create read_votefile() as a basic function to read the votes out of a
raw_board_votes.txt file. Similarly, read_labelmap() as a simple
function to read a lable:name mapping from board_nominations.ini files.
Concept: these two core functions return the needed data for running
an STV vote. Clarify these, and deprecate the older functions which
layer upon and alter the core data.
NOTE: it is unclear who may consume this module, so backwards-compat
is being retained for now.
---
monitoring/stv_tool.py | 56 ++++++++++++++++++++++++++++++++------------------
1 file changed, 36 insertions(+), 20 deletions(-)
diff --git a/monitoring/stv_tool.py b/monitoring/stv_tool.py
index de20885..cb7ffbc 100755
--- a/monitoring/stv_tool.py
+++ b/monitoring/stv_tool.py
@@ -51,9 +51,12 @@ RE_VOTE = re.compile(r'\[.{19}\]\s+'
VERBOSE = False
+#@deprecated
def load_votes(fname):
- lines = open(fname).readlines()
- if lines[0].strip() == 'rank order':
+ line = open(fname).readline()
+ if line.strip() == 'rank order':
+ lines = open(fname).readlines()
+
# The input file was processed by nstv-rank.py, or somehow otherwise
# converted to the standard input for VoteMain.jar
names = [s.strip() for s in lines[1].strip().split(',')][1:]
@@ -67,41 +70,54 @@ def load_votes(fname):
votes[parts[0]] = [remap[l] for l in parts[1:]]
return names, votes
- # Let's assume we're looking at a raw_board_votes.txt file, and a
- # companion board_nominations.ini file.
- nominees = read_nominees(fname)
+ ini_fname = os.path.join(os.path.dirname(fname),
+ 'board_nominations.ini')
+ labelmap = read_labelmap(ini_fname)
+
+ # Construct a label-sorted list of names from the labelmap.
+ names = [name for _, name in sorted(labelmap.items())]
+ # Load the raw votes that were recorded.
+ votes_by_label = read_votefile(fname)
+
+ # Remap all labels to names in the votes.
+ # NOTE: v represents the voter hash. (### why return this?)
+ votes = dict((v, [labelmap[l] for l in vote])
+ for v, vote in votes_by_label.items())
+
+ return names, votes
+
+
+def read_votefile(fname):
votes = { }
- for line in lines:
+ for line in open(fname).readlines():
match = RE_VOTE.match(line)
if match:
- # For a given voter hashcode, record their latest set of votes,
- # mapped from (character) labels to the nominee's name.
- votes[match.group('voterhash')] = [nominees[label]
- for label in match.group('votes')]
+ # For a given voter hashcode, record their latest set of votes.
+ votes[match.group('voterhash')] = match.group('votes')
- # Map the nominee dictionary into a label-sorted list of names
- names = [nominees[label] for label in sorted(nominees)]
-
- return names, votes
+ ### we should discard voterhash, and just return .values()
+ return votes
+#@deprecated
def read_nominees(votefile):
ini_fname = os.path.join(os.path.dirname(votefile),
'board_nominations.ini')
+ return read_labelmap(ini_fname)
+
- # Use the below try instead to catch this??
- if not os.path.exists(ini_fname):
- print("Error: board_nominations.ini could not be found at " + ini_fname, file=sys.stderr)
+def read_labelmap(fname):
+ if not os.path.exists(fname):
+ print(f'ERROR: "{fname}" could not be found.', file=sys.stderr)
sys.exit(2)
config = configparser.ConfigParser()
- config.read(ini_fname)
+ config.read(fname)
try:
return dict(config.items('nominees'))
except:
- print("Error processing input file: " + ini_fname, file=sys.stderr)
- print(" Goodbye!", file=sys.stderr)
+ print(f'ERROR: could not process input file, "{fname}".', file=sys.stderr)
sys.exit(2)
[steve] 01/05: shift to a main() function; particularly to clarify the VERBOSE global
Posted by gs...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
commit b5e7298aa0035574f258c445f13fc3abc761c80b
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Wed Jun 1 17:14:41 2022 -0500
shift to a main() function; particularly to clarify the VERBOSE global
---
monitoring/stv_tool.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/monitoring/stv_tool.py b/monitoring/stv_tool.py
index 131bcc6..de20885 100755
--- a/monitoring/stv_tool.py
+++ b/monitoring/stv_tool.py
@@ -386,7 +386,7 @@ def dbg(fmt, *args):
print(fmt % args)
-if __name__ == '__main__':
+def main(argv):
parser = argparse.ArgumentParser(description="Calculate a winner for a vote")
parser.add_argument('raw_file')
parser.add_argument("-s", "--seats", dest="seats", type=int,
@@ -395,9 +395,11 @@ if __name__ == '__main__':
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
help="Enable verbose logging", default=False)
- args = parser.parse_args()
+ args = parser.parse_args(argv)
+ global VERBOSE
VERBOSE = args.verbose
+
votefile = args.raw_file
num_seats = args.seats
@@ -410,3 +412,7 @@ if __name__ == '__main__':
candidates = run_vote(names, votes, num_seats)
candidates.print_results()
print('Done!')
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
[steve] 04/05: use sum() to replace a couple loops
Posted by gs...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
commit 8de31df41d9bb247953491cdd26451dc74db8bad
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Wed Jun 1 20:50:29 2022 -0500
use sum() to replace a couple loops
---
monitoring/stv_tool.py | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/monitoring/stv_tool.py b/monitoring/stv_tool.py
index 22389c1..a946a5b 100755
--- a/monitoring/stv_tool.py
+++ b/monitoring/stv_tool.py
@@ -165,11 +165,7 @@ class CandidateList(object):
self.l.append(c)
def count(self, state):
- count = 0
- for c in self.l:
- if (c.status & state) != 0:
- count += 1
- return count
+ return sum(int((c.status & state) != 0) for c in self.l)
def change_state(self, from_state, to_state):
any_changed = False
@@ -216,6 +212,7 @@ class CandidateList(object):
return 1
return sorted(self.l, key=functools.cmp_to_key(compare))
+
class Candidate(object):
def __init__(self, name, rand, ahead):
self.name = name
@@ -238,6 +235,7 @@ class Candidate(object):
assert quota is not None
self.weight = (self.weight * quota) / self.vote
+
def iterate_one(quota, votes, candidates, num_seats):
# assume that: count(ELECTED) < num_seats
if candidates.count(ELECTED + HOPEFUL) <= num_seats:
@@ -326,10 +324,7 @@ def elect(quota, candidates, num_seats):
def calc_surplus(quota, candidates):
- surplus = 0.0
- for c in candidates.l:
- if c.status == ELECTED:
- surplus += c.vote - quota
+ surplus = sum(c.vote - quota for c in candidates.l if c.status == ELECTED)
dbg('Total Surplus = %.9f', surplus)
return surplus
[steve] 03/05: small tweak to collecting a list of unique rando values
Posted by gs...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
commit a991c7f85a94376c49e9f5bfd322678c4a1f05e1
Author: Greg Stein <gs...@gmail.com>
AuthorDate: Wed Jun 1 20:36:26 2022 -0500
small tweak to collecting a list of unique rando values
---
monitoring/stv_tool.py | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/monitoring/stv_tool.py b/monitoring/stv_tool.py
index cb7ffbc..22389c1 100755
--- a/monitoring/stv_tool.py
+++ b/monitoring/stv_tool.py
@@ -157,6 +157,7 @@ class CandidateList(object):
def __init__(self, names):
num_cand = len(names)
randset = generate_random(num_cand)
+ #dbg('RANDSET: %s', randset)
self.l = [ ]
for n, r in zip(names, randset):
@@ -386,14 +387,11 @@ def generate_random(count):
random.seed(0) ### choose a seed based on input? for now: repeatable.
while True:
# Generate COUNT values in [0.0, 1.0)
+ # NOTE: use a list (not a set or dict) for repeatable ordering.
values = [random.random() for x in range(count)]
- # Ensure there are no duplicates
- for value in values:
- if values.count(value) > 1:
- break
- else:
- # The loop finished without breaking out
+ # Use a set() to check for dups. If no dups, then return the values.
+ if len(set(values)) == count:
return values