============== Polling places ============== **Due: Friday, Nov 4th, 4pm** *You may work alone or in a pair on this assignment.* .. image:: images/voting_booths.png :width: 3in :align: right Every two years, many people around the country wait in long lines to vote due, in part, to inadequate provisioning of voting booths. In this assignment you will write code to simulate the flow of voters through precincts. The information generated by your simulator could be used by an election official as a crude lower bound to help determine the number of voting booths needed at a precinct. We will view this problem as an instance of the more general problem of simulating an :math:`M`/:math:`M`/:math:`N` queue, where: - the first :math:`M` indicates that arrivals (voters arriving at the polls) obey a Markov process; - the second :math:`M` indicates that the service times obey a Markov process (the amount of time a voter takes to complete a ballot); and finally - the :math:`N` indicates that there are :math:`N` servers (the number of voting booths). We will tell you how to structure your implementation at a high level, but you will be responsible for the design details. Getting started --------------- See `these start-up instructions `__ if you intend to work alone. See `these start-up instructions `__ if you intend to work with the same partner as in a previous assignment. See `these start-up instructions `__ if you intend to work in a *NEW* pair. The ``pa4`` directory includes a file named ``simulate.py``, which you will modify, a file named ``util.py`` that contains a few useful functions, and a directory named ``data``, which contains sample data. See the file ``README.txt`` in ``pa4/data`` for an explanation of the contents of the data directory. Please put all of your code for this assignment in ``simulate.py``. Do not add extra files for the required classes. Precinct Configurations ----------------------- The configurations for a precinct, except the number of voting booths, is specified in a JSON file that contains the: - number of voters assigned to the precinct (``num_voters``), - number of hours the polls are open (``hours_open``), - voting duration rate (``voting_duration_rate``), - arrival rate (``arrival_rate``), and a - seed for the random number generator (``seed``). The number of voting booths (``number_of_booths``) will be supplied as a command-line argument. We have provided a function, ``setup_config`` in ``util.py`` that takes the name of a configuration file and a value for the number of voting booths and returns a dictionary containing the configuration parameters. The key for each value is shown in parenthesis next to its description above. In addition to being called to set up the simulation in the ``main`` function of ``simulate.py``, this function will also be useful when testing your code. Here is an example use of this function: :: In [1]: import util In [2]: util.setup_config("data/config-0.json", 3) Out[2]: {'arrival_rate': 0.11, 'hours_open': 1, 'num_voters': 5, 'number_of_booths': 3, 'seed': 1468604453, 'voting_duration_rate': 0.1} Representing Voters ------------------- Your implementation must contain a class for representing a voter. This data structure must include the time the voter arrives at the polls, voting duration, that is, the amount of time the voter takes to vote, and the time the voter is assigned to a voting booth (aka, the start time). We found it convenient for debugging purposes to store a unique identifier and the time the voter leaves the voting booth (aka, the departure time) as well. The start time for a voter, which is not available at construction time, should be initialized to ``None`` when the voter object is constructed. This value should be reset to the correct value when the voter is assigned to a voting booth. Similarly, voters' departure times cannot be determined until we know their start times. Your implementation **must** include at a minimum: a constructor and properties named ``arrival_time``, ``voting_duration``, and ``start_time`` for the corresponding values. Our utility function for printing voters, described below, relies on the existence of these properties. This class is straightforward. It merely serves as a way to encapsulate information about a voter. Our implementation is roughly 30 lines, including the code for generating unique identifiers for the voters and for handling the optional attributes. Please note, the voter sample class, described next, is the only class that is allowed to call the voter constructor. Generating a voter sample ------------------------- Your implementation must include a class for generating voter samples using a Poisson process. From Wikipedia: a Poisson process, named after the French mathematician Simeon-Denis Poisson (1781-1840), is the stochastic process in which events occur continuously and independently of one another. We can use the following fact to generate a voter sample: if the number of arrivals in a given time interval [0,t] follows the Poisson distribution, with mean :math:`t*\lambda`, then the gap between arrivals times follows the exponential distribution with rate parameter :math:`\lambda`. **You may not generate the voters en masse when you construct a voter sample. Instead, you must generate voters one at a time as requests are received from the client of the sample.** To generate a voter, you will need to generate an arrival time and a voting duration. To generate the arrival time of the next voter, you will draw an inter-arrival time (:math:`gap`) from the exponential distribution using the specified arrival rate as :math:`\lambda` and add it to the current time (:math:`t`). You'll then move time forward by setting :math:`t` to :math:`t+gap`. You will draw the voter's voting duration by drawing a value from the exponential distribution using the specified voting duration rate as :math:`\lambda`. For testing purposes, it is important that you draw the gap and the voting duration in the same order as our code. To reduce the likelihood that the testing process fails simply because the values were drawn in the wrong order, we have provided a function, ``gen_voter_parameters``, in ``util.py`` that takes the arrival rate and the voting duration rate as arguments and returns a pair of floats to use for the gap and voting duration (in that order). We have one detail left to describe: the stopping conditions. Your simulator should generate new voters until (1) you have generated the number of the voters specified for the precinct or (2) you generate a voter who arrives after closing time. In the second case, the late voter should be discarded and no further voters should be generated. We will refer to voters who arrive before the polls close as *valid*. Here is a more formal statement of the stopping conditions: if :math:`M` is the number of voters who arrive before the polls close and :math:`N` is the number of voters in the precinct, then your implementation should generate exactly: - :math:`N` voters if :math:`M=N` - :math:`M+1` voters if :math:`M [number of voting booths] You can safely ignore this message. If your code does not generate any debugging output or sends its debugging output to standard error (include ``file=sys.stderr`` as an argument in your calls to ``print``), then you can compare your results to ours using a Linux pipe and ``diff``. For example,:: python3 simulate.py data/config-0.json | diff - data/config-0-1-voters.txt Using dash (``-``) as an argument to ``diff`` indicates that the data coming from the pipe should be used in place of data from a file. Submission ---------- See `these submission instructions `__ if you are working alone. See `these submission instructions `__ if you are working in the same pair as for PA #2 or PA #3. See `these submission instructions `__ if you are working in a *NEW* pair. .. _random: https://docs.python.org/3/library/random.html .. _queue: https://docs.python.org/3/library/queue.html .. |clearfloat| raw:: html