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 Runtime code generation, random numbers are generated by numpy.random.rand and numpy.random.randn respectively, which uses a Mersenne-Twister pseudorandom number generator. When the numpy code generation target is used, these functions are called directly, but for 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 Standalone code generation, 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, Runtime code generation makes use of numpy’s random number generator. In principle, using numpy.random.seed therefore permits reproducing a stream of random numbers. However, for 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 Standalone code generation, 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.