A Julia package for discrete event generation and simulation
I’m pleased to announce that DiscreteEvents
0.3 is now available from the Julia registry. It has been renamed from Simulate
0.2 following the discussion on Discourse.
DiscreteEvents is an extensive rework and has some exciting features:
- It allows to schedule and execute arbitrary Julia functions and expressions on a clock’s timeline.
- It has a streamlined API allowing to combine several approaches to simulation of discrete event systems (DES): based on events, activities, processes, state-machines and actors.
- It is well documented and has a companion site
DiscreteEventsCompanion
with more documentation and examples. - It is pure Julia and now has much better performance.
- Using Julia’s multi-threading capabilities it introduces parallel clocks (with thread-local time) and allows multi-threaded simulations (experimental!).
- It introduces real-time clocks where you can schedule your functions on a physical timeline (experimental!).
Example, an M/M/3 queue
Three servers serve customers arriving at an arrival rate λ with a service rate μ. We implement serve
and arrive
functions, create a clock and queues, then start three server processes, a Poisson arrival process and run.
using DiscreteEvents, Printf, Distributions, Random
# describe a server process
function serve(clk::Clock, id::Int, input::Channel, output::Channel, X::Distribution)
job = take!(input)
print(clk, @sprintf("%6.3f: server %d serving customer %d\n", tau(clk), id, job))
delay!(clk, X)
print(clk, @sprintf("%6.3f: server %d finished serving %d\n", tau(clk), id, job))
put!(output, job)
end
# model the arrivals
function arrive(c::Clock, input::Channel, cust::Vector{Int})
cust[1] += 1
@printf("%6.3f: customer %d arrived\n", tau(c), cust[1])
put!(input, cust[1])
end
Random.seed!(123) # set random number seed
const μ = 1/3 # service rate
const λ = 0.9 # arrival rate
count = [0] # a job counter
clock = Clock() # create a virtual clock
input = Channel{Int}(Inf)
output = Channel{Int}(Inf)
for i in 1:3 # start three server processes
process!(clock, Prc(i, serve, i, input, output, Exponential(1/μ)))
end
# create a repeating event for 10 arrivals
event!(clock, fun(arrive, clock, input, count), every, Exponential(1/λ), n=10)
run!(clock, 20) # run the clock 20 time units
If we source this program, it runs a simulation.
output:
julia> include("examples/intro.jl")
0.141: customer 1 arrived
0.141: server 1 serving customer 1
1.668: server 1 finished serving 1
2.316: customer 2 arrived
2.316: server 2 serving customer 2
3.154: customer 3 arrived
3.154: server 3 serving customer 3
4.182: customer 4 arrived
4.182: server 1 serving customer 4
4.364: server 3 finished serving 3
4.409: customer 5 arrived
4.409: server 3 serving customer 5
4.533: customer 6 arrived
4.566: server 2 finished serving 2
4.566: server 2 serving customer 6
5.072: customer 7 arrived
5.299: server 3 finished serving 5
5.299: server 3 serving customer 7
5.335: server 1 finished serving 4
5.376: customer 8 arrived
5.376: server 1 serving customer 8
5.833: customer 9 arrived
6.134: customer 10 arrived
6.570: server 1 finished serving 8
6.570: server 1 serving customer 9
6.841: server 3 finished serving 7
6.841: server 3 serving customer 10
8.371: server 2 finished serving 6
10.453: server 1 finished serving 9
10.477: server 3 finished serving 10
"run! finished with 40 clock events, 0 sample steps, simulation time: 20.0"
DiscreteEvents
is in development and I am happy if you try it out and contribute your ideas, suggestions, experience and code. I think it enters and opens as well some uncharted territory in DES simulation. Maybe some of you are interested to join …
After some time since the last release I now will switch to continuous delivery of updates and improvements.