-1

I have a function and a subfunction and in each of them some random arrays are being generated. In order to make the result reproducible, I use seed. However I see strange cases.

When, I have a seed in the subfunction, the random numbers in the main function also get an effect from the seed in subfunction. And, there is not such effect from main function to subfunction. For example, consider the following code.

import random
from random import randint
import numpy as np


def randgene():

    a=np.random.randint(0,5,size=5)
    print "a in function", a
    np.random.seed(seed=15)
    b=np.random.randint(0,5,size=5)
    print "b in function", b

    d=np.random.choice(50, size=5, replace = False)
    print "d in function", d
    # np.random.seed(seed=None)

def main():

    print "d-without seed", np.random.choice(50, size=5, replace = False)
    print "a-without seed", np.random.randint(0,5,size=5)
    print "b-without seed", np.random.randint(0,5,size=5)
    f = randgene()    
    print "d-without seed", np.random.choice(50, size=5, replace = False)
    print "a-without seed", np.random.randint(0,5,size=5)
    print "b-without seed", np.random.randint(0,5,size=5)
    f = randgene()
    print "d-without seed", np.random.choice(50, size=5, replace = False)
    print "a-without seed", np.random.randint(0,5,size=5)
    print "b-without seed", np.random.randint(0,5,size=5)

    np.random.seed(seed=10)

    print "d-with seed", np.random.choice(50, size=5, replace = False)
    print "a-with seed", np.random.randint(0,5,size=5)
    print "b-with seed", np.random.randint(0,5,size=5)

    f = randgene()

    print "d-with seed", np.random.choice(50, size=5, replace = False)
    print "a-with seed", np.random.randint(0,5,size=5)

    f = randgene()

    print "d-with seed", np.random.choice(50, size=5, replace = False)
    print "a-with seed", np.random.randint(0,5,size=5)

if __name__ == '__main__':
    main()

For this code I get this result:

d-without seed [14 29  9 42 18]
a-without seed [3 0 0 3 4]
b-without seed [3 2 0 2 1]
a in function [2 3 1 2 3]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-without seed [ 8 21 32 39 11]
a-without seed [3 0 3 3 0]
b-without seed [1 2 2 1 4]
a in function [4 4 0 2 2]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-without seed [ 8 21 32 39 11]
a-without seed [3 0 3 3 0]
b-without seed [1 2 2 1 4]
d-with seed [37 23 44 42 47]
a-with seed [2 0 0 4 4]
b-with seed [0 0 2 4 2]
a in function [0 0 2 3 0]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-with seed [ 8 21 32 39 11]
a-with seed [3 0 3 3 0]
a in function [1 2 2 1 4]
b in function [0 4 0 4 3]
d in function [41 16 22 24 14]
d-with seed [ 8 21 32 39 11]
a-with seed [3 0 3 3 0]

in which you see d-with seed [ 8 21 32 39 11], a-with seed [3 0 3 3 0] repeated in main function, whenever I have called the subfunction. However, if I comment the line np.random.seed(seed=15) in the subfunction I get this result:

d-without seed [17 20 23 36 28]
a-without seed [3 1 1 2 0]
b-without seed [3 2 1 1 3]
a in function [1 2 2 0 4]
b in function [4 4 0 4 2]
d in function [ 9 46 19  7 47]
d-without seed [39 42 10 17  4]
a-without seed [2 3 0 2 4]
b-without seed [1 4 1 3 2]
a in function [1 1 3 3 2]
b in function [1 3 4 4 3]
d in function [ 0  2 45  5 19]
d-without seed [24 20 47  3 29]
a-without seed [3 0 3 3 3]
b-without seed [1 0 0 2 3]
d-with seed [37 23 44 42 47]
a-with seed [2 0 0 4 4]
b-with seed [0 0 2 4 2]
a in function [0 0 2 3 0]
b in function [4 4 0 1 1]
d in function [ 6 11 35  4  7]
d-with seed [19 47 43 38 15]
a-with seed [0 4 2 1 2]
a in function [1 2 1 3 2]
b in function [3 4 4 0 2]
d in function [38 31 17 43  2]
d-with seed [ 7 15 39  2 49]
a-with seed [3 4 1 4 0]

which apparently has no repeating. So, the seed in the main function has no effect in the subfunction. If I keep both np.random.seed(seed=15) and np.random.seed(seed=None) uncommented, I will get similar result. May someone explain me what is happening?

Thanks in Advance, Afshin

Afshin Oroojlooy
  • 1,326
  • 3
  • 21
  • 43

2 Answers2

2

The random number generators seed is used to set up its global state. That state is used for all numbers generated after that point (and each number generated causes the state to be changed). You can compare your example to this one using a global variable:

def print_and_increment(): # this is like generating a random number
    global x
    print("x was {}. Incrementing...".format(x))
    x += 1
    print("x is now", x)

def func():
    global x
    print("func before seed")
    print_and_increment()

    x = 10 # this is equivalent to calling seed()

    print("func after seed")
    print_and_increment()

x = 0  # seed
print("top")
print_and_increment()

func()

print("middle")
print_and_increment()

func()

print("bottom")
print_and_increment()

If you want your function to have its own seed that it always uses, but for it to not change the state of the random number generator used outside, you can use numpy.random.get_state and numpy.random.set_state.

Try something like this:

def randgene():

    prev_state = np.random.get_state()

    a=np.random.randint(0,5,size=5)
    print "a in function", a
    np.random.seed(seed=15)
    b=np.random.randint(0,5,size=5)
    print "b in function", b

    d=np.random.choice(50, size=5, replace = False)
    print "d in function", d
    # np.random.seed(seed=None)

    np.random.set_state(prev_state)

Now your function should have no effect on the external code's use of the random number generator.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
2

For local random states simply use numpy.random.RandomState instead of the global state set by numpy.random.seed.

An example:

import numpy as np

seed1 = 1
seed2 = 42

rs1 = np.random.RandomState(seed1)
rs2 = np.random.RandomState(seed2)
rs3 = np.random.RandomState(seed1)  # same seed value used for rs1

a = rs1.randint(0, 5, size=5)
b = rs1.randint(0, 5, size=5)

c = rs2.randint(0, 5, size=5)

e = rs3.randint(0, 5, size=5)
f = rs3.randint(0, 5, size=5)


eq = np.testing.assert_array_equal

try:
    eq(a, b, err_msg='OK, a should be != b')
except AssertionError:
    pass

try:
    eq(a, c, err_msg='OK, a should be != c')
except AssertionError:
    pass

eq(a, e, err_msg='ERROR')  # a and e are equal due to same seed
eq(b, f, err_msg='ERROR')  # b and f are equal due to same seed

print('OK')
mab
  • 2,658
  • 26
  • 36