Example #1
0
def simulate_queue(mar, ast):
    """
    Function to iterate through events and perform the main queue simulation.
    
    Parameters: mean arrival time and average service time.
    Returns: mean queue length and mean waiting time per customer.
    """

    # Initialize the queue
    q = Queue()

    # This variable stores the arrival time of the last event to enter the
    # queue. This is required because only the inter-arrival times of events
    # are known, not their absolute arrival times.
    last_arrival_time = 0.0

    # Global time parameter. This will usually point to the exit time of the
    # item that is currently at the head of the queue.
    t = 0.0

    # Variables to record waiting times and queue length
    num_events = 0
    total_waiting_time = 0.0
    total_length_time = 0.0

    # Start iterating through events
    for event in events(mar, ast):
        print t
        # Figure out the new event's absolute arrival time...
        event.arrival_time = last_arrival_time + event.inter_arrival_time
        # ...and update last_arrival_time to match this event
        last_arrival_time = event.arrival_time
        
        # We'll need to compare the arrival time of the new event with the exit
        # time of the event at the head of the queue
        head = q.head()
        if head is None:
            # If the queue is empty, just add the new event.
            event.exit_time = event.arrival_time + event.service_time
            q.enqueue(event)
            # No one else can get serviced while this event is at the counter.
            # Let's skip to when this event exits.
            t = event.exit_time
            continue
        if head.exit_time > event.arrival_time:
            # We're now at the head's exit time, but this new event came in
            # before the head could finish. So the new event gets queued.
            q.enqueue(event)
            # This event must now wait in the queue until the head exits. This
            # means that it adds one to the queue length until then. We'll take
            # care of queue lengths after then later.
            total_length_time += 1 * (head.exit_time - event.arrival_time)
            # We can't do anything more. Let's check if another event happened
            # to come in before the head could finish.
            continue
        if head.exit_time <= event.arrival_time:
            # This event actually arrived just when or after the head finished.
            # Let's queue the new event for now.
            q.enqueue(event)
            # The head is done, so let's remove it.
            completed_event = q.dequeue()
            # We need to catch the head and ask it how long it waited in the
            # queue
            total_waiting_time += (  completed_event.exit_time
                                   - completed_event.arrival_time
                                   - completed_event.service_time )
            num_events += 1
            # Before we enqueue the new event, we need to check whether 
            # We now need to set the new head's exit time - note that the
            # presence of a new head is guaranteed, since we just added a new
            # event.
            # If the head is the newly added event, then we need to consider
            # time it takes since *its* arrival. Otherwise, we consider the
            # time taken since the previous event exited.
            l = len(q)
            if l == 1:
                new_exit_time = event.arrival_time + event.service_time
            else:
                new_exit_time = t + q.head().service_time
            q.update_head('exit_time', new_exit_time)
            # In order to update the total_length_time, we note that the events
            # still in the queue need to wait until the new head completes. So,
            # each waiting queue member adds one queue length until then.
            # Note: l must be at least 1, since a new event was added.
            if l == 1:
                # l is 1, meaning that the new event immediately became the
                # head. Hence, it does not contribute to the queue length.
                pass
            else:
                # l is greater than or equal to 2. Now, there is a new event
                # which contributes one queue length, starting from its time
                # of arrival, until the new head's exit time.
                total_length_time += 1 * (new_exit_time - event.arrival_time)
                # Additionally, there are potentially another (l-2) events that
                # were already in the queue. These add one queue length *each*
                # until the new head's exit time.
                total_length_time += (l - 2) * (new_exit_time - t)
            # Once again, no one can get serviced until the new head leaves. So
            # it's safe to jump forward in time.
            t = new_exit_time
    
    # Now to remove elements that are still left in the queue
    # Note that there will always be at least one event left in the queue
    completed_event = q.dequeue()
    total_waiting_time += (  completed_event.exit_time
                           - completed_event.arrival_time
                           - completed_event.service_time )
    num_events += 1
    while len(q) > 0:
        new_exit_time = t + q.head().service_time
        q.update_head('exit_time', new_exit_time)
        # In order to update the total_length_time, we note that the events
        # still in the queue need to wait until the new head completes. So,
        # each waiting queue member adds one queue length until then.
        # Note: l must be at least 1, since a new event was added.
        l = len(q)
        if l > 1:
            # There are potentially another (l-1) events that were already in
            # the queue. These add one queue length *each* until the new head's
            # exit time.
            total_length_time += (l - 1) * (new_exit_time - t)
        # Catch the event as it comes out of the queue in order to find out how
        # long it waited in the queue.
        completed_event = q.dequeue()
        total_waiting_time += (  completed_event.exit_time
                               - completed_event.arrival_time
                               - completed_event.service_time )
        num_events += 1
        t = new_exit_time

    avg_queue_length = total_length_time / t
    avg_waiting_time = total_waiting_time / num_events
    return avg_queue_length, avg_waiting_time