[ANN] DiscreteEvents 0.3: discrete event generation and simulation

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.

10 Likes

Now I’m happy to announce v0.3.1 of DiscreteEvents. It’s available from the Julia registry.

A few days after the last release @hdavid16 contributed some macros and we worked together to create a more Julian API to schedule functions as events or to start them as processes on a clock.

The above example now becomes:

....

for i in 1:3       # start three server processes
    @process serve(clock, i, input, output, Exponential(1/μ))
end
# create a repeating event for 10 arrivals
@event arrive(clock, input, count) every Exponential(1/λ) 10
@run! clock 20     # run the clock

Wouldn’t you like too to schedule and run your functions on a clock as easy as that? :wink:

4 Likes