multithreading random cython

multithreading - Manera canónica de generar números aleatorios en Cython



random (1)

Creo que la manera más fácil de hacerlo es utilizar la biblioteca estándar C ++ 11 que proporciona buenos generadores de números aleatorios encapsulados y formas de usarlos . Por supuesto, esta no es la única opción, y podría envolver prácticamente cualquier biblioteca de C / C ++ adecuada (una buena opción podría ser usar cualquier biblioteca que numpy use, ya que es muy probable que ya esté instalada).

Mi consejo general es solo envolver los bits que necesita y no molestar con la jerarquía completa y todos los parámetros opcionales de la plantilla. A modo de ejemplo, he mostrado uno de los generadores predeterminados, alimentado a una distribución de flotación uniforme.

# distutils: language = c++ # distutils: extra_compile_args = -std=c++11 cdef extern from "<random>" namespace "std": cdef cppclass mt19937: mt19937() # we need to define this constructor to stack allocate classes in Cython mt19937(unsigned int seed) # not worrying about matching the exact int type for seed cdef cppclass uniform_real_distribution[T]: uniform_real_distribution() uniform_real_distribution(T a, T b) T operator()(mt19937 gen) # ignore the possibility of using other classes for "gen" def test(): cdef: mt19937 gen = mt19937(5) uniform_real_distribution[double] dist = uniform_real_distribution[double](0.0,1.0) return dist(gen)

(El -std=c++11 al comienzo es para GCC. Para otros compiladores puede que necesite ajustar esto. De todos modos, cada vez más c ++ 11 es un valor predeterminado, por lo que puede soltarlo)

Con referencia a sus criterios:

  1. Plataforma cruzada en cualquier cosa que admita C ++. Creo que la secuencia debe especificarse para que sea repetible.
  2. El hilo es seguro, ya que el estado se almacena por completo dentro del objeto mt19937 (cada hilo debe tener su propio mt19937 ).
  3. No GIL - es C ++, sin partes de Python
  4. Razonablemente fácil.

Editar: sobre el uso de discrete_distribution .

Esto es un poco más difícil porque los constructores para discrete_distribution son menos obvios sobre cómo envolver (implican iteradores). Creo que lo más fácil es ir a través de un vector C ++ ya que el soporte para eso está integrado en Cython y es fácilmente convertible a / desde una lista de Python

# use Cython''s built in wrapping of std::vector from libcpp.vector cimport vector cdef extern from "<random>" namespace "std": # mt19937 as before cdef cppclass discrete_distribution[T]: discrete_distribution() # The following constructor is really a more generic template class # but tell Cython it only accepts vector iterators discrete_distribution(vector.iterator first, vector.iterator last) T operator()(mt19937 gen) # an example function def test2(): cdef: mt19937 gen = mt19937(5) vector[double] values = [1,3,3,1] # autoconvert vector from Python list discrete_distribution[int] dd = discrete_distribution[int](values.begin(),values.end()) return dd(gen)

Obviamente eso es un poco más complicado que la distribución uniforme, pero no es imposible (y las partes desagradables podrían estar ocultas dentro de una función de Cython).

Cuál es la mejor manera de generar números aleatorios pseudo uniformes (un doble en [0, 1)] que es:

  1. Plataforma cruzada (idealmente con la misma secuencia de muestra)
  2. Thread safe (paso explícito del estado mutado del prng o usando un estado local de threads)
  3. Sin bloqueo GIL
  4. Fácilmente envolvente en Cython

Hubo una publicación similar hace más de 3 años sobre esto, pero muchas de las respuestas no cumplen todos los criterios. Por ejemplo, drand48 es específico de POSIX.

El único método que conozco, que parece (pero no está seguro) para cumplir con todos los criterios es:

from libc.stdlib cimport rand, RAND_MAX random = rand() / (RAND_MAX + 1.0)

Nota @ogrisel hizo la misma pregunta hace 3 años.

Editar

Llamar rand no es seguro para subprocesos. Gracias por señalar eso a @DavidW.