From R. Shannon (1975), simulation is
the process of designing a model of a real system and conducting experiments with this model for the purpose either of understanding the behavior of the system or of evaluating various strategies [...] for the operation of the system.
From R. Shannon (1975), simulation is
the process of designing a model of a real system and conducting experiments with this model for the purpose either of understanding the behavior of the system or of evaluating various strategies [...] for the operation of the system.
Taxonomy, from Law and Kelton (2000):
From R. Shannon (1975), simulation is
the process of designing a model of a real system and conducting experiments with this model for the purpose either of understanding the behavior of the system or of evaluating various strategies [...] for the operation of the system.
Taxonomy, from Law and Kelton (2000):
Examples:
What can be modelled as a Discrete-Event Simulation (DES)?
What can be modelled as a Discrete-Event Simulation (DES)?
Common examples:
and many more applications from manufacturing systems, construction engineering, project management, logistics, transportation systems, business processes, healthcare, telecommunications networks...
Programming styles (Banks 2005):
Programming styles (Banks 2005):
Spectrum of tools:
but
simmer
Main characteristics:
simmer
Main characteristics:
Resources:
Online documentation (manual + 10 vignettes): https://r-simmer.org
Ucar I, Smeets B, Azcorra A (2019). “simmer: Discrete-Event Simulation for R.” Journal of Statistical Software, 90(2), 1-30. doi: 10.18637/jss.v090.i02.
Ucar I, Hernández JA, Serrano P, Azcorra A (2018). “Design and Analysis of 5G Scenarios with simmer: An R Package for Fast DES Prototyping.” IEEE Communications Magazine, 56(11), 145-151. doi: 10.1109/MCOM.2018.1700960.
From M. Pidd (1988), Section 5.3.1:
From M. Pidd (1988), Section 5.3.1:
job <- trajectory() %>% seize("machine") %>% timeout(RUNNING) %>% branch( CHECK_WORN, continue = TRUE, trajectory() %>% seize("operative") %>% timeout(RETOOL) %>% release("operative") ) %>% seize("operative") %>% timeout(RESET) %>% release("operative") %>% release("machine")
From M. Pidd (1988), Section 5.3.1:
job <- trajectory() %>% seize("machine") %>% timeout(RUNNING) %>% branch( CHECK_WORN, continue = TRUE, trajectory() %>% seize("operative") %>% timeout(RETOOL) %>% release("operative") ) %>% seize("operative") %>% timeout(RESET) %>% release("operative") %>% release("machine")
task <- trajectory() %>% seize("operative") %>% timeout(AWAY) %>% release("operative")
library(simmer); set.seed(1234)RUNNING <- function() rexp(1, 1)CHECK_WORN <- function() runif(1) < 0.2RETOOL <- function() rexp(1, 2)RESET <- function() rexp(1, 3)AWAY <- function() rexp(1, 1)
job <- trajectory() %>% seize("machine") %>% timeout(RUNNING) %>% branch( CHECK_WORN, continue = TRUE, trajectory() %>% seize("operative") %>% timeout(RETOOL) %>% release("operative") ) %>% seize("operative") %>% timeout(RESET) %>% release("operative") %>% release("machine")
task <- trajectory() %>% seize("operative") %>% timeout(AWAY) %>% release("operative")
library(simmer); set.seed(1234)RUNNING <- function() rexp(1, 1)CHECK_WORN <- function() runif(1) < 0.2RETOOL <- function() rexp(1, 2)RESET <- function() rexp(1, 3)AWAY <- function() rexp(1, 1)job <- trajectory() %>% ...task <- trajectory() %>% ...NEW_JOB <- function() rexp(1, 5)NEW_TASK <- function() rexp(1, 1)
library(simmer); set.seed(1234)RUNNING <- function() rexp(1, 1)CHECK_WORN <- function() runif(1) < 0.2RETOOL <- function() rexp(1, 2)RESET <- function() rexp(1, 3)AWAY <- function() rexp(1, 1)job <- trajectory() %>% ...task <- trajectory() %>% ...NEW_JOB <- function() rexp(1, 5)NEW_TASK <- function() rexp(1, 1)
env <- simmer("Job Shop") %>% add_resource("machine", 10) %>% add_resource("operative", 5) %>% add_generator("job", job, NEW_JOB) %>% add_generator("task", task, NEW_TASK) %>% run(until=1000)env
## simmer environment: Job Shop | now: 1000 | next: 1000.09508921831## { Monitor: in memory }## { Resource: machine | monitored: TRUE | server status: 3(10) | queue status: 0(Inf) }## { Resource: operative | monitored: TRUE | server status: 2(5) | queue status: 0(Inf) }## { Source: job | monitored: 1 | n_generated: 5177 }## { Source: task | monitored: 1 | n_generated: 995 }
library(simmer); set.seed(1234)RUNNING <- function() rexp(1, 1)CHECK_WORN <- function() runif(1) < 0.2RETOOL <- function() rexp(1, 2)RESET <- function() rexp(1, 3)AWAY <- function() rexp(1, 1)job <- trajectory() %>% ...task <- trajectory() %>% ...NEW_JOB <- function() rexp(1, 5)NEW_TASK <- function() rexp(1, 1)
env <- simmer("Job Shop") %>% add_resource("machine", 10) %>% add_resource("operative", 5) %>% add_generator("job", job, NEW_JOB) %>% add_generator("task", task, NEW_TASK) %>% run(until=1000)env
## simmer environment: Job Shop | now: 1000 | next: 1000.09508921831## { Monitor: in memory }## { Resource: machine | monitored: TRUE | server status: 3(10) | queue status: 0(Inf) }## { Resource: operative | monitored: TRUE | server status: 2(5) | queue status: 0(Inf) }## { Source: job | monitored: 1 | n_generated: 5177 }## { Source: task | monitored: 1 | n_generated: 995 }
aggregate(cbind(server, queue) ~ resource, get_mon_resources(env), mean)
## resource server queue## 1 machine 7.987438 1.0355590## 2 operative 3.505732 0.4441298
Overview of the C++ core (white) and R API (blue)
Overview of the C++ core (white) and R API (blue)
Resource: server (configurable capacity) + priority queue (configurable size), supports preemption
Manager: modifies resources at run time (schedule)
Overview of the C++ core (white) and R API (blue)
Resource: server (configurable capacity) + priority queue (configurable size), supports preemption
Manager: modifies resources at run time (schedule)
Source: creates new arrivals following some distribution of inter-arrival times
Arrival: interacting processes, with attributes and prioritization values
Overview of the C++ core (white) and R API (blue)
Resource: server (configurable capacity) + priority queue (configurable size), supports preemption
Manager: modifies resources at run time (schedule)
Source: creates new arrivals following some distribution of inter-arrival times
Arrival: interacting processes, with attributes and prioritization values
Trajectory: interlinkage of activities, a common path for arrivals of the same type
Activity: unit of action, main building block
Similar to dplyr
for data manipulation. In the words of H. Wickham,
by constraining your options, it simplifies how you can think about [something]
Similar to dplyr
for data manipulation. In the words of H. Wickham,
by constraining your options, it simplifies how you can think about [something]
Fixed vs. dynamic parameters:
traj0 <- trajectory() %>% log_("Entering the trajectory") %>% timeout(10) %>% log_("Leaving the trajectory")
traj1 <- trajectory() %>% log_(function() "Entering the trajectory") %>% timeout(function() 10) %>% log_(function() "Leaving the trajectory")
timeout
, timeout_from_attribute
, timeout_from_global
set_attribute
, set_global
set_prioritization
seize
, release
set_capacity
, set_queue_size
select
, seize_selected
...activate
, deactivate
set_trajectory
, set_source
rollback
branch
clone
, synchronize
batch
, separate
send
, trap
, untrap
, wait
leave
renege_in
, renege_if
, renege_abort
handle_unfinished
log_
stop_if
timeout
, timeout_from_attribute
, timeout_from_global
set_attribute
, set_global
set_prioritization
seize
, release
set_capacity
, set_queue_size
select
, seize_selected
...activate
, deactivate
set_trajectory
, set_source
rollback
branch
clone
, synchronize
batch
, separate
send
, trap
, untrap
, wait
leave
renege_in
, renege_if
, renege_abort
handle_unfinished
log_
stop_if
(Plus many getters to retrieve parameters at run time)
simmer
monitor_mem
(default), monitor_csv
... (extensible, see simmer.mon
on GitHub)simmer
monitor_mem
(default), monitor_csv
... (extensible, see simmer.mon
on GitHub)add_generator
: based on a distribution functionadd_dataframe
: based on a data frame (additional columns as attributes)simmer
monitor_mem
(default), monitor_csv
... (extensible, see simmer.mon
on GitHub)add_generator
: based on a distribution functionadd_dataframe
: based on a data frame (additional columns as attributes)add_resource
: priority resource, with capacity and queue size; optional preemptionsimmer
monitor_mem
(default), monitor_csv
... (extensible, see simmer.mon
on GitHub)add_generator
: based on a distribution functionadd_dataframe
: based on a data frame (additional columns as attributes)add_resource
: priority resource, with capacity and queue size; optional preemptionadd_global
simmer
monitor_mem
(default), monitor_csv
... (extensible, see simmer.mon
on GitHub)add_generator
: based on a distribution functionadd_dataframe
: based on a data frame (additional columns as attributes)add_resource
: priority resource, with capacity and queue size; optional preemptionadd_global
run
, stepn
simmer
automatically records every change in the state of the system. All these statistics can be retrieved after the simulation:
names( get_mon_arrivals(simmer(), per_resource=FALSE) )
## [1] "name" "start_time" "end_time" "activity_time"## [5] "finished"
names( get_mon_arrivals(simmer(), per_resource=TRUE) )
## [1] "name" "start_time" "end_time" "activity_time"## [5] "resource"
names( get_mon_attributes(simmer()) )
## [1] "time" "name" "key" "value"
names( get_mon_resources(simmer()) )
## [1] "resource" "time" "server" "queue" "capacity" ## [6] "queue_size" "system" "limit"
Natural way to simulate CTMC and birth-death processes:
set.seed(1234)lambda <- 2mu <- 4rho <- lambda/mu
mm1.traj <- trajectory() %>% seize("mm1.resource", 1) %>% timeout(function() rexp(1, mu)) %>% release("mm1.resource", 1)
mm1.env <- simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(1, lambda)) %>% run(2000)
Natural way to simulate CTMC and birth-death processes:
set.seed(1234)lambda <- 2mu <- 4rho <- lambda/mu
mm1.traj <- trajectory() %>% seize("mm1.resource", 1) %>% timeout(function() rexp(1, mu)) %>% release("mm1.resource", 1)
mm1.env <- simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(1, lambda)) %>% run(2000)
Easy replication with standard R functions:
mm1.envs <- lapply(1:20, function(i) { simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(100, lambda)) %>% run(1000/lambda)})
Easy replication with standard R functions:
mm1.envs <- lapply(1:20, function(i) { simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(100, lambda)) %>% run(1000/lambda)})
Even easier parallelization of replicas:
mm1.envs <- parallel::mclapply(1:20, function(i) { simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(100, lambda)) %>% run(1000/lambda) %>% wrap()}, mc.cores=4)
Easy replication with standard R functions:
mm1.envs <- lapply(1:20, function(i) { simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(100, lambda)) %>% run(1000/lambda)})
Even easier parallelization of replicas:
mm1.envs <- parallel::mclapply(1:20, function(i) { simmer() %>% add_resource("mm1.resource", 1, Inf) %>% add_generator("arrival", mm1.traj, function() rexp(100, lambda)) %>% run(1000/lambda) %>% wrap()}, mc.cores=4)
head(get_mon_arrivals(mm1.envs), 3)
## name start_time end_time activity_time finished replication## 1 arrival0 0.04471412 0.2337613 0.189047164 TRUE 1## 2 arrival1 0.17734846 0.2384445 0.004683229 TRUE 1## 3 arrival2 0.52254010 0.9208069 0.398266801 TRUE 1
There are usually multiple valid ways of mapping the identified resources and processes into the simmer
API
There are usually multiple valid ways of mapping the identified resources and processes into the simmer
API
beep <- trajectory() %>% log_("beeeep!")
env <- simmer() %>% add_generator("beep", beep, function() 1) %>% run(2.5)
## 1: beep0: beeeep!## 2: beep1: beeeep!
There are usually multiple valid ways of mapping the identified resources and processes into the simmer
API
beep <- trajectory() %>% log_("beeeep!")
env <- simmer() %>% add_generator("beep", beep, function() 1) %>% run(2.5)
## 1: beep0: beeeep!## 2: beep1: beeeep!
alarm <- trajectory() %>% timeout(1) %>% log_("beeeep!") %>% rollback(2)
env <- simmer() %>% add_generator("alarm", alarm, at(0)) %>% run(2.5)
## 1: alarm0: beeeep!## 2: alarm0: beeeep!
Comparison with similar frameworks (out-of-date!):
Heavy M/M/1, ρ≈0.9:
test_mm1_simmer <- function(n, m, mon=FALSE) { mm1 <- trajectory() %>% seize("server", 1) %>% timeout(function() rexp(1, 1.1)) %>% release("server", 1) env <- simmer() %>% add_resource("server", 1, mon=mon) %>% add_generator("customer", mm1, function() rexp(m, 1), mon=mon) %>% run(until=n)}
Comparison with similar frameworks (out-of-date!):
Heavy M/M/1, ρ≈0.9:
test_mm1_simmer <- function(n, m, mon=FALSE) { mm1 <- trajectory() %>% seize("server", 1) %>% timeout(function() rexp(1, 1.1)) %>% release("server", 1) env <- simmer() %>% add_resource("server", 1, mon=mon) %>% add_generator("customer", mm1, function() rexp(m, 1), mon=mon) %>% run(until=n)}
Very simple deterministic test to study the impact:
test_simmer <- function(n, delay) { test <- trajectory() %>% timeout(delay) simmer() %>% add_generator("test", test, at(1:n)) %>% run() %>% get_mon_arrivals()}test_simmer(5, 1)[,1:5]
## name start_time end_time activity_time finished## 1 test0 1 2 1 TRUE## 2 test1 2 3 1 TRUE## 3 test2 3 4 1 TRUE## 4 test3 4 5 1 TRUE## 5 test4 5 6 1 TRUE
Very simple deterministic test to study the impact:
test_simmer <- function(n, delay) { test <- trajectory() %>% timeout(delay) simmer() %>% add_generator("test", test, at(1:n)) %>% run() %>% get_mon_arrivals()}test_simmer(5, 1)[,1:5]
## name start_time end_time activity_time finished## 1 test0 1 2 1 TRUE## 2 test1 2 3 1 TRUE## 3 test2 3 4 1 TRUE## 4 test3 4 5 1 TRUE## 5 test4 5 6 1 TRUE
Original benchmark in the JSS paper:
Expr | Min | Mean | Median | Max |
---|---|---|---|---|
test_simmer(n, 1) | 429.8663 | 492.365 | 480.5408 | 599.3547 |
test_simmer(n, function() 1) | 3067.9957 | 3176.963 | 3165.6859 | 3434.7979 |
test_R_for(n) | 2053.0840 | 2176.164 | 2102.5848 | 2438.6836 |
Very simple deterministic test to study the impact:
test_simmer <- function(n, delay) { test <- trajectory() %>% timeout(delay) simmer() %>% add_generator("test", test, at(1:n)) %>% run() %>% get_mon_arrivals()}test_simmer(5, 1)[,1:5]
## name start_time end_time activity_time finished## 1 test0 1 2 1 TRUE## 2 test1 2 3 1 TRUE## 3 test2 3 4 1 TRUE## 4 test3 4 5 1 TRUE## 5 test4 5 6 1 TRUE
Original benchmark in the JSS paper:
Expr | Min | Mean | Median | Max |
---|---|---|---|---|
test_simmer(n, 1) | 429.8663 | 492.365 | 480.5408 | 599.3547 |
test_simmer(n, function() 1) | 3067.9957 | 3176.963 | 3165.6859 | 3434.7979 |
test_R_for(n) | 2053.0840 | 2176.164 | 2102.5848 | 2438.6836 |
Update with -DRCPP_USE_UNWIND_PROTECT
:
Expr | Min | Mean | Median | Max |
---|---|---|---|---|
test_simmer(n, 1) | 467.8971 | 481.213 | 476.1667 | 521.4916 |
test_simmer(n, function() 1) | 498.2631 | 583.777 | 561.6798 | 816.1343 |
test_R_for(n) | 1158.9348 | 1201.460 | 1196.7223 | 1244.4041 |
Generic yet powerful process-oriented Discrete-Event Simulation framework for R [1, 2]
Combines a robust and fast C++ simulation core with a rich and flexible R API
[1] Ucar I, Smeets B, Azcorra A (2019). “simmer: Discrete-Event Simulation for R.” Journal of Statistical Software, 90(2), 1-30. doi: 10.18637/jss.v090.i02.
[2] Ucar I, Hernández JA, Serrano P, Azcorra A (2018). “Design and Analysis of 5G Scenarios with simmer: An R Package for Fast DES Prototyping.” IEEE Communications Magazine, 56(11), 145-151. doi: 10.1109/MCOM.2018.1700960.
Generic yet powerful process-oriented Discrete-Event Simulation framework for R [1, 2]
Combines a robust and fast C++ simulation core with a rich and flexible R API
[1] Ucar I, Smeets B, Azcorra A (2019). “simmer: Discrete-Event Simulation for R.” Journal of Statistical Software, 90(2), 1-30. doi: 10.18637/jss.v090.i02.
[2] Ucar I, Hernández JA, Serrano P, Azcorra A (2018). “Design and Analysis of 5G Scenarios with simmer: An R Package for Fast DES Prototyping.” IEEE Communications Magazine, 56(11), 145-151. doi: 10.1109/MCOM.2018.1700960.
Broad set of activities, the basic building block; extensible via custom routines
Activities are chained into a trajectory, a common path for processes of the same type
Generic yet powerful process-oriented Discrete-Event Simulation framework for R [1, 2]
Combines a robust and fast C++ simulation core with a rich and flexible R API
[1] Ucar I, Smeets B, Azcorra A (2019). “simmer: Discrete-Event Simulation for R.” Journal of Statistical Software, 90(2), 1-30. doi: 10.18637/jss.v090.i02.
[2] Ucar I, Hernández JA, Serrano P, Azcorra A (2018). “Design and Analysis of 5G Scenarios with simmer: An R Package for Fast DES Prototyping.” IEEE Communications Magazine, 56(11), 145-151. doi: 10.1109/MCOM.2018.1700960.
Broad set of activities, the basic building block; extensible via custom routines
Activities are chained into a trajectory, a common path for processes of the same type
Generic yet powerful process-oriented Discrete-Event Simulation framework for R [1, 2]
Combines a robust and fast C++ simulation core with a rich and flexible R API
[1] Ucar I, Smeets B, Azcorra A (2019). “simmer: Discrete-Event Simulation for R.” Journal of Statistical Software, 90(2), 1-30. doi: 10.18637/jss.v090.i02.
[2] Ucar I, Hernández JA, Serrano P, Azcorra A (2018). “Design and Analysis of 5G Scenarios with simmer: An R Package for Fast DES Prototyping.” IEEE Communications Magazine, 56(11), 145-151. doi: 10.1109/MCOM.2018.1700960.
Broad set of activities, the basic building block; extensible via custom routines
Activities are chained into a trajectory, a common path for processes of the same type
From R. Shannon (1975), simulation is
the process of designing a model of a real system and conducting experiments with this model for the purpose either of understanding the behavior of the system or of evaluating various strategies [...] for the operation of the system.
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |