simmer: un simulador de eventos discretos para R

R es un maravilloso lenguaje de programación principalmente enfocado a la estadística y la representación de datos que utilizo en mi día a día, tanto en mis labores de investigación como de docencia, aunque no me prodigo mucho al respecto por aquí. Al mismo tiempo, la simulación por eventos discretos, una de las técnicas por excelencia en mi campo (y otros, y a la cual dedico mis horas de docencia), no tenía hasta ahora representación en el universo R. Y digo hasta ahora porque, gracias al esfuerzo de Bart Smeets y el mío propio, el CRAN (Comprehensive R Archive Network, el repositorio oficial de paquetes de R) ya cuenta con simmer, un simulador de eventos discretos para R.

El paquete simmer es un proyecto creado originalmente por Bart al que yo me he unido como colaborador y ahora también mantenedor del paquete. Si alguien está interesado, lo puede instalar directamente desde CRAN con el instalador de paquetes de R o aquí tiene el repositorio de GitHub con las últimas novedades. Sugerencias y reportes de bugs, también en GitHub.

Aquí un pequeño ejemplo de funcionamiento (una introducción de uso más extensa puede encontrarse aquí):

[code language=»r»]
library(simmer)

coffees <- list(
espresso =function() runif(1, min=5, max=10),
macchiato=function() runif(1, min=6, max=12),
americano=function() runif(1, min=3, max=8)
)
coffee_prep_duration <- function()
sample(coffees, 1)[[1]]()

coffee_trajectory <- create_trajectory("I want coffee!") %>%
seize("barista", 1) %>%
timeout(coffee_prep_duration) %>%
release("barista", 1)

env <- simmer("coffee shop") %>%
add_resource("barista", 2) %>%
add_generator("caffeine addict",
coffee_trajectory,
function() abs(rnorm(1, 5, 2))) %>%
run(until=480)

plot_evolution_arrival_times(env, type = "flow_time")
[/code]

simmer

Refrigerando un Raspberry Pi

raspberry

El propósito de esta entrada es doble: 1) dejar anotados los pasos que he seguido por si en algún momento yo mismo necesito consultarlos y 2) que cualquier poseedor de un Raspberry Pi que quiera hacer algo así encuentre todo lo necesario reunido en un mismo lugar. El resto puede dejar de leer aquí mismo.

El Raspberry Pi, entre otras muchas cosas, es una excelente solución de bajo consumo y silenciosa para tener en casa un servidor personal más que digno funcionando las 24 horas. Sobre estas líneas veis el mío en su estado actual. Está conectado doblemente al router blanco que se ve detrás: lo alimenta a través de una salida USB y le proporciona conectividad Ethernet. Además, se ven dos unidades USB conectadas:

  • La de debajo, pequeña y blanca, que apenas se ve, contiene el sistema operativo. Antes lo tenía en la tarjeta SD, pero al poco tiempo se acabaron corrompiendo los archivos porque escribía bastante. Mi decisión fue dejar el boot en la SD, que se cambia muy de vez en cuando, y trasladar el resto del sistema a una unidad flash: ese USB. Desde entonces, ningún problema.
  • La unidad de arriba es simplemente de almacenamiento, tal y como se puede deducir de ese «128G».

Este servidor me proporciona acceso a la red de casa desde el exterior y, además, hace de centro de descargas y servidor de archivos, puesto que lo tengo funcionando ininterrumpidamente. El problema era que abrir un gran número de conexiones en redes P2P es exigente de cara al procesador y esto hacía que se calentara mucho: subía de los 70 ºC. A continuación, explico cómo añadir refrigeración mediante un ventilador con velocidad controlada en función de la temperatura.

En realidad es muy sencillo. Se puede optar por escoger un ventilador y modificar una caja de Raspberry o hacerse una a medida, pero ya hay soluciones precocinadas. Yo opté por este kit que podéis encontrar en eBay (por si en algún momento el enlace deja de estar disponible, algunas palabras clave: acrylic case with fan, heat sink, overclock kit). Contiene una caja de metacrilato a piezas, muy fácil de montar, con el agujero hecho y un microventilador de 0.5 W encajado. Dicha caja es muy compacta porque lleva el ventilador por dentro, muy cerquita de la placa, a diferencia de otras soluciones, y además la tapa es abatible (se puede abrir y cerrar una vez montada). También trae unos pequeños disipadores para pegar en los microcontroladores.

Con este kit sería más que suficiente. Conectando el ventilador al terminal de 3.3 V, la placa se mantiene a 35 ºC. No obstante, ni se requiere una temperatura tan baja ni es deseable que el ventilador esté permanentemente encendido a máxima velocidad, puesto que hace algo de ruido (poco, todo hay que decirlo). La solución es regular la velocidad en función de la temperatura. ¿Cómo? Con un simple transistor BJT como muestra la figura a continuación.

fan

Como se ve, el transistor NPN se coloca a continuación de la carga y su base se conecta al PIN 18 que nos proporciona una salida digital PWM, que no es más que una señal cuadrada cuyo valor máximo es 3.3 V y el mínimo es 0 V. Nótese que, cuando la señal se encuentra en su valor alto, el transistor tiene ambas uniones en polarización directa y trabaja en saturación: es un interruptor cerrado, la corriente fluye impulsando el ventilador. Por el contrario, cuando la señal se encuentra en su valor bajo, el transistor está en corte y no fluye corriente por la rama del ventilador.

Por tanto, basta con modular el porcentaje del tiempo que la señal digital aplicada a la base del transistor está en el valor alto, 3.3 V, para obtener el mismo porcentaje de la velocidad máxima del ventilador a dicha tensión. Y esto puede hacerse en función de la temperatura, con un programa que monitorice esta última y habilite la señal correspondiente a través del PIN 18. Afortunadamente, alguien ya ha escrito el código por nosotros y lo ha compartido amablemente. Lo he recogido aquí y el lugar de publicación original es este foro.

Descargar archivos recursivamente de un enlace compartido de Dropbox

La funcionalidad de enlaces compartidos de Dropbox es una característica muy útil, pero a veces sucede que la carpeta que queremos descargar contiene un volumen tan grande de archivos que

  • no podemos añadirla a nuestro Dropbox porque no tenemos suficiente espacio.
  • no permite descargarla como archivo comprimido.

Para esas ocasiones, dejo por aquí un pedazo de código en Python. Básicamente, se le pasa un directorio donde se desea descargar el contenido y la URL de la carpeta compartida. El resto lo hace él: recorre recursivamente la carpeta de Dropbox, crea una copia de la estructura de directorios y descarga todos los archivos (necesario wget, pero se puede sustituir por cualquier otra cosa). Que aproveche.

¿Es seguro el generador aleatorio de Linux? Torvalds responde

Los últimos documentos filtrados por Edward Snowden revelan que la agencia de ciberinteligencia estadounidense, la NSA, en su afán por espiar las comunicaciones cifradas, habría llegado a comprometer incluso el propio kernel de Linux a través de la empresa Intel. Explicaré aquí brevemente cuál es este problema que recorre Internet estos días y de qué manera ha quedado zanjado, de momento.

Qué es un generador de números aleatorios

La criptografía necesita números aleatorios para funcionar: secuencias no predecibles que sirvan para generar claves igualmente no predecibles y otros menesteres. Sin embargo, los ordenadores son más bien lo contrario de la aleatoriedad —como suelo decir, el ordenador hace lo que le mandas—, lo cual es un problema. El kernel de Linux, como cualquier otro sistema operativo, tiene un generador de números aleatorios, que en realidad es un generador pseudoaleatorio (léase lo suficientemente aleatorio, también como el de cualquier otro sistema), que es a lo que se puede aspirar con algo determinista como un ordenador.

Cuál es —supuestamente— el problema

El generador del kernel de Linux utiliza como fuente de aleatoriedad una instrucción de los procesadores Intel llamada RDRAND, que no deja de ser un componente hardware ajeno al kernel de Linux y, por tanto, difícilmente auditable. Al parecer, la NSA podría haber introducido una puerta trasera en dicha fuente de aleatoriedad. Esto quiere decir que las secuencias generadas con esos chips de Intel podrían ser predecibles para la NSA.

De confirmarse el problema, las consecuencias serían bastante gordas. Para empezar, no sería confiable ningún certificado o clave pública generada con hardware Intel desde la inclusión de la instrucción RDRAND.

La respuesta de Torvalds

Las discusiones en las listas de correo de desarrolladores de Linux han sido acaloradas durante los últimos días. Se llegó a abrir una petición en Change.org para que Linus Torvalds, el creador del kernel de Linux, eliminase RDRAND del generador. La respuesta de este ha sido tajante, tal y como nos tiene acostumbrados:

Where do I start a petition to raise the IQ and kernel knowledge of people? Guys, go read drivers/char/random.c. Then, learn about cryptography. Finally, come back here and admit to the world that you were wrong. Short answer: we actually know what we are doing. You don’t. Long answer: we use rdrand as one of many inputs into the random pool, and we use it as a way to improve that random pool. So even if rdrand were to be back-doored by the NSA, our use of rdrand actually improves the quality of the random numbers you get from /dev/random. Really short answer: you’re ignorant.

Resumiendo, RDRAND es solo una fuente más de aleatoriedad utilizada por el generador. Por tanto, aunque tuviera una puerta trasera, no serviría de nada y las secuencias no serían predecibles. Todo ello explicado con el peculiar estilo Torvalds.

¿La moneda está trucada?

Una moneda, idealmente, es un pequeño cilindro de altura despreciable y densidad homogénea que, sometida a un lanzamiento caótico dentro de la ineluctable acción de un campo gravitatorio, necesariamente se posa sobre una de sus bases mostrando la otra. Se dice entonces que la probabilidad de mostrar una base, la cara, es igual a la probabilidad de mostrar la otra, la cruz, e igual a 0,5. Así, si realizamos, digamos, 10 lanzamientos, tendremos la esperanza de obtener 5 caras. Ojo, esto solo significa que obtener 5 caras es más probable que obtener 4, o 6, o 3, o 7… No obstante, hay que tener presente que obtener cualquier resultado distinto de 5 es mucho más probable que obtener 5 en concreto.

Pero volviendo a la moneda, ¿podemos saber si una moneda en particular está trucada? ¿Cómo podemos determinar si se ajusta o no a esa idealización, fifty-fifty? ¿Cuántos lanzamientos necesitamos realizar para estar cuánto de seguros de ello?

Pues bien: existe la forma y se denomina inferencia estadística. Se pueden utilizar varios métodos, pero el más clásico es la aproximación Bayesiana. Se trata, básicamente, de tirar la moneda una y otra vez y contrastar los resultados con el conocimiento teórico que tenemos de la moneda ideal. En otras palabras, sabemos qué distribución de probabilidades tienen las caras, dados un número de tiradas y la probabilidad del suceso «cara». Mediante la inferencia estadística, por el contrario, determinamos qué probabilidad del suceso «cara» se ajusta mejor al número de caras que se van observando en tiradas sucesivas.

Como vale más una imagen que mil palabras, he preparado una pequeña animación. He tirado una moneda virtual 400 veces y he ido construyendo la evolución de la función de distribución a posteriori para la probabilidad de sacar cara. Si mi moneda virtual no está trucada, y conforme los lanzamientos aumentan, la distribución debería aproximarse cada vez más al valor 0,5, marcado con una línea a trazos. Se muestra, asimismo, la probabilidad de que la probabilidad de sacar cara —valga la redundancia— esté entre 0,45 y 0,55. Veamos:

coin

Como se intuye, ese pico crecerá de forma infinita. Eso significa que, con los suficientes lanzamientos, podemos estar todo lo seguros que queramos de que la probabilidad de sacar cara, se encuentra en un rango todo lo limitado que queramos. Digamos, por ejemplo, entre 0,499999 y 0,500001 con una probabilidad del 99,999 %.

Por cierto, el resultado final de 200 caras ha sido pura casualidad. Recordemos del principio del artículo que esto era más bien improbable.

Y para los más curiosos, aquí está el código de R con el que he generado la imagen:

[code language=»R»]
require(animation)

trials <- 400
tosses <- 0
heads <- 0

binom <- function(x) {
(tosses+1)*choose(tosses, heads) * x^heads * (1-x)^(tosses-heads)
}

int <- integrate(binom, 0, 0)

plot_curve <- function(frames) {
for (j in 1:frames) {
curve(binom, 0, 1, 1000, ylim=c(0,12), ylab=NA, xlab=NA, yaxt=’n’)
abline(v=.5, lty = 2)
text(.8, 10, paste("Tiradas:", tosses), pos=4)
text(.8, 9, paste("Caras     :", heads), pos=4)
text(0, 10, paste("P(0.45<x<0.55):", round(int$value*100), "%"), pos=4)
}
}

saveGIF({
# hold first frame
plot_curve(25)

for (i in 1:trials) {
# flip the coin
if (sample(0:1, 1))
heads <- heads + 1
tosses <- tosses + 1

# update results
int <- integrate(binom, .45, .55)
plot_curve(round(6-3*atan(tosses-5)))
}
# hold last frame
plot_curve(40)
}, interval=.04, nmax=trials)
[/code]