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) --> schedules CUSTOMER-DEPARTURE (1) at t_a=5
...
t=3 CUSTOMER-ARRIVAL (2)
t=4 TELLER-BEGINS-SERVICE (1) --> 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) --> 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 > 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 > 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()