Как создать семена NumPy SeedSequence во время параллельной генерации случайных чисел?

В документации NumPy по параллельному генерированию случайных чисел показано, как используйте SeedSequence для создания семян внуков (см. ниже).

from numpy.random import SeedSequence, default_rng

ss = SeedSequence(12345)

# Spawn off 10 child SeedSequences to pass to child processes.
child_seeds = ss.spawn(10)
streams = [default_rng(s) for s in child_seeds]

Дочерние объекты SeedSequence также могут порождаться для создания внуков и т. Д. Каждая SeedSequence занимает свою позицию в дереве порожденных объектов SeedSequence, смешанных с предоставленными пользователем начальными числами, чтобы генерировать независимые (с очень высокой вероятностью) потоки.

grandchildren = child_seeds[0].spawn(4)
grand_streams = [default_rng(s) for s in grandchildren]

Мой вопрос:

Следует ли мне использовать для создания следующего поколения семян:

 great_grandchildren = grandchildren[0].spawn(4)
 great_grand_streams = [default_rng(s) for s in great_grandchildren]

или он должен всегда ссылаться на child_seeds[0]:

great_grandchildren = child_seeds[0].spawn(4)
great_grand_streams = [default_rng(s) for s in great_grandchildren]

Контекст моего вопроса касается реализации начальных значений и функции, состоящей из объекта concurrent.futures.ProcessPoolExecutor, который использует начальные числа для каждого процесса в сценарии цикла while, который может быть бесконечным. Я хотел бы знать, является ли приведенный ниже правильный способ создания семян из SeedSequence при условии, что я уже использовал термины grandchildren и grand_streams, упомянутые в примере NumPy. Например:

 from numpy.random import SeedSequence, default_rng
 
 ss = SeedSequence(12345)
 
 # Spawn off 10 child SeedSequences to pass to child processes.
 child_seeds = ss.spawn(10)
 streams = [default_rng(s) for s in child_seeds]
 
 run_func1( streams ) #child_seeds is consummed

 grandchildren = child_seeds[0].spawn(4)
 grand_streams = [default_rng(s) for s in grandchildren]

 while True:
     run_concurrent_futures_ProcessPoolExecutor_func( grand_streams )
     if condition_not_met:
         grandchildren = grandchildren[0].spawn(4) #Do I use grandchildren[0] or child_seeds[0] to ensure randomness?
         grand_streams = [default_rng(s) for s in grandchildren]
     else:
         break

person Sun Bear    schedule 26.08.2020    source источник


Ответы (2)


Не важно. Вы строите дерево, структура которого не имеет значения, и разница только в том, будет ли ваше дерево трехуровневым или двухуровневым.

person John Zwinck    schedule 26.08.2020
comment
Извините, я не понимаю вашего ответа. Я добавил немного больше информации к своему вопросу. Не могли бы вы просмотреть их и обновить свой ответ? Спасибо. - person Sun Bear; 26.08.2020
comment
Вы не должны создавать новую SeedSequence каждый раз в цикле. Повторное заполнение одного и того же ГПСЧ бессмысленно. - person John Zwinck; 26.08.2020
comment
Я имел в виду создание семян из SeedSequence, а не создание нового SeedSequence. Вы неправильно поняли мой вопрос. Я не понимаю твоего ответа. Не могли бы вы ответить на мой вопрос прямо, чтобы помочь мне понять? Ваше здоровье. - person Sun Bear; 26.08.2020
comment
Позвольте мне задать вам вопрос: вы пытаетесь генерировать одни и те же случайные числа при каждом запуске своей программы? То есть, имеет ли для вас значение, что результаты воспроизводимы, или нормально, если они будут случайными и разными при каждом запуске? - person John Zwinck; 26.08.2020
comment
Я хочу начать свою программу с известным начальным числом и назначить разные начальные числа каждому процессу в каждом цикле while. В контексте использования объекта Python concurrent.futures.ProcessPoolExecutor в каждый процесс будут передаваться одни и те же начальные числа. Я пытаюсь этого избежать. - person Sun Bear; 26.08.2020
comment
Верно, поэтому вам нужно только spawn() одну SeedSequence для каждого процесса. Вам не нужно spawn() снова в рамках одного процесса, если он не запускает новые дочерние процессы. - person John Zwinck; 26.08.2020
comment
да. Отсюда у меня вопрос о том, как нереститься. Могу ли я появиться из child_seeds[0] или grandchildren[0]? - person Sun Bear; 26.08.2020
comment
Не важно. - person John Zwinck; 26.08.2020
comment
Почему это не важно? Я не понимаю. Если я повторно использую child_seeds[0] в цикле while, не получу ли я тот же образец начального числа, что и раньше? Я думал, что это имеет значение, т.е. great_grandchildren должен появиться из grandchildren. О рождении следующего поколения внуков не упоминалось в документации, следовательно, вопрос. - person Sun Bear; 26.08.2020
comment
Я заметил, что в примере NumPy написано grandchildren = child_seeds[0].spawn(4), а не grandchildren = ss.spawn(4) или grandchildren = ss[0].spawn(4). Отсюда и логическое обоснование моих мыслей. Вы можете объяснить свое мнение о том, почему не имеет значения, используется ли child_seeds[0] или grandchildren[0]? - person Sun Bear; 26.08.2020

spawn предназначен для создания независимых RNG для параллельных процессов. Однако у вас нет параллельного процесса: он последовательный, поскольку вы каждый раз проверяете условие. Так что неважно, что вы делаете.

Обратите внимание, что вы можете продолжать создавать новые последовательности из каждой последовательности, поэтому вы можете изменить свой код на:

from numpy.random import SeedSequence, default_rng

ss = SeedSequence(12345)

# Spawn off 10 child SeedSequences to pass to child processes.
child_seeds = ss.spawn(10)
streams = [default_rng(s) for s in child_seeds]

run_func1( streams ) #  child_seeds is consumed

while condition_not_met:
    child_seeds = ss.spawn(4)
    streams = [child_seeds (s) for s in grandchildren]
    run_concurrent_futures_ProcessPoolExecutor_func(streams)

Но на самом деле вам также нужно подумать о том, какая функция должна брать на себя ответственность за решение, сколько потоков необходимо.

from numpy.random import SeedSequence

ss = SeedSequence(12345)
run_func1(ss.spawn(1)[0]) #  creates as many child seeds as it needs

while condition_not_met:
    #  creates as many child seeds as it needs
    run_concurrent_futures_ProcessPoolExecutor_func(ss.spawn(1)[0])
person Nzbuu    schedule 09.11.2020