simmer 4.1.0

The 4.1.0 release of simmer, the Discrete-Event Simulator for R, is on CRAN. As per request in the mailing list, now get_global() is able to work inside a generator function. Moreover, the new add_global() method attaches a global attribute to a simulator.

library(simmer)

env <- simmer()

hello_sayer <- trajectory() %>%
  log_("hello world!") %>%
  set_global("interarrival", 1, mod="+")

generator <- function() get_global(env, "interarrival")

env %>%
  add_global("interarrival", 1) %>%
  add_generator("dummy", hello_sayer, generator) %>%
  run(7) %>%
  get_global("interarrival")
## 1: dummy0: hello world!
## 3: dummy1: hello world!
## 6: dummy2: hello world!
## [1] 4

Compared to plain global variables, these ones are automatically managed and thus reinitialised if the environment is reset.

env %>%
  reset() %>%
  get_global("interarrival")
## [1] 1
env %>%
  run(7) %>%
  get_global("interarrival")
## 1: dummy0: hello world!
## 3: dummy1: hello world!
## 6: dummy2: hello world!
## [1] 4

There has been a small refactoring of some parts of the C++ core, which motivates the minor version bump, but this shouldn’t be noticeable to the users. Finally, several bug fixes and improvements complete this release. See below for a complete list.

New features:

  • New getter get_selected() retrieves names of selected resources via the select() activity (#172 addressing #171).
  • Source and resource getters have been vectorised to retrieve parameters from multiple entities (as part of #172).
  • Simplify C++ Simulator interface for adding processes and resources (#162). The responsibility of building the objects has been moved to the caller.
  • New add_global() method to attach global attributes to a simulation environment (#174 addressing #158).

Minor changes and fixes:

  • Remove 3.8.0 and 4.0.1 deprecations (#170 addressing #165).
  • Fix get_global() to work outside trajectories (#170 addressing #165).
  • Fix rollback() with an infinite amount (#173).
  • Fix and improve schedules and managers (as part of #174).
  • Fix reset() to avoid overwriting the simulation environment (#175).

#Naukas18: Física, maestro

El pasado 14-15 de septiembre de 2018 se celebró la octava edición de Naukas Bilbao, segunda consecutiva en un repleto Palacio Euskalduna, con la receta a la que estamos acostumbrados: ciencia, escepticismo, humor y espectáculo administrados en pequeñas píldoras de 10 minutos, como una constante más del universo. Este año, tuvimos el honor de contar con Pedro Miguel Etxenike, Francis Mojica y un mensaje sorpresa del astronauta y ministro Pedro Duque, además de muchos de los habituales, colaboraciones y nuevas incorporaciones.

No me dedicaré a hacer una crónica del evento —podéis ver todas las charlas en Kosmos de EITB (a la que hay que agradecer, un año más, su apoyo y su excelente cobertura del evento) o, con mayor resolución, en EITB a la carta—, pero sí me gustaría, por fin, confesar algo que se repite cada año y que los ponentes no suelen comentar en público: Naukas Bilbao da mucha pereza.

Puede sonar sorprendente, pero tened en cuenta que el evento no ha parado de crecer a lo largo de los años, así como la comunidad Naukas y la calidad de las ponencias. Te encuentras a mitad del verano, hace calor, tienes tus propios marrones en el trabajo y necesitas descansar, pero hay que preparar una charla y hay que ensayar, porque pronto llega septiembre y hay que viajar a Bilbao. Por si fuera poco, muchos habituales se han quedado fuera del programa porque no había tiempo para todos. En definitiva, la responsabilidad y la presión que siente el ponente de Naukas van en aumento curso tras curso.

Este año, además, teníamos la responsabilidad adicional, y el privilegio, de tener a nuestra disposición nada más y nada menos que la Orquesta Sinfónica de la UPV/EHU y 40 minutos de un programa muy apretado. El resultado es que no quieres ir a Bilbao. “El año que viene me escaqueo”, piensas, “o voy solo de oyente”. Pero luego llegas, todo va como la seda, la respuesta del público es excelente, compartes buenos momentos con mucha gente y vuelves a ver a buenos amigos. Al final, vuelves a casa en una nube y deseando volver al año siguiente. Esta, para mí al menos, es otra constante del universo Naukas.

Solo me queda darle las gracias a los padres de la criatura, Javier, Antonio y Miguel, así como a Juan Ignacio Pérez y a la gente de GUK por la gestión y organización; a EITB, de nuevo, por la cobertura del evento, y a Doru Artenie, director de la EHUorkestra, por su disposición y trabajo. Este fue el resultado:

simmer 4.0.1

The 4.0.1 release of simmer, the Discrete-Event Simulator for R, is on CRAN since a couple of weeks ago. There are few changes, notably new getters (get_sources()get_resources()get_trajectory()) for simmer environments and some improvements in resource selection policies (see details in help(select)).

A new convenience function, when_activated, makes it easier to generate arrivals on demand, triggered from trajectories. Let us consider, for instance, a simple restocking pattern:

library(simmer)

restock <- trajectory() %>%
  log_("restock")

serve <- trajectory() %>%
  log_("serve") %>%
  activate("Restock")

env <- simmer() %>%
  add_generator("Customer", serve, at(1, 2, 3)) %>%
  add_generator("Restock", restock, when_activated()) %>%
  run()
## 1: Customer0: serve
## 1: Restock0: restock
## 2: Customer1: serve
## 2: Restock1: restock
## 3: Customer2: serve
## 3: Restock2: restock

Finally, this release leverages the new fast evaluation framework offered by Rcpp (>= 0.12.18) by default, and includes some minor improvements and bug fixes.

New features:

  • New getters (#159):
    • get_sources() and get_resources() retrieve a character vector of source/resource names defined in a simulation environment.
    • get_trajectory() retrieves a trajectory to which a given source is attached.
  • New resource selection policies: shortest-queue-availableround-robin-availablerandom-available (#156). These are the same as the existing non-available ones, but they exclude unavailable resources (capacity set to zero). Thus, if all resources are unavailable, an error is raised.

Minor changes and fixes:

  • Rename -DRCPP_PROTECTED_EVAL (Rcpp >= 0.12.17.4) as -DRCPP_USE_UNWIND_PROTECT (6d27671).
  • Keep compilation quieter with -DBOOST_NO_AUTO_PTR (70328b6).
  • Improve log_ print (7c2e3b1).
  • Add when_activated() convenience function to easily generate arrivals on demand from trajectories (#161 closing #160).
  • Enhance schedule printing (9c66285).
  • Fix generator-manager name clashing (#163).
  • Deprecate set_attribute(global=TRUE)get_attribute(global=TRUE) and timeout_from_attribute(global=TRUE) (#164), the *_global versions should be used instead.

Read the docs before questioning R’s defaults

The latest R tip in Win-Vector Blog encourages you to Use Radix Sort based on a simple benchmark showing a x35 speedup compared to the default method, but with no further explanation. In my opinion, though, the complete tip would be, instead, use radix sort… if you know what you are doing, because a quick benchmark shouldn’t spare you the effort of actually reading the docs. And here is a spoiler: you are already using it.

One may wonder why R’s default sorting algorithm is so bad, and why was even chosen. The thing is that there is a trick here, and to understand it, first we must understand the benchmark’s data and then read the docs. This is the function from the original code (slightly modified for subsequent reuse) that generates the data:

mk_data <- function(nrow, stringsAsFactors = FALSE) {
  alphabet <- paste("sym", seq_len(max(2, floor(nrow^(1/3)))), sep = "_")
  data.frame(col_a = sample(alphabet, nrow, replace=TRUE),
             col_b = sample(alphabet, nrow, replace=TRUE),
             col_c = sample(alphabet, nrow, replace=TRUE),
             col_x = runif(nrow),
             stringsAsFactors = stringsAsFactors)
}

set.seed(32523)
d <- mk_data(1e+6)

summary(d)
##     col_a              col_b              col_c          
##  Length:1000000     Length:1000000     Length:1000000    
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
##                                                          
##      col_x          
##  Min.   :0.0000002  
##  1st Qu.:0.2496717  
##  Median :0.4991010  
##  Mean   :0.4996031  
##  3rd Qu.:0.7494089  
##  Max.   :0.9999999
length(table(d$col_a))
## [1] 99

There are three character columns sampled from 99 symbols (sym_1sym_2, …, sym_99) and a numeric column sampled from a uniform. The first three columns are thus clearly factors, but they are not treated as such. Let’s see now what help(sort) has to tell us about the sorting method, which by default is method="auto":

The “auto” method selects “radix” for short (less than 2^31 elements) numeric vectors, integer vectors, logical vectors and factors; otherwise, “shell”.

So, as I said in the opening paragraph, you are already using radix sort, except for characters. Let’s see then what happens if we treat such columns as proper factors:

library(microbenchmark)

set.seed(32523)
d <- mk_data(1e+6, stringsAsFactors = TRUE)

timings <- microbenchmark(
  order_default = d[order(d$col_a, d$col_b, d$col_c, d$col_x), , 
                    drop = FALSE],
  order_radix = d[order(d$col_a, d$col_b, d$col_c, d$col_x,
                        method = "radix"), ,
                  drop = FALSE],
  times = 10L)

print(timings)
## Unit: milliseconds
##           expr      min       lq     mean   median       uq      max neval
##  order_default 289.4685 312.0257 388.5259 387.8308 418.2673 584.4771    10
##    order_radix 265.6491 321.8337 421.2072 376.1166 512.0047 667.0545    10
##  cld
##    a
##    a

Unsurprisingly, timings are the same, because R automatically selects "radix" for you when appropriate. But when is it considered appropriate and why isn’t it appropriate in general for character vectors? We should go back to the docs:

The implementation is orders of magnitude faster than shell sort for character vectors, in part thanks to clever use of the internal CHARSXP table.

However, there are some caveats with the radix sort:

  • If x is a character vector, all elements must share the same encoding. Only UTF-8 (including ASCII) and Latin-1 encodings are supported. Collation always follows the “C” locale.
  • Long vectors (with more than 2^32 elements) and complex vectors are not supported yet.

An there it is: R is doing the right thing for you for the general case. So let us round up the tip: enforce method="radix" for character vectors if you know what you are doing. And, please, do read the docs.

Mi tesis en 3 minutos

Desde 2016, la Universidad Carlos III de Madrid organiza el certamen Thesis Talk.

En Thesis Talk nuestros estudiantes de doctorado están invitados a presentar el qué, cómo y porqué de su proyecto de investigación a una audiencia no especializada en sólo 3 minutos, con una transparencia fija como único elemento de apoyo. […] Las presentaciones son grabadas y evaluadas por un jurado de expertos.

Thesis Talk se inspira en el concepto Three Minute Thesis (3MT), creado por la Universidad de Queensland (Australia) en 2008 y adoptado por más de doscientas universidades en todo el mundo.

Y aquí está mi participación, por la que recibí el tercer premio (y, no menos importante, me pagó el título).

El segundo premio fue para Juan Margalef por Las fronteras de la física, y el primer premio para Carolina Ortega por Matrimonios forzados en comunidades indígenas mexicanas, ¿tradición cultural o violencia de género?