Discrete event simulation with variable intervals I've built a discrete event simulation system, similar to the bank problem presented on the wikipedia page but with a key difference.

Let's say, that a TELLER can service two CUSTOMERS at the same time, and that the CUSTOMER-DEPARTURE time depends upon the number of CUSTOMERS a TELLER is servicing concurrently; I need to manage the dynamic CUSTOMER-DEPARTURE event scheduling when the number of concurrently served CUSTOMERS changes.

Consider the following example, where the servicing time for a single customer is of 4 time intervals and is doubled when the teller is servicing two customers at the same time:

t=0  CUSTOMER-ARRIVAL (1)   
t=1  TELLER-BEGINS-SERVICE (1) --&gt; schedules CUSTOMER-DEPARTURE (1) at t_a=5
...
t=3  CUSTOMER-ARRIVAL (2)
t=4  TELLER-BEGINS-SERVICE (1) --&gt; teller is now servicing two customers:
                                   - reschedule CUSTOMER-DEPARTURE (1) to t_b=6
                                   - schedule CUSTOMER-DEPARTURE (2) at t_c=12
...
t=6  CUSTOMER-DEPARTURE (1)    --&gt; teller is now servicing only one customer:
                                   - reschedule CUSTOMER-DEPARTURE (2) at t_d=9
...
t=9  CUSTOMER-DEPARTURE (2)


The times are calculated using this formula:

t = t_current + (4 - completed) * customer_count


Which gives these results for the values used above:


t_a = 1 + (4 - 0) * 1 = 5
t_b = 4 + (4 - 3) * 2 = 6
t_c = 4 + (4 - 0) * 2 = 12
t_d = 6 + (4 - 1) * 1 = 9


I came up with the following code in Python; is there something you would have done differently?

import heapq

class Simulator(object):

    def __init__(self):
        self.queue = []
        self.time = 0


    def schedule(self, time, callback, *args, **kwargs):
        """
        Schedules an event to be executed at a later point in time.
        ``callback`` is a callable which executed the event-specific behavior;
        the optional ``args`` and ``kwargs`` arguments will be passed to the
        event callback at invocation time.

        Returns an object which can be used to reschedule the event.
        """
        assert time &gt; self.time
        event = [time, True, callback, args, kwargs]
        heapq.heappush(self.queue, event)
        return event


    def reschedule(self, event, time):
        """
        Reschedules ``event`` to take place at a different point in time
        """
        assert time &gt; self.time
        rescheduled = list(event)
        event[1] = False
        rescheduled[0] = time
        heapq.heappush(self.queue, rescheduled)
        return rescheduled


    def run(self):
        """
        Simple simulation function to test the behavior
        """
        while self.queue:
            time, valid, callback, args, kwargs = heapq.heappop(self.queue)
            if not valid:
                continue
            self.time = time
            callback(*args, **kwargs)


def event(id, simulator, scheduled, rescheduled=None):
    if rescheduled is None:
        print "Event #{0}: scheduled at {1} and fired at {2}".format(id,
                scheduled, simulator.time)
    else:
        print "Event #{0}: scheduled at {1}, rescheduled at {2} and fired " \
                "at {3}".format(id, scheduled, rescheduled, simulator.time)


s = Simulator()
e1 = s.schedule(5, event, 1, s, 5)
e2 = s.schedule(7, event, 2, s, 7, 12)
e3 = s.schedule(9, event, 3, s, 9)
s.reschedule(e2, 12)
s.run()
Given the problem scope as I understand it (need to execute events in particular sequence, with ability to rearrange sequence at any point) I think the design seems clean and direct. I caveat that with: I don't know python, and I seem to be missing the part where you are ensuring sequence of your queue by the event's time member. I understand the event.time needs to be the sequencing key for your queue, or else the reschedule will have erratic behavior as self.time goes forward and backward if the queue isn't in order

The design wholesale seems clean though, to my eyes.