Random numbers
==============

Brian provides two basic functions to generate random numbers that can be used in model code and equations: ``rand()``,
to generate uniformly generated random numbers between 0 and 1, and ``randn()``, to generate random numbers from a
standard normal distribution (i.e. normally distributed numbers with a mean of 0 and a standard deviation of 1). All
other stochastic elements of a simulation (probabilistic connections, Poisson-distributed input generated by
`PoissonGroup` or `PoissonInput`, differential equations using the noise term ``xi``, ...) will internally make use of
these two basic functions.

For :ref:`runtime`, random numbers are generated by `numpy.random.rand` and `numpy.random.randn` respectively, which
uses a `Mersenne-Twister <https://en.wikipedia.org/wiki/Mersenne_Twister>`_ pseudorandom number generator. When the
``numpy`` code generation target is used, these functions are called directly, but for ``weave`` and ``cython``, Brian
uses a internal buffers for uniformly and normally distributed random numbers and calls the numpy functions whenever
all numbers from this buffer have been used. This avoids the overhead of switching between C code and Python code for
each random number. For :ref:`cpp_standalone`, the random number generation is based on "randomkit", the same
Mersenne-Twister implementation that is used by numpy. The source code of this implementation will be included in every
generated standalone project.

Seeding and reproducibility
---------------------------
Runtime mode
~~~~~~~~~~~~
As explained above, :ref:`runtime` makes use of numpy's random number generator. In principle, using `numpy.random.seed`
therefore permits reproducing a stream of random numbers. However, for ``weave`` and ``cython``, Brian's buffer
complicates the matter a bit: if a simulation sets numpy's seed, uses 10000 random numbers, and then resets the seed,
the following 10000 random numbers (assuming the current size of the buffer) will come out of the pre-generated buffer
before numpy's random number generation functions are called again and take into account the seed set by the user.
Instead, users should use the `seed` function provided by Brian 2 itself, this will take care of setting numpy's random
seed *and* empty Brian's internal buffers. This function also has the advantage that it will continue to work when the
simulation is switched to standalone code generation (see below). Note that random numbers are not guaranteed to be
reproducible across different code generation targets or different versions of Brian, especially if several sources of
randomness are used in the same `CodeObject` (e.g. two noise variables in the equations of a `NeuronGroup`). This is
because Brian does not guarantee the order of certain operations (e.g. should it first generate all random numbers for
the first noise variable for all neurons, followed by the random numbers for the second noise variable for all neurons
or rather first the random numbers for all noice variables of the first neuron, then for the second neuron, etc.) Since
all random numbers are coming from the same stream of random numbers, the order of getting the numbers out of this
stream matter.

Standalone mode
~~~~~~~~~~~~~~~
For :ref:`cpp_standalone`, Brian's `seed` function will insert code to set the random number generator seed into the
generated code. The code will be generated at the position where the `seed` call was made, allowing detailed control
over the seeding. For example the following code would generate identical initial conditions every time it is run, but
the noise generated by the ``xi`` variable would differ::

    G = NeuronGroup(10, 'dv/dt = -v/(10*ms) + 0.1*xi/sqrt(ms) : 1')
    seed(4321)
    G.v = 'rand()'
    seed()
    run(100*ms)

.. note::

    In standalone mode, `seed` will not set numpy's random number generator. If you use random numbers in the Python
    script itself (e.g. to generate a list of synaptic connections that will be passed to the standalone code as a
    pre-calculated array), then you have to explicitly call `numpy.random.seed` yourself to make these random numbers
    reproducible.

.. note::

    Seeding *should* lead to reproducible random numbers even when using OpenMP with multiple threads (for repeated
    simulations with the same number of threads), but this has not been rigorously tested. Use at your own risk.
