Advocates of Science and Technology for the People

Vulnerable Elections (2): Cutting It Close


# Prometheus Bound Vulnerable Elections (2): Cutting it Close
# Please acknowledge G.Tapang and AGHAM for this code
#
# Copyright (C)2010 G.Tapang gtapang-at-agham.org
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#

""" Single Random Voter """
import pylab as p
import sys, math
from SimPy.Simulation import *
from random import expovariate, seed

if len(sys.argv) != 9: # the program name and the two arguments
# stop the program and #print an error message
sys.exit("Must provide 8 positive numbers")

aveRate_IntervalBetweenVotersPerHour = float(sys.argv[1])
aveTime_Looking = float(sys.argv[2])/60.0 # in seconds
aveTime_BEIProcessing = float(sys.argv[3])/60.0 # in seconds
aveTime_ChairProcessing = float(sys.argv[4])/60.0 # in seconds
aveTime_FillingUpBallot = float(sys.argv[5])/60.0 # in seconds
aveTime_PCOSUse = float(sys.argv[6])/60.0 # in seconds
aveTime_PostVoteProcessing = float(sys.argv[7])/60.0 # in seconds
RUNS = int(sys.argv[8])

aveInterval_BetweenVoterArrival = 60.0/aveRate_IntervalBetweenVotersPerHour
print "Average interval between voters : ", aveInterval_BetweenVoterArrival
print "Rate of voter arrival per hour : ", aveRate_IntervalBetweenVotersPerHour
print "Average time used in looking name up in voters list : ", aveTime_Looking
print "Average time used by BEI in processing voter (s) : ", aveTime_BEIProcessing
print "Average time used by chairman of BEI in giving ballot (s): ", aveTime_ChairProcessing
print "Average time used by voter in filling up ballot : ", aveTime_FillingUpBallot
print "Average time used by voter in using PCOS machine : ", aveTime_PCOSUse
print "Average time used by BEI in post-vote processing : ", aveTime_PostVoteProcessing
print "-----------------------------------------------------------"

class Voter(Process):
""" Voter arrives at random time,
searches for his/her name,
spends a few moments to decide, votes then leaves """
def visit(self, lookupmon, bei, beiwaitmon, beimon, folder, foldermon,
pollareamon, pcos, pcoswaitmon, pcosmon, chair, chairwaitmon, chairmon,
postvoteprocesswaitmon, postvoteprocessmon, votingprocessmon):

start = now() #arrival time

# find name
# #print "%5.4f %s : Entering polling area. Finding my name "%(now(),self.name)
lookingtime = expovariate(1.0/aveTime_Looking) # averageTimeToLook
yield hold, self, lookingtime # Looking...
wait = now() - start
lookupmon.observe(wait) # keep track of finding name time
#print "%5.4f %s : Found my name!"%(now(), self.name)

# Go to BEI. BEI is limited to at most 5 (6 minus the chair)
# now we need to factor the BEI. The BEI is a limited resource. it can only respond
# to single person at a time (check GI from comelec)
arrive = now() # time start waiting for BEI to be free
yield request, self, bei # wait for a BEI to be free
wait = now() - arrive # waiting time when BEI is free
beiwaitmon.observe(wait) # save the waiting time for stats
#print "%5.4f %s : BEI is free"%(now(),self.name)
# at this point the BEI will verify voter and process
beitime = expovariate(1.0/aveTime_BEIProcessing)
arrive = now()
yield hold, self, beitime
wait = now() - arrive # bei processing time
beimon.observe(wait) # save the processing time for stats
yield release, self, bei # release the bei because we are done
#print "%5.4f %s : Going to chair"%(now(), self.name)

# We then need to go to the BEI chair. As before this is a limited
# resource. The BEI chair cannot do multiple ballots or multiple
# voters at a time. The GI is explicit on this
arrive = now() # Start of waiting time
yield request, self, chair # Wait for the chair to be free
wait = now() - arrive # this is the chair waiting time
chairwaitmon.observe(wait) # save waiting time for stats
#print "%5.4f %s : Chair is free."%(now(), self.name)

# afterwards the chair will then process the voter
# give him a ballot, sign some things and give a folder.
chairtime = expovariate(1.0/aveTime_ChairProcessing)
arrive = now()
yield hold, self, chairtime # chair is processing voter
wait = now() - arrive # we made a prelim time motion = 45 sec
chairmon.observe(wait) # save waiting time
#print "%5.4f %s : Chair done with me."%(now(), self.name)
yield release, self, chair # i guess you can release the chair now
#print "%5.4f %s : Release chair."%(now(),self.name)

# is there a folder available? if not, wait for a folder
# there are only 20 folders available. One must NOT vote and cast
# in the PCOS without the folder
#print "%5.4f %s : Got a ballot. Waiting for a folder"%(now(),self.name)
arrive = now()
yield request, self, folder # wait for the folder
wait = now() - arrive
foldermon.observe(wait) # this is the time waiting for the folder

# once you have the folder then you can fill up the ballot
# afterwards what we need to do is to go to the voting area
#print "%5.4f %s : Got a folder! Filling up ballot"%(now(),self.name)
arrive = now()
timepollarea = expovariate(1.0/aveTime_FillingUpBallot)
yield hold, self, timepollarea
wait = now() - arrive #waiting time
pollareamon.observe(wait)
#print "%5.4f %s : Done filling up ballot! "%(now(),self.name)
yield release, self, folder # yield up your folder

#print "%5.4f %s : Lining up for PCOS "%(now(),self.name)
arrive = now()
yield request, self, pcos
wait = now() - arrive # waiting time
pcoswaitmon.observe(wait) # waiting time for PCOS to be free
#print "%5.4f %s : Casting vote at the PCOS "%(now(),self.name)
# here we go to the PCOS machine and cast
pcostime = expovariate(1.0/aveTime_PCOSUse)
arrive = now()
yield hold, self, pcostime
wait = now() - arrive
pcosmon.observe(wait)
#print "%5.4f %s : Finished casting vote"%(now(),self.name)
yield release, self, pcos

#afterwards the bei will process you again
# put an ink on your finger, have you sign up some more forms etc
# you need a bei to do this
arrive = now()
yield request, self, bei # so we request one!
wait = now() - arrive # this is post processing waiting time
postvoteprocesswaitmon.observe(wait)
#print "%5.4f %s : Got a BEI after casting vote"%(now(),self.name)

postprocesstime = expovariate(1.0/aveTime_PostVoteProcessing)
arrive = now()
yield hold, self, postprocesstime
wait = now() - arrive # post processing time
postvoteprocessmon.observe(wait)
#print "%5.4f %s : Finished vote processing"%(now(),self.name)

yield release, self, bei # we're done with the bei
wait = now() - start
votingprocessmon.observe(wait)
#print "%5.4f %s : Releasing BEI. Done Voting"%(now(),self.name)

class Source(Process):
""" Generates regular voters from a pool """
# def visit(self, lookupmon, bei, beiwaitmon, beimon, folder, folderwaitmon, foldermon,
# pollareamon, pcos, pcosmon, chair, chairwaitmon, chairmon,
# postvoteprocesswaitmon, postvoteprocessmon, votingprocessmon):
def generate(self, number, interval, lookupMon, BEI, beiWaitMon, beiMon,
FOLDER, folderMon, pollareaMon, PCOS, pcosWaitMon, pcosMon,
CHAIR, chairWaitMon, chairMon, postVoteProcessWaitMon, postVoteProcessMon, voterMon, entryMon):
for i in range(number):
c = Voter(name = "Voter%04d"%(i,))
activate(c,c.visit(lookupmon=lookupMon, bei=BEI, beiwaitmon=beiWaitMon, beimon=beiMon,
folder=FOLDER, foldermon=folderMon,
pollareamon=pollareaMon, pcos=PCOS, pcoswaitmon=pcosWaitMon,
pcosmon=pcosMon, chair=CHAIR, chairwaitmon=chairWaitMon, chairmon=chairMon,
postvoteprocesswaitmon=postVoteProcessWaitMon, postvoteprocessmon=postVoteProcessMon,
votingprocessmon=voterMon))
t = expovariate(1.0/interval)
yield hold,self,t
entryMon.observe(now()) # get voter time stamp and count voters who arrived at polling place

maxNumber = 1000
maxTime = 60*11.0 #minutes
theSeed = 12345
NumberOfDesks = 1

def model(runSeed=theSeed):
seed(runSeed)
bei_r = Resource(capacity=5, name="BEI")
chair_r = Resource(capacity=1, name="CHAIR")
folder_r = Resource(capacity=20, name="FOLDERS")
pcos_r = Resource(capacity=1, name="PCOS")
lookupMonitor = Monitor()
beiMonitor = Monitor()
pollareaMonitor = Monitor()
pcosMonitor = Monitor()
folderMonitor = Monitor()
beiWaitMonitor = Monitor()
pcosWaitMonitor = Monitor()
voteMonitor = Monitor()
postVoteMonitor = Monitor()
postVoteWaitMonitor = Monitor()
chairMonitor = Monitor()
chairWaitMonitor = Monitor()
entryMonitor = Monitor()
initialize()
s = Source(name='Source')
activate(s, s.generate(number=maxNumber, interval=aveInterval_BetweenVoterArrival,
lookupMon=lookupMonitor, BEI=bei_r, beiWaitMon = beiWaitMonitor, beiMon=beiMonitor,
FOLDER=folder_r, folderMon=folderMonitor,
pollareaMon = pollareaMonitor, PCOS=pcos_r, pcosWaitMon=pcosWaitMonitor, pcosMon=pcosMonitor,
CHAIR=chair_r, chairWaitMon=chairWaitMonitor, chairMon=chairMonitor,
postVoteProcessWaitMon=postVoteWaitMonitor, postVoteProcessMon=postVoteMonitor, voterMon = voteMonitor,
entryMon=entryMonitor), at=0.0)
simulate(until=maxTime)
return (entryMonitor.count(), voteMonitor.count(), voteMonitor.mean(),
chairWaitMonitor.mean(), chairMonitor.mean(),
pcosWaitMonitor.mean(), pcosMonitor.mean(),
folderMonitor.mean(),
lookupMonitor.mean(), pollareaMonitor.mean()
)

print "Number of simulation runs : ", RUNS
theseeds = random.sample(xrange(100000000),RUNS)
vm = Monitor()
vmt = Monitor()
em = Monitor()
cm = Monitor()
cwm = Monitor()
pm = Monitor()
pwm = Monitor()
fm = Monitor()
lm = Monitor()
pam = Monitor()
for Sd in theseeds:
result = model(Sd)
em.observe(result[0])
vm.observe(result[1])
vmt.observe(result[2])
cwm.observe(result[3])
cm.observe(result[4])
pwm.observe(result[5])
pm.observe(result[6])
fm.observe(result[7])
lm.observe(result[8])
pam.observe(result[9])

print "average voters who entered poll area : ", em.mean()
print "average voters finished by 6pm : ", vm.mean()
print "average total time in polling area (minutes) : ", vmt.mean()
print "average lookup time (minutes) : ", lm.mean()
print "average chair wait time (minutes) : ", cwm.mean()
print "average chair processing time : ", cm.mean()
print "average folder wait time : ", fm.mean()
print "average filling up ballots (minutes) : ", pam.mean()
print "average pcos wait time (minutes) : ", pwm.mean()
print "average pcos processing time : ", pm.mean()