nifty8.random module#

Some remarks on NIFTy’s treatment of random numbers

NIFTy makes use of the Generator and SeedSequence classes introduced to numpy.random in numpy 1.17.

On first load of the nifty8.random module, it creates a stack of SeedSequence objects which contains a single SeedSequence with a fixed seed, and also a stack of Generator objects, which contains a single generator derived from the above seed sequence. Without user intervention, this generator will be used for all random number generation tasks within NIFTy. This means

  • that random numbers drawn by NIFTy will be reproducible across multiple runs (assuming there are no complications like MPI-enabled runs with a varying number of tasks), and

  • that trying to change random seeds via numpy.random.seed will have no effect on the random numbers drawn by NIFTy.

Users who want to change the random seed for a given run can achieve this by calling push_sseq_from_seed() with a seed of their choice. This will push a new seed sequence generated from that seed onto the seed sequence stack, and a generator derived from this seed sequence onto the generator stack. Since all NIFTy RNG-related calls will use the generator on the top of the stack, all calls from this point on will use the new generator. If the user already has a SeedSequence object at hand, they can pass this to NIFTy via push_sseq(). A new generator derived from this sequence will then also be pushed onto the generator stack. These operations can be reverted (and should be, as soon as the new generator is no longer needed) by a call to pop_sseq(). When users need direct access to the RNG currently in use, they can access it via the current_rng() function.

Example for using multiple seed sequences:

Assume that N samples are needed to compute a KL, which are distributed over a variable number of MPI tasks. In this situation, whenever random numbers need to be drawn for these samples:

  • each MPI task should spawn as many seed sequences as there are samples in total, using sseq = spawn_sseq(N)

  • each task loops over the local samples

    • first pushing the seed sequence for the global index of the current sample via push_sseq(sseq[iglob])`

    • drawing the required random numbers

    • then popping the seed sequence again via pop_sseq()

That way, random numbers should be reproducible and independent of the number of MPI tasks.

WARNING: do not push/pop the same SeedSequence object more than once - this will lead to repeated random sequences! Whenever you have to push SeedSequence objects, generate new ones via spawn_sseq().

class Context(inp)[source]#

Bases: object

Convenience class for easy management of the RNG state. Usage:

with ift.random.Context(seed|sseq):
    code using the new RNG state

At the end of the scope, the original RNG state will be restored automatically.

Parameters:

inp (int or numpy.random.SeedSequence) – The starting information for the new RNG state. If it is an integer, a new SeedSequence will be generated from it.

__init__(inp)[source]#
class Random[source]#

Bases: object

static normal(dtype, shape, mean=0.0, std=1.0)[source]#
static pm1(dtype, shape)[source]#
static uniform(dtype, shape, low=0.0, high=1.0)[source]#
current_rng()[source]#

Returns the RNG object currently in use by NIFTy

Returns:

the current Generator object (top of the generatir stack)

Return type:

Generator

getState()[source]#

Returns the full internal state of the module. Intended for pickling.

Returns:

state

Return type:

unspecified

pop_sseq()[source]#

Pops the top of the SeedSequence and generator stacks.

push_sseq(sseq)[source]#

Pushes a new SeedSequence object onto the SeedSequence stack. This also pushes a new Generator object built from the new SeedSequence to the generator stack.

Parameters:

sseq (SeedSequence) – the SeedSequence object to be used from this point

Notes

This function should only be used

  • if you only want to change the random seed once at the very beginning of a run, or

  • if the restoring of the previous state has to happen in a different Python function. In this case, please make sure that there is a matching call to pop_sseq() for every call to this function!

In all other situations, it is highly recommended to use the Context class for managing the RNG state.

push_sseq_from_seed(seed)[source]#

Pushes a new SeedSequence object derived from an integer seed onto the SeedSequence stack. This also pushes a new Generator object built from the new SeedSequence to the generator stack.

Parameters:

seed (int) – the seed from which the new SeedSequence will be built

Notes

This function should only be used

  • if you only want to change the random seed once at the very beginning of a run, or

  • if the restoring of the previous state has to happen in a different Python function. In this case, please make sure that there is a matching call to pop_sseq() for every call to this function!

In all other situations, it is highly recommended to use the Context class for managing the RNG state.

setState(state)[source]#

Restores the full internal state of the module. Intended for unpickling.

Parameters:

state (unspecified) – Result of an earlier call to getState.

spawn_sseq(n, parent=None)[source]#

Returns a list of n SeedSequence objects which are children of parent

Parameters:
  • n (int) – number of requested SeedSequence objects

  • parent (SeedSequence) – the object from which the returned objects will be derived If None, the top of the current SeedSequence stack will be used

Returns:

the requested SeedSequence objects

Return type:

list(SeedSequence)