Numpy: Реализация PCG, предоставленная Numpy, имеет значительную и опасную самокорреляцию.

Созданный на 20 мая 2020  ·  104Комментарии  ·  Источник: numpy/numpy

Генератор PCG, используемый Numpy, имеет значительную самокорреляцию. То есть для каждой последовательности, сгенерированной из начального числа, существует большое количество коррелированных неперекрывающихся последовательностей, начинающихся с других начальных значений. Под «коррелированными» я подразумеваю, что перемежая две такие последовательности и проверяя результат, вы получаете сбои, которые не проявлялись в каждой последовательности по отдельности.

Вероятность того, что два генератора из большого набора терминалов получат две из этих последовательностей, ничтожно мала. Почему это происходит с математической точки зрения, хорошо известно, но это подробно объясняется здесь: http://prng.di.unimi.it/pcg.pgp (см. «Подпоследовательности в одном генераторе»).

Чтобы показать эту проблему напрямую, я написал эту простую программу на C, повторно используя код Numpy: http://prng.di.unimi.it/intpcgnumpy.c . Программа принимает два 128-битных состояния двух генераторов (с одинаковой константой LCG или «потоком») в виде старших и младших битов, чередует их выходные данные и записывает их в двоичной форме. После того, как мы отправим его через PractRand, мы не увидим статистических ошибок, так как два потока должны быть независимыми. Но если попытаться начать с двух состояний с одинаковыми 64 младшими битами, вы получите:

./intpcgnumpy 0x596d84dfefec2fc7 0x6b79f81ab9f3e37b 0x8d7deae980a64ab0 0x6b79f81ab9f3e37b | stdbuf -oL ~ / svn / c / xorshift / practicerand / RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded
RNG_test с использованием PractRand версии 0.94
RNG = RNG_stdin, seed = unknown
набор тестов = развернутый, складной = дополнительный

rng=RNG_stdin, seed=unknown
length= 128 megabytes (2^27 bytes), time= 2.2 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(0+0,13-2,T)                  R= +27.6  p =  1.0e-13    FAIL
  BCFN(0+1,13-2,T)                  R= +68.0  p =  2.3e-34    FAIL !!!
  BCFN(0+2,13-3,T)                  R= +90.8  p =  8.8e-43    FAIL !!!
  BCFN(0+3,13-3,T)                  R=+120.6  p =  6.9e-57    FAIL !!!!
  DC6-6x2Bytes-1                    R=  +8.9  p =  4.0e-5   mildly suspicious
  DC6-5x4Bytes-1                    R= +15.7  p =  4.3e-9   very suspicious
  [Low1/8]BCFN(0+0,13-4,T)          R= +11.6  p =  4.9e-5   unusual
  ...and 1074 test result(s) without anomalies

Вы даже можете пойти ниже - вам просто нужны те же 58 младших бит:

./intpcgnumpy 0x596d84dfefec2fc7 0x0579f81ab9f3e37b 0x8d7deae980a64ab0 0x6b79f81ab9f3e37b | stdbuf -oL ~/svn/c/xorshift/practrand/RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded

[...]
rng=RNG_stdin, seed=unknown
length= 32 gigabytes (2^35 bytes), time= 453 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/16]FPF-14+6/32:cross        R= +11.6  p =  4.0e-10   VERY SUSPICIOUS
  [Low1/32]FPF-14+6/32:cross        R= +16.5  p =  3.2e-14    FAIL
  [Low1/32]FPF-14+6/16:cross        R= +12.8  p =  3.8e-11   VERY SUSPICIOUS
  [Low1/64]FPF-14+6/64:cross        R=  +6.8  p =  4.8e-6   mildly suspicious
  [Low1/64]FPF-14+6/32:cross        R=  +6.0  p =  1.9e-5   unusual
  [Low1/64]FPF-14+6/16:cross        R=  +5.5  p =  5.8e-5   unusual
  [Low4/32]FPF-14+6/64:all          R=  +5.8  p =  5.9e-5   unusual
  [Low4/32]FPF-14+6/32:(0,14-0)     R=  +7.7  p =  1.0e-6   unusual
  [Low4/32]FPF-14+6/32:(1,14-0)     R=  +7.7  p =  9.1e-7   unusual
  [Low4/32]FPF-14+6/32:all          R=  +6.5  p =  1.3e-5   unusual
  [Low4/64]FPF-14+6/64:all          R=  +5.9  p =  5.1e-5   unusual
  [Low4/64]FPF-14+6/64:cross        R=  +8.2  p =  3.0e-7   suspicious
  [Low4/64]FPF-14+6/32:(0,14-0)     R=  +7.6  p =  1.0e-6   unusual
  [Low8/64]FPF-14+6/64:(0,14-0)     R= +17.0  p =  2.2e-15    FAIL
  [Low8/64]FPF-14+6/64:(1,14-0)     R=  +9.1  p =  5.1e-8   mildly suspicious
  [Low8/64]FPF-14+6/64:all          R= +12.7  p =  2.1e-11   VERY SUSPICIOUS
  [Low8/64]FPF-14+6/32:(0,14-0)     R= +12.8  p =  1.7e-11   VERY SUSPICIOUS
  [Low8/64]FPF-14+6/32:all          R= +11.0  p =  9.3e-10   VERY SUSPICIOUS
  ...and 1696 test result(s) without anomalies

Обратите внимание, что для увеличения вероятности 50% того, что два генератора начнутся с двух коррелированных начальных чисел (выбранных случайным образом), вам понадобится всего около полумиллиона генераторов, запускающихся случайным образом (парадокс дня рождения). А если учесть вероятность того, что они не начинаются в одном и том же состоянии, но имеют значительные перекрывающиеся коррелирующие последовательности, вам нужно гораздо меньше.

Ни один толковый генератор из литературы так себя вести не будет. Вы можете состязательно выбрать любые два начальных состояния MRG32k3a, SFC64, CMWC, xoshiro256 ++ и т. Д., И пока вы генерируете неперекрывающиеся последовательности, вы не увидите ошибок, описанных выше. Это серьезный недостаток, который может возникнуть, когда несколько устройств используют генератор, и предполагается (как и должно быть), что попарно эти последовательности не должны показывать корреляцию. Корреляция может вызвать нежелательное поведение, которое трудно обнаружить.

Пожалуйста, хотя бы где-нибудь задокументируйте, что генератор не следует использовать на нескольких терминалах или в высоко параллельной среде.

То же самое может происходить с разными «потоками», поскольку последовательности, генерируемые LCG путем изменения аддитивной константы, одинаковы по модулю изменения знака и аддитивной константы. Вы можете увидеть обсуждение здесь: https://github.com/rust-random/rand/issues/907 и полное математическое обсуждение проблемы здесь: https://arxiv.org/abs/2001.05304 .

numpy.random

Все 104 Комментарий

@imneme , @bashtage , @rkern будут здесь авторитетами, но я думаю, что мы обошли это стороной , и поэтому мы предпочли интерфейс SeedSequence.spawn интерфейсу jumped . Например, это обсуждение было, когда мы обсуждали API. Пожалуйста, ознакомьтесь с советом здесь https://numpy.org/devdocs/reference/random/parallel.html и при необходимости предложите улучшения.

@mattip Это не имеет никакого отношения к прыжкам.

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

Я бы, вероятно, порекомендовал AESCounter всем, у кого есть AES-NI, или SPECK128 всем, у кого нет высокопараллельных настроек.

То же самое может происходить с разными «потоками», поскольку последовательности, генерируемые LCG путем изменения аддитивной константы, одинаковы по модулю изменения знака и аддитивной константы.

Можете ли вы это количественно оценить? Я могу воспроизвести сбои с использованием того же приращения, но мы заполняем приращение так же, как и состояние, и я еще не наблюдал сбоя с двумя разными случайными приращениями. Если приращения также должны быть тщательно построены, это повлияет на практическую частоту столкновений дней рождения.

https://gist.github.com/rkern/f46552e030e59b5f1ebbd3b3ec045759

❯ ./pcg64_correlations.py --same-increment | stdbuf -oL ./RNG_test stdin64 -tf 2 -te 1 -tlmaxonly -multithreaded
0x56b35656ede2b560587e4251568a8fed
0x93526034ed105e9e587e4251568a8fed
[
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 115244779949650410574112983538102603757,
            "inc": 137507567477557873606783385380908979143
        },
        "has_uint32": 0,
        "uinteger": 0
    },
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 195824235027336627448689568147458133997,
            "inc": 137507567477557873606783385380908979143
        },
        "has_uint32": 0,
        "uinteger": 0
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0x4bf19f7b
test set = expanded, folding = extra

rng=RNG_stdin64, seed=0x4bf19f7b
length= 128 megabytes (2^27 bytes), time= 3.0 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN_FF(2+0,13-3,T)               R= +59.9  p =  3.8e-28    FAIL !!!       
  BCFN_FF(2+1):freq                 R= +89.0  p~=   6e-18     FAIL !         
  BCFN_FF(2+2):freq                 R= +39.6  p~=   6e-18     FAIL !         
  BCFN_FF(2+3):freq                 R= +14.6  p~=   6e-18     FAIL !         
  BCFN_FF(2+4):freq                 R= +10.3  p~=   5e-11   very suspicious  
  DC6-9x1Bytes-1                    R=  +7.1  p =  5.6e-4   unusual          
  DC6-6x2Bytes-1                    R= +18.9  p =  1.0e-10   VERY SUSPICIOUS 
  DC6-5x4Bytes-1                    R= +11.2  p =  1.4e-6   suspicious       
  [Low4/16]BCFN_FF(2+0):freq        R= +19.5  p~=   6e-18     FAIL !         
  [Low4/16]FPF-14+6/16:all          R=  +5.6  p =  1.0e-4   unusual          
  [Low4/16]FPF-14+6/4:all           R=  +5.9  p =  4.6e-5   unusual          
  [Low4/32]BCFN_FF(2+0):freq        R=  +6.5  p~=   2e-5    unusual          
  [Low8/32]BCFN_FF(2+0):freq        R= +15.1  p~=   6e-18     FAIL !         
  [Low8/32]FPF-14+6/32:all          R=  +8.4  p =  2.5e-7   very suspicious  
  [Low8/32]FPF-14+6/32:all2         R=  +9.0  p =  7.8e-5   unusual          
  [Low8/32]FPF-14+6/16:(0,14-0)     R= +12.4  p =  4.5e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/16:all          R= +15.5  p =  5.2e-14    FAIL           
  [Low8/32]FPF-14+6/16:all2         R= +41.4  p =  2.6e-16    FAIL !         
  [Low8/32]FPF-14+6/4:(0,14-0)      R=  +6.9  p =  5.9e-6   unusual          
  [Low8/32]FPF-14+6/4:all           R=  +7.9  p =  6.6e-7   suspicious       
  ...and 871 test result(s) without anomalies

ОК, попробую еще раз.

В LCG нет нескольких потоков с модулем степени двойки. Многие верили в это в первые дни, и есть даже длинные старые статьи, в которых утверждается, что с этими «потоками» можно делать интересные вещи, но уже несколько десятилетий известно, что орбиты, получаемые при изменении констант, являются одинаковыми по модулю добавки постоянная и, возможно, изменение знака_. Самое дальнее, что я могу отследить, это

Марк Дж. Дерст, Использование линейных конгруэнтных генераторов для параллельной генерации случайных чисел,
Труды Зимней конференции по моделированию 1989 г., IEEE Press, 1989, стр. 462–466.

Итак, я написал другую программу http://prng.di.unimi.it/corrpcgnumpy.c, в которой вы можете установить:

  • Начальное состояние для ГПСЧ.
  • Начальное состояние для другого ГПСЧ.
  • Произвольная «постоянная потока» для первого ГПСЧ.
  • Произвольная «константа потока» для второго ГПСЧ (они должны быть как четными, так и нечетными; это ограничение можно снять с помощью дополнительных действий).
  • Фиксированное количество младших битов, которое мы установим во втором ГПСЧ, по существу, таким образом, чтобы он начинался с тех же битов первого ГПСЧ. Остальные биты будут взяты из начального состояния для второго предоставленного вами ГПСЧ.

Итак, это _ точно_ установка первой программы, но вы также можете выбрать константы.

./corrpcgnumpy 0x596d84dfefec2fc7 0x6b79f81ab9f3e37b 0xac9c8abfcb89f65f 0xe42e8dff1c46de8b 0x8d7deae9efec2fc7 0x6b79f81ab9f3e37b 0x06e13e5e8c92c843 0xf92e8346feee7a21 56 | stdbuf -oL ~/svn/c/xorshift/practrand/RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded

rng=RNG_stdin, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 113 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]BCFN(0+0,13-1,T)          R= +27.2  p =  4.0e-14    FAIL
  [Low1/8]DC6-6x2Bytes-1            R= +10.9  p =  4.4e-6   suspicious
  [Low1/64]DC6-5x4Bytes-1           R=  -6.4  p =1-1.4e-4   unusual
  [Low8/64]FPF-14+6/64:(0,14-0)     R=  +8.4  p =  2.2e-7   mildly suspicious
  [Low8/64]FPF-14+6/64:all          R=  +8.7  p =  1.2e-7   suspicious
  [Low8/64]FPF-14+6/32:(0,14-0)     R= +10.2  p =  5.1e-9   suspicious
  [Low8/64]FPF-14+6/32:all          R=  +9.4  p =  2.7e-8   very suspicious
  [Low8/64]FPF-14+6/16:all          R=  +5.8  p =  6.4e-5   unusual
  ...and 1439 test result(s) without anomalies

Таким образом, существует не менее 2 ^ 72 коррелированных подпоследовательностей, независимо от того, как вы выбираете «константы потока», точно так же, как в случае с той же константой.

И нам дается смехотворный провал в генераторе: даже если вместо точной начальной точки, которую я рассчитываю, вы будете использовать состояние немного до или после, корреляция все равно обнаружится. Вы можете легко изменить программу, добавив для этого дополнительный параметр.

Я повторяю еще раз: ни один из существующих современных генераторов из научной литературы не имеет такого неправильного поведения (конечно, LCG со степенью двойки имеет такое поведение, но, ради бога, это _не_ современный генератор).

Критика Сабастиано в отношении PCG рассматривается в этом сообщении блога от 2018 года.

Краткая версия заключается в том, что если вам разрешено изобретать определенные семена, вы можете продемонстрировать «плохое» поведение практически любого ГПСЧ. Несмотря на его заявление о том, что PCG в чем-то уникальна, на самом деле PCG довольно обычна - потоки PCG не хуже, чем, скажем, SplitMix, который является еще одним широко используемым ГПСЧ.

Это совершенно неверно. Чтобы доказать мою неправоту, покажите две коррелированные неперекрывающиеся последовательности из MRG32k3a или xoshiro256 ++.

Я никогда не говорил «неперекрывающиеся». Покажите мне доступный на данный момент тест для xoshiro256 ++. что два семени избегают перекрытия.

Напротив, у меня есть тест для PCG, который показывает, что показанные вами «корреляции», по сути, являются формой перекрытия.

Я не могу бороться с FUD, как «по существу» и «формой», но я изменил http://prng.di.unimi.it/intpcgnumpy.c, так что сначала он повторяет каждый PRNG 10 миллиардов раз и выходит с ошибкой. сообщение, если сгенерированная последовательность пересекает начальное состояние другого ГПСЧ. Это гарантирует, что первые 160 ГБ данных в Practrand поступают из неперекрывающихся последовательностей:

./intpcgnumpy 0x596d84dfefec2fc7 0x0579f81ab9f3e37b 0x8d7deae980a64ab0 0x6c79f81ab9f3e37b | stdbuf -oL ~/svn/c/xorshift/practrand/RNG_test stdin -tf 2 -te 1 -tlmaxonly -multithreaded
[...]
rng=RNG_stdin, seed=unknown
length= 64 gigabytes (2^36 bytes), time= 926 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]FPF-14+6/64:(0,14-0)      R=  +8.8  p =  8.7e-8   mildly suspicious
  [Low1/8]FPF-14+6/64:all           R=  +6.3  p =  2.1e-5   unusual          
  [Low1/16]FPF-14+6/64:(0,14-0)     R=  +7.6  p =  1.1e-6   unusual          
  [Low1/16]FPF-14+6/64:(1,14-0)     R=  +8.3  p =  2.9e-7   mildly suspicious
  [Low1/16]FPF-14+6/64:all          R=  +8.0  p =  5.8e-7   suspicious       
  [Low1/16]FPF-14+6/32:all          R=  +7.1  p =  3.9e-6   mildly suspicious
  [Low1/64]FPF-14+6/32:cross        R=  +7.1  p =  2.6e-6   mildly suspicious
  [Low4/32]FPF-14+6/64:(0,14-0)     R= +13.5  p =  4.3e-12   VERY SUSPICIOUS 
  [Low4/32]FPF-14+6/64:all          R=  +9.0  p =  5.9e-8   very suspicious  
  [Low4/64]FPF-14+6/64:(0,14-0)     R= +11.4  p =  3.8e-10  very suspicious  
  [Low4/64]FPF-14+6/64:all          R=  +8.0  p =  5.3e-7   suspicious       
  [Low4/64]FPF-14+6/32:(0,14-0)     R= +10.3  p =  3.6e-9   suspicious       
  [Low4/64]FPF-14+6/32:all          R=  +6.1  p =  3.2e-5   unusual          
  [Low8/64]FPF-14+6/64:(0,14-0)     R= +18.6  p =  8.4e-17    FAIL           
  [Low8/64]FPF-14+6/64:(1,14-0)     R= +11.4  p =  3.9e-10  very suspicious  
  [Low8/64]FPF-14+6/64:(2,14-0)     R=  +8.3  p =  2.8e-7   mildly suspicious
  [Low8/64]FPF-14+6/64:all          R= +15.3  p =  6.9e-14    FAIL           
  [Low8/64]FPF-14+6/32:(0,14-0)     R=  +7.8  p =  7.1e-7   unusual          
  [Low8/64]FPF-14+6/32:(1,14-0)     R=  +7.2  p =  2.7e-6   unusual          
  [Low8/64]FPF-14+6/32:all          R=  +5.8  p =  6.9e-5   unusual          
  ...and 1786 test result(s) without anomalies

Эти конкретные данные инициализации имеют всего 56 младших фиксированных битов, поэтому можно сгенерировать 2 ^ 72 коррелированных последовательности, перевернув старшие биты. Статистические сбои происходят после всего лишь 64 ГБ данных, показывая, что перекрытия не несут ответственности за корреляцию. Возможно, что с другими конкретными целевыми вариантами совпадение, конечно, произойдет до 64 ГБ - это конкретный пример. Но теперь легко проверить, что перекрытие не является проблемой - генератор имеет просто множество нежелательных внутренних корреляций.

Пожалуйста, соблюдайте кодекс поведения . Постарайтесь, чтобы ваши комментарии соответствовали указаниям быть «чуткими, гостеприимными, дружелюбными и терпеливыми» и «будьте осторожны в словах, которые мы выбираем. Мы осторожны и уважительны в общении».

Я никогда не говорил «неперекрывающиеся». Покажите мне доступный на данный момент тест для xoshiro256 ++. что два семени избегают перекрытия.

Что ж, это тривиально: определите длину потока, выполните итерацию и убедитесь, что два потока не пересекают начальное состояние. Это тот же код, который я использовал для отображения коррелированных потоков PCG в программе http://prng.di.unimi.it/intpcgnumpy.c , которые не перекрываются.

Несмотря на его заявление о том, что PCG в чем-то уникальна, на самом деле PCG довольно обычна - потоки PCG не хуже, чем, скажем, SplitMix, который является еще одним широко используемым ГПСЧ.

ИМХО, самокорреляция внутри PCG намного хуже. Для аддитивного генератора, лежащего в основе экземпляра SplitMix, нет результата, аналогичного впечатляющим результатам Дерста 1989 года о LCG.

Но очень легкие проблемы SplitMix известны, и JEP 356 предоставит новый класс разделяемых генераторов, LXM, пытающийся решить эти проблемы. Пора было бы двигаться дальше и заменить PCG чем-то менее ущербным.

Основная проблема известна обоим генераторам и связана с отсутствием смешивания состояний. Если вы измените бит _k_ состояния одного из этих генераторов, изменение никогда не будет распространяться ниже бита _k_. Этого не происходит в LCG с простым модулем, в F₂-линейных генераторах, генераторах CMWC и т. Д. Все остальные генераторы пытаются смешивать свое состояние как можно быстрее и в большей степени.

Уравнивание проблем PCG и SplitMix - отвлекающий маневр. SplitMix имеет очень простой базовый генератор, только аддитивный, но помимо этого есть очень мощная функция скремблирования: это 64-битный финализатор Appleby для хэш-функции MurmurHash3, который широко использовался в ряде контекстов и был улучшен Стаффордом (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html). Константы функции были обучены иметь особые, измеримые свойства схода лавины. Даже изменения в небольшом количестве битов имеют тенденцию распространяться на весь вывод. Другими словами, SplitMix стоит на плече гигантов.

Напротив, генераторы PCG, лежащие в основе LCG, имеют те же проблемы отсутствия микширования, но функции скремблирования представляют собой простую последовательность арифметических и логических операций, собранную автором без каких-либо теоретических или статистических гарантий. Если бы они были разработаны с учетом того факта, что все последовательности лежащей в основе LCG являются одинаковыми по модулю аддитивной константы и, возможно, изменения знака, можно было бы решить проблему.

Но автор понятия не имел, что последовательности можно так легко вывести одна из другой. Это легко увидеть из заявления в разделе 4.2.3 технического отчета PCG (https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf):

«Каждый выбор _c_ приводит к другой последовательности чисел, у которой нет ни одной из пар последовательных выходных данных, общих с другой последовательностью».

Это считается доказательством того, что последовательности различны, то есть лежащая в основе LCG предоставляет несколько потоков. Отрицательные результаты Дерста 1989 г. по этой теме нигде в статье не появляются. Как я отмечал ранее, по этим результатам все такие последовательности являются одинаковыми, по модулю аддитивной константы и, возможно, смены знака (для LCG с модулем мощности 2 максимальной эффективности, как это происходит в PCG).

Я уверен, что не цитировать результаты Дерста - это ошибка _bona fide_, но проблема в том, что как только вы убедитесь, что используемый вами LCG предоставляет «потоки», которые в некотором смысле «разные», когда это не так, вы в конечном итоге с генератором наподобие PCG, в котором для каждой подпоследовательности есть 2 ^ 72 неперекрывающихся коррелированных подпоследовательности, даже если вы измените «поток».

Спасибо всем за ваш вклад. На данный момент меня не интересуют бинарные суждения типа «PCG - это хорошо / плохо». Пожалуйста, используйте свои собственные форумы для таких обсуждений. То, что здесь по теме, - это то, что будет делать numpy, и это окончательное решение принадлежит разработчикам numpy. Мы ценим опыт, который вы все привносите в это обсуждение, но я хочу сосредоточить его на основных фактах, а не на окончательном решении. Я особенно ценю количественные утверждения, которые дают мне представление о том, какой запас у нас есть. Если мои предыдущие суждения были неправильными, то это потому, что я слишком быстро перешел к суждению, поэтому я был бы признателен за вашу помощь, чтобы избежать этого снова. Спасибо.

Обратите внимание, что для увеличения вероятности 50% того, что два генератора начнутся с двух коррелированных начальных чисел (выбранных случайным образом), вам понадобится всего около полумиллиона генераторов, запускающихся случайным образом (парадокс дня рождения).

@vigna Можете n -битов в 2**(n/2) элементах (плюс-минус коэффициент 2). Полмиллиона 2**19 , поэтому вы, кажется, утверждаете, что опасные корреляции начинаются примерно при 40-битном конфликте в младших битах, но я не видел доказательств того, что это практически наблюдаемо. Я протестировал пару, разделяющую младшие 40 бит, и получил 16 ТиБ в PractRand, прежде чем отменить тест. Если вы наблюдали сбой с 40-битным конфликтом, сколько ТиБ вам нужно было проверить, чтобы его увидеть?

Я убежден, что изменение шага не влияет на вероятность столкновения. Дальнейшее обсуждение достоинств «потоков PCG» не по теме. Использование этого обсуждения как предлога для неоднократных нападок на «автора» особенно нежелательно и является нарушением нашего кодекса поведения . Упорство будет означать, что нам придется действовать без вашего участия. Спасибо.

@imneme Похоже, это связано с проблемами, связанными с прыжком на величину, кратную большой степени 2. Когда я PCG64 с одинаковым приращением и разделяю нижние n бит, расстояние, которое я вычисляю между ними, кратно 1 << n . Похоже, что ваша более сильная функция вывода DXSM похоже, также разрешает это проявление. Я протестировал пару экземпляров PCG64DXSM которые без проблем разделяют инкремент и нижние 64 бита состояния до 2 ТиБ.

Хорошо, это смущает: это была половина миллиарда, а не полмиллиона. Одна буква может иметь большое значение. Прошу прощения за оплошность.

Но, как я уже сказал ранее, это вероятность достижения точно такого же начального состояния, а не вероятность значительного перекрытия коррелированных подпоследовательностей. Лично я предпочитаю использовать ГПСЧ без коррелированных подпоследовательностей, поскольку их много, но, как вы правильно сказали, решение только ваше.

Исправление функции скремблирования так, чтобы она имела лучшие свойства микширования, звучит как совершенно разумное решение.

Мое сообщение предназначалось только для разъяснения структурных различий между PCG и SplitMix, поскольку в предыдущем сообщении утверждалось, что у них были похожие проблемы, и я не думаю, что это правильное утверждение. Вы не можете написать программу вроде http://prng.di.unimi.it/corrpcgnumpy.c для SplitMix.

@rkern , вы спросили:

@imneme Похоже, это связано с проблемами, связанными с прыжками на кратное большое число 2. Когда я создаю пару экземпляров PCG64 с одинаковым приращением и разделяю нижние n бит, расстояние, которое я вычисляю между ними, кратно 1 << n . Похоже, что ваша более сильная функция вывода DXSM также разрешает это проявление. Я протестировал пару экземпляров PCG64DXSM которые совместно используют инкремент и нижние 64 бита состояния до 2 ТиБ без проблем.

Спасибо, что нашли и связались с веткой обсуждения в прошлом году. Да, как отмечает Себастьяно в своем ответе,

Исправление функции скремблирования так, чтобы она имела лучшие свойства микширования, звучит как совершенно разумное решение.

XSL-RR находится на более слабом конце. Напротив, как исходная функция вывода RXS-M из документа PCG, так и новая функция вывода DXSM больше способствуют скремблированию и поэтому не проявляют подобных проблем. DXSM (добавленный в исходный код PCG в этом коммите в прошлом году) был специально разработан, чтобы быть сильнее XSL-RR, но иметь аналогичную временную производительность (см. RXS-M, который медленнее). В прошлом году я довольно жестко тестировал DXSM, но через 67 дней у нас было продолжительное отключение электроэнергии, в результате которого был отключен сервер (разрядился аккумулятор ИБП) и закончился тестовый прогон, но на тот момент он неплохо зарекомендовал себя как в нормальных условиях. тестирование (проверено 128 ТБ вывода) и скачки 2 ^ 48 (проверено 64 ТБ вывода, поскольку он работает медленнее).

Если даже без разработки DXSM, RXS-M позаботился бы об этой проблеме, возникает вопрос, почему я когда-либо использовал более слабую перестановку XSL-RR вместо этого - почему бы всегда не использовать очень сильное битовое скремблирование в функции вывода? Ответ в том, что все сводится к циклам. Скорость имеет значение для людей, поэтому старайтесь не скремблировать намного больше, чем нужно.

Это проблема, с которой Себастьяно знаком, потому что его подход и мой имеют много общего. Каждый из нас придерживается давно устоявшегося подхода, который не справится с современными статистическими тестами (LCG в моем случае, и XorShift LFSR Marsaglia в его), и добавит функцию вывода скремблирования, чтобы исправить это. Мы оба стремимся сделать эту функцию вывода дешевой, и мы оба были немного пойманы, когда недостатки в основном генераторе, который мы пытаемся замаскировать с помощью нашей функции вывода, тем не менее проявляются. В его случае проявились проблемы с

Но в недавней работе, которая меня очень порадовала, он также показал, сколько проектов на основе LFSR имеют проблемы с весом (что отражает мою давнюю озабоченность), которые неадекватно маскируются их функциями вывода. Его собственные генераторы прошли его испытание, так что это кое-что, но еще в 2018 году, когда я смотрел на его новый xoshiro PRNG, мне казалось, что проблемы с весом от подчиненного генератора действительно прошли через его функцию вывода. С тех пор он переработал xoshiro с новой функцией вывода, и я надеюсь, что это поможет (он также внес некоторые другие изменения, так что, возможно, это также исправляет проблему с повторениями, выделенную этой тестовой программой ?).

Что касается его программ корреляции, еще в 2018 году, когда он разместил критику PCG на своем веб-сайте, который включал написанные им программы с различными проблемами (например, надуманный посев и т. Д.), Я написал ответ , содержащий кучу похожих программ. для других давно установленных ГПСЧ, включая corrsplitmix2.c который создает коррелированные потоки в SplitMix. Я не совсем уверен, что имеет в виду Себастьян, когда говорит, что это невозможно, но признаю, что у меня не было возможности внимательно изучить его программу испытаний, чтобы увидеть, отличается ли его новая программа существенно от тех, которые он писал пару лет назад.

Пожалуйста, простите меня за непонимание - но могу ли я попросить кого-нибудь дать краткое заключение на данном этапе? Существуют ли реальные обстоятельства, при которых стандартная PCG небезопасна? Каковы аргументы в пользу переключения на значение по умолчанию?

Самый простой путь - добавить некоторую документацию о приложениях с высокой (сверхвысокой?) Размерностью и вероятности коррелированных последовательностей.

Более сложный путь - заменить функцию вывода, которая нарушит поток. Я не уверен, насколько сильным является обещание default_rng . Документы, похоже, не предупреждают, что это может измениться, поэтому, вероятно, для изменения потребуется цикл устаревания. Для этого потребуется добавить новую функцию вывода в качестве автономного генератора бит (или настроить из PCG64, что было бы более разумно), а затем предупредить пользователей, что она изменится через год XXXX / выпуск Y.ZZ.

Самым трудным путем было бы найти новое кольцо по умолчанию. В первый раз это было непросто, и я не думаю, что за последние 18 месяцев что-то изменилось, чтобы двигать стрелку в каком-либо конкретном направлении.

Я открыл gh-16493 о модификации default_rng() , я не думаю, что это связано с этой проблемой, даже не уверен, что мы должны ее обсуждать, мы, вероятно, уже давно установили правила, а я просто не не помню.


Я не претендую на полное понимание этого обсуждения, но мне кажется, что нужно выяснить две вещи:

  1. Будучи уверен в том, что у нас достаточно прогресса, я должен доверять Роберту в этом вопросе, и сейчас мне кажется, что все должно быть в порядке, насколько нам известно? (То есть вероятность фактического столкновения, вероятно, смущающе мала даже в средах с величинами, превышающими все, что может использоваться NumPy? Может ли это гарантировать изменение значения по умолчанию в будущем - это другой вопрос.)
  2. Мы заявляем:

    и поддерживает [...], а также: math: 2^{127} потоки

    что, не зная точно, откуда взялось число, звучит так, как будто это может быть небольшое завышение с нашей стороны, и мы могли бы подумать, немного изменив его, чтобы быть совершенно правильным? Или ссылку на какой-то внешний ресурс с дополнительной информацией?

Проще всего прямо сейчас добавить PCG64DXSM BitGenerator , вариант PCG64 с «дешевым множителем» и более сильной функцией вывода DXSM . Я думаю, все согласны с тем, что это шаг вперед по сравнению с функцией вывода XSL-RR, которая есть сейчас в нашей реализации PCG64 , которая статистически работает лучше без ущерба для производительности во время выполнения. Это простое обновление в той нише, которую PCG64 обслуживает для BitGenerator которые мы предоставляем. Я думаю, мы должны добавить его вместе с PCG64 .

Между прочим, я предпочитаю, чтобы это был отдельный объект с именем BitGenerator а не как вариант для конструктора PCG64 . Такие параметры отлично подходят для randomgen , цель которого - предоставить множество различных алгоритмов и вариантов, но для numpy , я думаю, мы хотим, чтобы наш выбор был "захватывающим и готовым", поскольку столько, сколько можем.

Я не думаю, что мы действительно разработали политику внесения изменений в то, что предоставляет default_rng() . Когда я предложил это, я поднял идею, что одна из причин, по которой я предпочел, чтобы он просто поместил свои функции в конструктор Generator() заключалась в том, что мы могли отказаться от использования и перейти к функции с другим именем, если нам нужно. Однако в то время мы считали, что default_rng() может потребовать раскрыть множество деталей лежащего в основе BitGenerator , чего мы впоследствии избегали. Поскольку PCG64DXSM предоставляет тот же API (в частности, .jumped() ), что и PCG64 , единственное соображение, которое мы могли бы иметь, это то, что использование в качестве нового значения по умолчанию изменит битовый поток. Я думаю, что для нас было бы разумно следовать той же временной шкале, что и любая другая модификация потока, исходящая из методов Generator на NEP 19 (то есть в выпусках функций X.Y.0 ). Мы можем быть немного более осторожными, если захотим, и сначала выставить PCG64DXSM как доступный BitGenerator в 1.20.0 и документ (но не warn() слишком шумно, чтобы не повредить), что default_rng() изменится на использование в 1.21.0 .

В рамках добавления новой BG было бы неплохо обновить примечания к PCG64, чтобы начать служить в качестве руководства и обоснования предпочтения более нового варианта.

  1. Будучи уверен в том, что у нас достаточно прогресса, я должен доверять Роберту в этом вопросе, и сейчас мне кажется, что все должно быть в порядке, насколько нам известно? (То есть вероятность фактического столкновения, вероятно, смущающе мала даже в средах с величинами, превышающими все, что может использоваться NumPy? Может ли это гарантировать изменение значения по умолчанию в будущем - это другой вопрос.)

Это, наверное, слишком бойко. Это зависит от того, сколько потоков, сколько данных вы извлекаете из каждого потока и какова ваша терпимость к риску столкновения в день рождения. Я еще не дошел до того, чтобы превратить эту математику в понятный абзац, чтобы сделать простые для выполнения рекомендации, поэтому я давно не возвращался к этой проблеме Github. Однако я не думаю, что это проблема, которую нужно решать прямо сейчас.

Я напишу кое-что более позднее, но, как я вижу в этой ветке, мы восстанавливаем почву, которую прошли в прошлом году. Ничего не изменилось, кроме того, что Себастьяно обнаружил, что NumPy отправил PCG. В прошлом году команда NumPy провела более глубокий анализ и рассмотрела более правдоподобные сценарии.

Я бы предпочел обновить стандартную версию как можно быстрее - просто чтобы избежать путаницы. Я имею в виду, не ждать цикла депрекции.

@imneme - большое спасибо - я бы нашел более длинную вещь очень полезной.

Наверное, это лучший рассказ из прошлого поста об этом. Думаю, это обязательно стоит прочитать. Оттуда вы можете прокрутить ветку вверх, чтобы увидеть, как мы обсуждаем эти проблемы. Речь шла о PCG 32.

У меня в голове было то, что я хотел сказать, но я нахожу, глядя на сообщения год назад, я уже все это сказал, как здесь, так и в другом месте ( мой блог (2017), Reddit , мой блог снова (2018) , и в обсуждениях NumPy и т. д.)

О потоках и их самоподобии (для которых @rkern написал тестер потоковой зависимости ) я писал в прошлом году:

Как отмечалось в моем сообщении в

Что касается графика @mdickinson , для _every_ PRNG, который позволяет вам заполнить все его состояние, включая криптографические на основе счетчиков, мы можем придумать начальные значения, где у нас были бы PRNG, выходы которых были каким-то образом коррелированы (самый простой способ сделать это заключается в создании состояний ГПСЧ, находящихся на небольшом расстоянии друг от друга, но часто мы можем делать другие вещи, основываясь на понимании того, как они работают). И хотя ГПСЧ, которые не допускают заполнение полного состояния, могут избежать этой проблемы, это просто вводит новый, предоставляя практический доступ только к небольшой части их возможных состояний.

Правильный способ думать о потоках - это просто более случайное состояние, которое необходимо засеять. Использование небольших значений, таких как 1,2,3, обычно является плохой идеей для любых целей заполнения для _any_ PRNG (потому что, если все действительно отдают предпочтение этим начальным значениям, соответствующие им исходные последовательности будут перепредставлены).

Мы можем вообще не называть его потоком, а просто называть его состоянием. Это то, что Марсалья сделал в XorWow . Если вы посмотрите на код, последовательность Вейля counter совсем не взаимодействует с остальной частью состояния, и, как и LCG, и вариации в начальном значении действительно просто составляют добавленную константу.

Потоки SplitMix, PCG и XorWow - это то, что мы могли бы назвать «глупыми» потоками. Они представляют собой тривиальную повторную параметризацию генератора. Однако в этом есть ценность. Предположим, что без потоков наш ГПСЧ имел бы интересное близкое повторение 42, где 42 возникает несколько раз в быстрой последовательности и делает это только для 42 и ни для какого другого числа. Используя глупые потоки «только приращение» или «просто xor», мы фактически избежим жесткой привязки странного повтора к 42; все числа имеют поток, в котором они странно повторяются. (По этой причине исправление, которое я бы применил для устранения проблем с близким повторением в Xoshiro 256, - это смешивание последовательности Вейля.)

(Затем я углубился в этот комментарий и в этот .)

Я бы сказал, что ключевой вывод состоит в том, что почти для _ любого_ PRNG, с небольшим количеством времени и энергии вы можете изобрести патологические посевы. PCG находится в несколько необычном положении, поскольку у него есть кто-то, кому нравится работать с правдоподобно выглядящими семенами для PCG, в частности, с патологией ручной работы (например, Себастьяно). В результате этой работы я изменился и сделал то же самое как для его ГПСЧ, так и для других давних.

В общем, вы хотите инициализировать состояние PRNG чем-то, что выглядит «отчасти случайным». Это довольно универсально, даже если люди хотят иначе . Например, для любого LFSR PRNG (в стиле XorShift, Mersenne Twister и т. Д.) Вы не должны инициализировать его состоянием «все нули», потому что он просто останется там. Но даже состояния, которые в основном равны нулю, часто являются проблематичными для LFSR (явление, известное как нулевое положение, и почему люди, работающие с C ++, сделали seed_seq .). Если вы хотите сыграть в игру «давайте сделаем какой-нибудь надуманный посев», нетрудно создать набор инициализаций для 100 LFSR и сделать так, чтобы все они находились на расстоянии 1 Кбайт от нулевой точки. Все надуманные инициализации будут выглядеть достаточно невинно, но все они одновременно достигнут этого странного падения веса.

Если вы используете генератор PCG, который был инициализирован с разумной энтропией, все в порядке. Если вы хотите инициализировать его мусором, например 1, 2, 3, это действительно проблематично с любым PRNG . А с PCG в 99,9% случаев можно было бы даже использовать что-то вроде пошлого. У него нет ничего похожего на те проблемы, которые есть у LFSR.

Но DXSM определенно сильнее. Я думаю, что это лучше, иначе я бы этого не сделал, но стоит иметь некоторую перспективу и понимать, что на практике пользователи не столкнутся с проблемами с классической реализацией PCG64.

Я хотел бы отделить критику / защиту потоков PCG64 (через приращение LCG) от настоящего обсуждения. Хотя здесь присутствует определенная двойственность из-за математики LCG, это не основная проблема, которая изначально поднималась здесь. Кроме того, здесь нужно учитывать больше деталей, чем было в исходной критике Себастьяно, вашем ответе или старой мега-ветке. Возможно, эта связь более очевидна для экспертов, которые потратили больше времени на математику, но практические последствия теперь, по крайней мере, для меня яснее.

Я бы сказал, что ключевой вывод состоит в том, что почти для _ любого_ PRNG, с небольшим количеством времени и энергии вы можете изобрести патологические посевы.

Конечно, но решение, стоящее передо мной, зависит не от двоичного кода. Если я возьму слишком много чисел из конечного ГПСЧ, в конечном итоге PractRand выяснит это. Этот двоичный факт не отменяет алгоритм ГПСЧ. Отказ от этого двоичного кода и установление концепции запаса по объему было одной из вещей, которые я действительно оценил в оригинальном документе PCG. Учитывая патологию, порожденную противоборством, мы можем посмотреть, как часто эта патология может возникать случайным образом из-за хорошего посева энтропии. Я хочу дать количественную оценку и превратить это в практический совет для пользователей.

Учитывая два состояния, которые разделяют младшие 58 битов и одно и то же приращение (мы вставим в это булавку), чередование экземпляров PCG64 XSL-RR из этих состояний демонстрирует практически наблюдаемые сбои в PractRand на уровне около 32 ГиБ. Я думаю, что разумно этого избежать. Итак, давайте возьмем это за основу и посмотрим, как часто это возникает при хорошем заполнении энтропии. К счастью, эта состязательная схема поддается вероятностному анализу (не все так дружелюбны). Для экземпляров n вероятность того, что любые 2 будут использовать одни и те же младшие 58 бит, равна n**2 / 2**58 , плюс-минус 2 для двойного подсчета. Таким образом, при полмиллиарда экземпляров высока вероятность того, что есть одна такая пара, которая не сможет выполнить PractRand при чередовании. Полмиллиарда - это много! По моему мнению, мы, вероятно, никогда не увидим программу numpy которая пытается создать такое количество экземпляров PCG64 . Тогда numpy , вероятно, будет неправильным инструментом.

Я думаю, что также разумно избегать начальных состояний, последующие отрисовки которых будут пересекать любое из нижних 58-битных состояний столкновения других начальных состояний. Я все еще пытаюсь обдумать логику этого, но я думаю, что длина влияет на вероятность линейно, а не квадратично. Если я прав, и я хочу извлечь 16 ГиБ из каждого экземпляра ( 2**28 рисует), то есть столько, сколько мы извлекли из каждой пары, показавшей сбои PractRand, то я могу работать только с примерно 2**15 экземпляров, или около 32 тыс., Прежде чем станет вероятным наблюдение пересечения. Это все еще довольно много экземпляров для Python! А общий объем сгенерированных данных составляет около полпетабайта, что очень много! Но это на горизонте практичности, и если я хочу, чтобы вероятность была низкой, а не ниже половины, я должен снизить одну из них. Меня эти цифры не особенно беспокоят; Я не думаю, что в реальных программах numpy могут возникнуть проблемы при использовании PCG64 с функцией вывода XSL-RR. Но некоторые приложения могут начать приближаться (например, большие запуски распределенного обучения с подкреплением ).

Давайте вытащим этот вывод инкремента и рассмотрим его. Я думаю, будет справедливо сказать, что с функцией вывода XSL-RR также энтропийное заполнение приращения в дополнение к состоянию не меняет этот конкретный анализ. Кажется, что для любой данной пары приращений с засеваемой энтропией существует одинаковое количество практически конфликтующих состояний. Конкретная процедура преднамеренного построения этих состояний выглядит более сложной, чем использование тех же самых младших 58 бит, но похоже, что количество конфликтующих состояний одинаково, поэтому вычисления вероятности остаются такими же. Это не характерно для схемы PCG в целом. Функция вывода DXSM кажется достаточно сильной, поэтому изменение приращения (даже с простым +2 ) кажется достаточным, чтобы противостоять даже наихудшему состоянию для основного LCG (когда метрика расстояния дает 0 ), по крайней мере, насколько я потрудился протестировать с PractRand.

В заключение я хочу повторить то, в чем мы все, кажется, полностью согласны: PCG64DXSM - хорошая идея. По крайней мере, его улучшенные статистические свойства упрощают ментальные модели, которые я чувствую себя обязанным документировать, и все, что означает, что мне нужно писать меньше документации, хорошо в моей книге.

Потоки все еще актуальны, потому что проблема возникает только в том случае, если у нас есть генераторы в одном потоке.

Но при каких обстоятельствах у них будут те же самые младшие 58 битов и они будут в одном потоке? Есть ли вариант использования, когда это произойдет?

Один довольно реалистичный случай, о котором я знаю, - это тот, о котором мы говорили в прошлом году (когда мы говорили о jumped ), и я говорил в этом посте, на который я ссылался ранее.

Потоки все еще актуальны, потому что проблема возникает только в том случае, если у нас есть генераторы в одном потоке.

К сожалению, это не относится к XSL-RR. Рассмотрим два экземпляра PCG64 XSL-RR. Мы произвольно заполняем энтропию инкрементами и энтропией зарождаем одно из состояний. Мы можем создать плохие состояния 2**70 для другого экземпляра PCG64 которые не работают с PractRand, таким же образом, как и ошибка того же приращения с тем же нижним 58-битным состоянием. Это просто сделать сложнее, чем в случае того же приращения. Вместо того, чтобы разделять младшие 58 бит в качестве первого состояния с другим приращением, он разделяет младшие 58 бит состояния, которое находится на расстоянии 0 (в соответствии с вашей мерой расстояния LCG) от первого экземпляра, учитывая приращения. У меня есть конструктивное доказательство (код Python), но сейчас мне нужно лечь спать, а завтра убрать его.

@rkern , хорошее замечание. Признаюсь, я не тестировал этот сценарий, чтобы увидеть, как он работает.

Я бы сказал, что ключевой вывод состоит в том, что почти для _ любого_ PRNG, с небольшим количеством времени и энергии вы можете изобрести патологические посевы. PCG находится в несколько необычном положении, поскольку у него есть кто-то, кому нравится работать с правдоподобно выглядящими семенами для PCG, в частности, с патологией ручной работы (например, Себастьяно). В результате этой работы я изменился и сделал то же самое как для его ГПСЧ, так и для других давних.

Как я уже заметил, это неверно. Я не знаю ни одного примера пары коррелированных неперекрывающихся последовательностей, скажем, из xoshiro256 ++, которые вы можете легко найти в PCG.

ГПСЧ, которые быстро смешивают все свое состояние, не имеют этой проблемы. Если вы можете предоставить программу, генерирующую две неперекрывающиеся последовательности из xoshiro256 ++, которые коррелированы, как в примерах, которые я разместил здесь, сделайте это.

Что касается его программ корреляции, еще в 2018 году, когда он разместил критику PCG на своем веб-сайте, который включал написанные им программы с различными проблемами (например, надуманный посев и т. Д.), Я написал ответ , содержащий кучу похожих программ. для других давно установленных PRNG, включая corrsplitmix2.c который создает коррелированные потоки в SplitMix. Я не совсем уверен, что имеет в виду Себастьян, когда говорит, что это невозможно, но признаю, что у меня не было возможности внимательно изучить его программу испытаний, чтобы увидеть, отличается ли его новая программа существенно от тех, которые он писал пару лет назад.

Цитированная выше программа _выбирает потоки_. Это, очевидно, легко написать.

Но это не имеет ничего общего с проблемами PCG. Программа, которую я предоставил, позволяет _user_ выбирать потоки, а затем показывает корреляцию.

Я снова предлагаю @inmeme предоставить программу для SplitMix, в которой пользователь может произвольно выбирать два разных потока, произвольное начальное состояние первого генератора, и _ тогда_ программа находит коррелированную последовательность в другом генераторе, например http: / /prng.di.unimi.it/corrpcgnumpy.c делает.

Если позволить пользователю выбирать поток произвольно, это демонстрирует гораздо более сильную форму корреляции.

Как я уже заметил, это неверно. Я не знаю ни одного примера пары коррелированных неперекрывающихся последовательностей, скажем, из xoshiro256 ++, которые вы можете легко найти в PCG.

Кажется, мы здесь говорим мимо друг друга. Я не говорил, что могу придумать коррелированные, неперекрывающиеся последовательности для любого ГПСЧ, я сказал, что могу придумать патологические посевы в целом, как показано с помощью различных программ корреляции, которые я написал ранее, и других подобных как плохой повторяет демонстрационную программу для Xoshiro ** .

Кроме того, ГПСЧ, которые не смешивают все свое состояние, имеют долгую историю, включая XorWow, генераторы в числовых рецептах и ​​т.д. последовательность, поскольку она создает огромное количество подобных генераторов.

Кажется, мы здесь говорим мимо друг друга. Я не говорил, что могу придумать коррелированные, неперекрывающиеся последовательности для любого ГПСЧ, я сказал, что могу придумать патологические посевы в целом, как показано с помощью различных программ корреляции, которые я написал ранее, и других подобных как плохой повторяет демонстрационную программу для Xoshiro ** .

Пожалуйста, постарайтесь поддерживать обсуждение на техническом уровне. «Патологический» не имеет математического значения.

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

Если вы рассмотрите две перекрывающиеся последовательности, они будут коррелированы для каждого генератора, даже для криптографического, просто потому, что одни и те же выходные данные будут происходить дважды после перекрытия последовательностей, и любой разумный тест обнаружит это.

«Патологический посев» с использованием перекрывающихся последовательностей - тривиальная задача для каждого генератора (независимо от того, что означает «патологический»).

Еще раз, поскольку вы утверждаете, что нашли корреляцию, аналогичную PCG (в которой последовательности не перекрываются, как показывает тест) в других генераторах, можете ли вы предоставить пару коррелированных неперекрывающихся последовательностей, скажем, из xoshiro256 ++ или SFC64 ?

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

Можете ли вы указать на литературу для этого определения корреляции, чтобы я мог убедиться, что я остаюсь «технически правильным» и в этом отношении?

Себастьяно, ты все время хочешь, чтобы я ответил на вызов, поставленный на твоих условиях. То, что вы указываете, относится к внутренним свойствам LCG, где есть самоподобие. Вы не найдете такой же проблемы в хаотическом ГПСЧ или в LFSR.

Но для других ГПСЧ будут и другие слабые места.

У LFSR есть нулевой диапазон, плохие состояния, проблемы с весом, линейность и, как мы узнали из ваших попыток с xoshiro, иногда другие странности, такие как странные проблемы с повторами.

Хаотические ГПСЧ подвержены риску коротких циклов (хотя те, у которых есть счетчик, этого избегают - последовательности Вейля FTW!) И присущей им смещенности.

Если последовательности пересекаются, как я уже писал, тест _ всегда_ терпит неудачу. Вам не нужна литература, чтобы понять, что тест, который _ всегда терпит неудачу, не является тестом.

Еще раз, поскольку вы утверждаете, что нашли корреляцию, аналогичную PCG (в которой последовательности не перекрываются, как показывает тест) в других генераторах, можете ли вы предоставить пару коррелированных неперекрывающихся последовательностей, скажем, из xoshiro256 ++ или SFC64 ?

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

Проще всего прямо сейчас добавить PCG64DXSM BitGenerator , вариант PCG64 с «дешевым множителем» и более сильной функцией вывода DXSM . Я думаю, все согласны с тем, что это шаг вперед по сравнению с функцией вывода XSL-RR, которая есть сейчас в нашей реализации PCG64 , которая статистически работает лучше без ущерба для производительности во время выполнения. Это простое обновление в той нише, которую PCG64 обслуживает для BitGenerator которые мы предоставляем. Я думаю, мы должны добавить его вместе с PCG64 .

Обратите внимание, что у 64-битных «дешевых умножителей» есть доказуемые недостатки. Это давно известно:

W. Hörmann и G. Derflinger, Портативный генератор случайных чисел, хорошо подходящий для
метод отбраковки, ACM Trans. Математика. Софтв. 19 (1993), нет. 4, 489–495.

В общем, множители, меньшие квадратного корня из модуля, имеют собственные ограничения на их спектральный показатель f₂.

Предел можно легко преодолеть, используя 65-битный умножитель, который компилятор преобразует просто с помощью дополнительной операции «сложения», возможно, даже не изменяя скорость генератора.

Мы с Гаем Стилом немного поработали над этой проблемой и опубликовали таблицы спектральных оценок для дешевых множителей разного размера: https://arxiv.org/pdf/2001.05304.pdf . Чем больше, тем лучше, но существует доказанный разрыв от 64 до 65 бит (для LCG со 128 битами состояния).

Например, из Таблицы 7 статьи вы получите 0x1d605bbb58c8abbfd, который имеет оценку f₂ 0,9919. Никакой 64-битный множитель не может превышать 0,9306 (теорема 4.1 в статье).

После смешивания и всего остального улучшение показателя f₂ может остаться совершенно незамеченным со статистической точки зрения. Но, учитывая значительное улучшение, которое вы получаете для наиболее релевантного измерения с помощью всего лишь дополнительной операции добавления, я думаю (ну, мы думаем, иначе мы бы не написали статью), это того стоит.

Себастьяно, ты все время хочешь, чтобы я ответил на вызов, поставленный на твоих условиях. То, что вы указываете, относится к внутренним свойствам LCG, где есть самоподобие. Вы не найдете такой же проблемы в хаотическом ГПСЧ или в LFSR.

Вау, чтобы добраться туда, потребовалось время!

У LFSR есть нулевой диапазон, плохие состояния, проблемы с весом, линейность и, как мы узнали из ваших попыток с xoshiro, иногда другие странности, такие как странные проблемы с повторами.

Я полностью согласен - поэтому их нужно перемешивать. Обратите внимание, что LFSR и F₂-линейные генераторы - разные вещи; связанные, но разные.

«Странные проблемы с повторами» - это, как обычно, нетехнический термин, который я не могу комментировать.

Хаотические ГПСЧ подвержены риску коротких циклов (хотя те, у которых есть счетчик, этого избегают - последовательности Вейля FTW!) И присущей им смещенности.

[Обновление: я пропустил наблюдение за счетчиком в скобках, поэтому я обновляю свой комментарий.]

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

«Странные проблемы с повторами» - это, как обычно, нетехнический термин, который я не могу комментировать.

Кажется странным не иметь возможности комментировать, потому что я использовал неправильный жаргон - запустите эту программу, а затем не стесняйтесь просветить меня, как лучше всего описать проблему на правильном жаргоне, а затем предоставить любые комментарии, которые кажутся подходящими. Я мог представить, что вы с Дэвидом Блэкманом обсудили бы этот вопрос, когда он впервые обнаружился, потому что я переписывался с ним по этому поводу, но я никогда не видел, чтобы вы его комментировали.

Обсуждение ГПСЧ не в numpy не по теме. Пожалуйста, используйте свой собственный форум, чтобы продолжить это обсуждение. Спасибо.

@rkern - этот критерий кажется немного строгим. Если в реализации Numpy есть недостаток, который не разделяется другими реализациями, это кажется разумным обсудить.

Я могу подтвердить, что обмен, о котором я говорю, не помогает мне принимать решения, которые стоят перед нами в этом вопросе. Пока мы в этом не добьемся прогресса, мне нужно, чтобы разговор оставался сосредоточенным.

Я думаю, полезно понимать более широкий контекст. Себастьяно кое-что понимает в PCG и годами выступал против него. Я думаю, что в наши дни некоторые люди могут смотреть на нас обоих, закатывать глаза и говорить «вы оба такие же плохие, как и друг друга», потому что я также критиковал его ГПСЧ, но на самом деле я сделал это только после того, как он стал заявлять, что Я пытался что-то скрыть, никогда не рассказывая о его вещах (когда на самом деле у меня просто не было времени / желания - на самом деле, я просто предполагал, что они в порядке).

Тем не менее, его критика полезна, и я рад, что он решил провести большую часть своей жизни, размышляя о моей работе, но важно понимать, что его тесты носят враждебный характер. Он использует знание структуры PCG для разработки механизмов, которые могут быть введены в тестеры ГСЧ и не пройдут тесты.

Учитывая, как это может выглядеть пугающе, кажется разумным показать, что подобный состязательный подход также может сбить с толку множество других генераторов, и что многие из опасений, которые он поднимает по поводу PCG, применимы и к другим схемам генерации, как я наблюдал в отношении схем генерации. как XorWow, и я часто использую SplitMix в качестве примера, как я сделаю ниже. (Я полагаю, что никто из нас так или иначе не вкладывается в SplitMix.)

Например, мы можем быть очень напуганы SplitMix и показать, что с константой потока по умолчанию, если мы посмотрим на каждый 35185-й вывод, он не работает в наборе тестов PRNG. О нет! Это связано с тем, что он внутренне увеличивает счетчик (последовательность Вейля!) На 0x9e3779b97f4a7c15 (на основе φ, золотого сечения), но 35185 * 0x9e3779b97f4a7c15 = 0x86a100000c480245 , который имеет только Набор из 14 бит и большая полоса пустоты посередине. Или, если мы посмотрим на каждый 360998717-й вывод, мы придем к тому, чтобы быть эквивалентным добавлению к внутреннему состоянию 0x48620000800401 , которое состоит только из 8 битов, и снова что-то сложное для его функции вывода, чтобы полностью замаскировать .

Мы могли бы продолжить запугивать SplitMix и сказать: «А что, если у меня будет два потока, один с аддитивной константой 0x9e3779b97f4a7c15 а другой с 0xdaa66d2c7ddf743f , мы увидим недостатки, если введем это в набор тестов ГПСЧ !!! Но это потому, что второй задуман быть в 3 раза больше другого.

И, наконец, если кто-то сказал: «Я дам вам оба потока, сделайте с этим что-нибудь страшное!», И предположим, что их поток основан на π ( 0x243f6a8885a308d3 ) и _e_ ( 0xb7e151628aed2a6b ), мы можем сказать, конечно, давайте еще немного напугаем и возьмем каждый 6561221343-й элемент из потока Pi и смешаем его с каждым 6663276199-м элементом из потока E и low-and-belhold, они производят два идентичных последовательности. И, что еще хуже, я продолжаю показывать, что для каждого перехода в потоке a существует соответствующий переход в потоке b, чтобы дать тот же результат, так что на самом деле существует 2 ^ 64 способа их корреляции !!! (И мы можем сделать это для любых двух потоков, в π и _e_ не было ничего особенного.)

Возвращаясь к PCG, тест Себастьяно полагается на то, что два генератора PCG64 XSH RR точно выровнены так, что совпадающие выходные сигналы чередуются. Если мы просто немного продвинем один из ГПСЧ на небольшую величину, нарушив идеальное выравнивание, станет значительно труднее обнаружить что-либо подозрительное.

Аналогичным состязательным тестом в другом направлении (налагающим бремя на Себастьяно) было бы предоставление выходных данных PCG64 XSH RR, которые соответствуют его утверждению, что они коррелированы, но мы не говорим ему, как именно они согласованы (они просто в правой общей окрестности). Его работа будет заключаться в том, чтобы найти выравнивание, чтобы показать, что они коррелированы.

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

PS Вы можете сделать магические аддитивные константы Вейля из вашего любимого действительного числа, используя этот код:

WeylConst[r_,bits_] = BitOr[Floor[(r-Floor[r])*2^bits],1]

Это Mathematica, я оставлю версию Python в качестве упражнения.

Вот как я создаю коллизии младших битов для разных приращений.

Результаты с PCG64 XSL-RR и конфликтом младших 58 бит

❯ ./pcg64_correlations.py -m 58 | stdbuf -oL ./RNG_test stdin64 -tf 2 -te 1 -tlmaxonly -multithreaded
s0 = 0b01110010100110011101000110010010101111111001100011001011001011111001001110101010011101111101001101011000011100001111111111100001
s1 = 0b10110001011001100111100010000110101110011010101010011011010100011001011111001100010001101001001011010010110101001011101111111100
dist = 0x2eb6ec432b0ea0f4fc00000000000000
[
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 152330663589051481538402839025803132897,
            "inc": 228410650821285501905570422998802152525
        },
        "has_uint32": 0,
        "uinteger": 0
    },
    {
        "bit_generator": "PCG64",
        "state": {
            "state": 235805414096687854712168706130903874556,
            "inc": 70910205337619270663569052684874994465
        },
        "has_uint32": 0,
        "uinteger": 0
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0x12d551b8
test set = expanded, folding = extra

rng=RNG_stdin64, seed=0x12d551b8
length= 128 megabytes (2^27 bytes), time= 2.8 seconds
  no anomalies in 891 test result(s)

rng=RNG_stdin64, seed=0x12d551b8
length= 256 megabytes (2^28 bytes), time= 9.4 seconds
  no anomalies in 938 test result(s)

rng=RNG_stdin64, seed=0x12d551b8
length= 512 megabytes (2^29 bytes), time= 18.1 seconds
  no anomalies in 985 test result(s)

rng=RNG_stdin64, seed=0x12d551b8
length= 1 gigabyte (2^30 bytes), time= 31.2 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]FPF-14+6/16:cross         R=  +4.9  p =  1.7e-4   unusual          
  [Low4/16]FPF-14+6/16:all          R=  +8.4  p =  2.3e-7   very suspicious  
  [Low4/16]FPF-14+6/16:all2         R=  +8.3  p =  8.1e-5   unusual          
  [Low8/32]FPF-14+6/32:all          R=  +6.3  p =  2.1e-5   mildly suspicious
  [Low8/32]FPF-14+6/16:all          R=  +5.7  p =  8.0e-5   unusual          
  ...and 1034 test result(s) without anomalies

rng=RNG_stdin64, seed=0x12d551b8
length= 2 gigabytes (2^31 bytes), time= 52.7 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low4/16]FPF-14+6/32:all          R=  +7.4  p =  2.0e-6   suspicious       
  [Low4/16]FPF-14+6/16:(0,14-0)     R=  +7.7  p =  9.4e-7   unusual          
  [Low4/16]FPF-14+6/16:all          R=  +8.0  p =  5.9e-7   suspicious       
  [Low4/16]FPF-14+6/16:all2         R= +12.2  p =  2.1e-6   mildly suspicious
  [Low4/16]FPF-14+6/4:(0,14-0)      R=  +7.9  p =  6.3e-7   mildly suspicious
  [Low4/16]FPF-14+6/4:all           R=  +5.8  p =  6.7e-5   unusual          
  [Low4/16]FPF-14+6/4:all2          R= +11.5  p =  3.1e-6   mildly suspicious
  [Low8/32]FPF-14+6/32:(0,14-0)     R=  +7.8  p =  8.4e-7   unusual          
  [Low8/32]FPF-14+6/32:all          R=  +7.3  p =  2.3e-6   suspicious       
  [Low8/32]FPF-14+6/32:all2         R= +14.3  p =  3.8e-7   suspicious       
  [Low8/32]FPF-14+6/16:(0,14-0)     R=  +7.7  p =  8.8e-7   unusual          
  [Low8/32]FPF-14+6/16:(1,14-0)     R=  +7.7  p =  9.3e-7   unusual          
  [Low8/32]FPF-14+6/16:all          R=  +6.9  p =  5.3e-6   mildly suspicious
  [Low8/32]FPF-14+6/16:all2         R= +18.3  p =  8.0e-9   very suspicious  
  ...and 1078 test result(s) without anomalies

rng=RNG_stdin64, seed=0x12d551b8
length= 4 gigabytes (2^32 bytes), time= 90.2 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/8]BCFN_FF(2+0):freq         R= +14.8  p~=   6e-18     FAIL !         
  [Low1/8]BCFN_FF(2+1):freq         R=  +7.4  p~=   1e-6    mildly suspicious
  [Low1/8]FPF-14+6/16:cross         R=  +8.4  p =  2.1e-7   very suspicious  
  [Low4/16]FPF-14+6/32:(0,14-0)     R=  +8.9  p =  8.1e-8   mildly suspicious
  [Low4/16]FPF-14+6/32:(1,14-0)     R=  +8.5  p =  1.9e-7   mildly suspicious
  [Low4/16]FPF-14+6/32:all          R=  +9.4  p =  2.4e-8   very suspicious  
  [Low4/16]FPF-14+6/32:all2         R= +23.9  p =  5.2e-11   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/16:(0,14-0)     R= +13.8  p =  2.2e-12   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/16:(1,14-0)     R= +10.0  p =  7.3e-9   suspicious       
  [Low4/16]FPF-14+6/16:all          R= +12.1  p =  8.0e-11   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/16:all2         R= +52.5  p =  1.3e-22    FAIL !!        
  [Low4/16]FPF-14+6/4:(0,14-0)      R= +12.2  p =  7.0e-11   VERY SUSPICIOUS 
  [Low4/16]FPF-14+6/4:all           R=  +7.1  p =  3.7e-6   mildly suspicious
  [Low4/16]FPF-14+6/4:all2          R= +29.8  p =  7.1e-14    FAIL           
  [Low4/16]FPF-14+6/4:cross         R=  +5.3  p =  7.8e-5   unusual          
  [Low4/32]FPF-14+6/32:(0,14-0)     R=  +7.6  p =  1.3e-6   unusual          
  [Low4/32]FPF-14+6/32:all          R=  +6.0  p =  4.4e-5   unusual          
  [Low4/32]FPF-14+6/32:all2         R=  +9.4  p =  2.9e-5   unusual          
  [Low4/32]FPF-14+6/16:(0,14-0)     R=  +7.3  p =  2.5e-6   unusual          
  [Low4/32]FPF-14+6/16:all          R=  +6.5  p =  1.4e-5   mildly suspicious
  [Low4/32]FPF-14+6/16:all2         R=  +8.2  p =  8.0e-5   unusual          
  [Low8/32]FPF-14+6/32:(0,14-0)     R= +17.2  p =  1.7e-15    FAIL           
  [Low8/32]FPF-14+6/32:(1,14-0)     R= +12.7  p =  2.3e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/32:all          R= +15.3  p =  7.9e-14    FAIL           
  [Low8/32]FPF-14+6/32:all2         R= +86.1  p =  1.2e-35    FAIL !!!       
  [Low8/32]FPF-14+6/16:(0,14-0)     R= +16.8  p =  3.5e-15    FAIL           
  [Low8/32]FPF-14+6/16:(1,14-0)     R= +12.2  p =  6.6e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/16:all          R= +13.1  p =  8.9e-12   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/16:all2         R= +82.1  p =  1.7e-34    FAIL !!!       
  [Low8/32]FPF-14+6/4:(0,14-0)      R= +12.8  p =  2.0e-11   VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/4:(1,14-0)      R=  +9.4  p =  2.5e-8   suspicious       
  [Low8/32]FPF-14+6/4:all           R= +10.5  p =  2.2e-9    VERY SUSPICIOUS 
  [Low8/32]FPF-14+6/4:all2          R= +42.0  p =  5.8e-19    FAIL !         
  ...and 1118 test result(s) without anomalies

Результаты с PCG64 DXSM и более низкой 64-битной коллизией (чтобы быстрее вызвать проблемы, хотя я не вижу их)

❯ ./pcg64_correlations.py -m 64 --dxsm | stdbuf -oL ./RNG_test stdin64 -tf 2 -te 1 -tlmaxonly -multithreaded
s0 = 0b10001000010110111101010101010101111100100011011111011111011111001011110101111100101101101100110101110001101101111111010101111111
s1 = 0b11000101110100011001011000001110100001001111001001100101010000101100011001010111011001100000010010011100101110001110101000011100
dist = 0x3a26b19c91e6da1d0000000000000000
[
    {
        "bit_generator": "PCG64DXSM",
        "state": {
            "state": 181251833403477538233003277050491434367,
            "inc": 46073632738916603716779705377640239269
        },
        "has_uint32": 0,
        "uinteger": 0
    },
    {
        "bit_generator": "PCG64DXSM",
        "state": {
            "state": 262946148724842088422233355148768897564,
            "inc": 125105549038853892415237434774494719583
        },
        "has_uint32": 0,
        "uinteger": 0
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin64, seed = 0x85cea9
test set = expanded, folding = extra

rng=RNG_stdin64, seed=0x85cea9
length= 128 megabytes (2^27 bytes), time= 2.6 seconds
  no anomalies in 891 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 256 megabytes (2^28 bytes), time= 9.4 seconds
  no anomalies in 938 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 512 megabytes (2^29 bytes), time= 18.5 seconds
  no anomalies in 985 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 1 gigabyte (2^30 bytes), time= 32.3 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low4/32]BCFN_FF(2+3,13-3,T)      R=  -8.3  p =1-9.5e-5   unusual          
  ...and 1035 test result(s) without anomalies

rng=RNG_stdin64, seed=0x85cea9
length= 2 gigabytes (2^31 bytes), time= 55.8 seconds
  no anomalies in 1092 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 4 gigabytes (2^32 bytes), time= 93.1 seconds
  no anomalies in 1154 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 8 gigabytes (2^33 bytes), time= 175 seconds
  no anomalies in 1222 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 16 gigabytes (2^34 bytes), time= 326 seconds
  no anomalies in 1302 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 32 gigabytes (2^35 bytes), time= 594 seconds
  no anomalies in 1359 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 64 gigabytes (2^36 bytes), time= 1194 seconds
  no anomalies in 1434 test result(s)

rng=RNG_stdin64, seed=0x85cea9
length= 128 gigabytes (2^37 bytes), time= 2334 seconds
  no anomalies in 1506 test result(s)
...

@rkern , спасибо, что поделились своим кодом. Было бы поучительно добавить опцию добавления перекоса для подачи чередующегося вывода в тестер, который не идеально выровнен. Я немного исследовал это.

Ага, я сделал это неформально, вставив bg0.advance(N) для различных N перед финальным return . Я использовал нижнюю 64-битную коллизию, чтобы быть уверенным, что что-то увижу. Небольшие сдвиги, такие как 16 , не сильно меняют сбой, но даже скромные изменения, такие как 128 продлевают сбой до 32 ГиБ.

Похоже, мы должны добавить PCG64 DXSM в качестве дополнительного генератора битов и в конечном итоге сделать его по умолчанию. К нам есть реализация?

Я думаю, полезно понимать более широкий контекст. Себастьяно кое-что понимает в PCG и годами выступал против него. Я думаю, что в наши дни некоторые люди могут смотреть на нас обоих, закатывать глаза и говорить «вы оба такие же плохие, как и друг друга», потому что я также критиковал его ГПСЧ, но на самом деле я сделал это только после того, как он стал заявлять, что Я пытался что-то скрыть, никогда не рассказывая о его вещах (когда на самом деле у меня просто не было времени / желания - на самом деле, я просто предполагал, что они в порядке).

Я считаю эти соображения совершенно неуместными.

Настаивание на атаке чужой работы (например, SplitMix) без каких-либо заслуг и без каких-либо доказательств не сделает беспорядок PCG или генератор Numpy. Вместо этого может помочь лучший множитель или лучше разработанный скремблер.

Я все еще жду теста, показывающего корреляцию в SplitMix, когда пользователь сможет выбирать потоки. Для ясности: для генератора Numpy я доказал утверждение вида

∀c ∀d ∀x ∃y коррелированные

где c, d - приращения («потоки»), а x, y - начальные состояния. Фактически, это 2 ^ 72 года. То есть, независимо от того, как вы выбираете c, d и x, есть 2 ^ 72 y, показывающие корреляцию.

Предполагаемый соответствующий код, который вы предоставили для SplitMix, показывает, что

∃c ∃d ∃x ∃y коррелированные

То есть, состязательно выбирая c, d, x и y, вы можете показать корреляцию.

Разница в силе этих двух утверждений ошеломляет. Попытка объединить два утверждения неверна.

@vigna вас дважды предупреждали о нашем кодексе поведения: @mattip и @rkern. Использовать такие выражения, как «критиковать чужую работу» и «пытаться объединить эти два утверждения - это чистая FUD» - это не нормально. Считайте это своим последним предупреждением. Пожалуйста, измените свой тон, иначе мы _банем_ вас. Технические аргументы все еще приветствуются, ничего другого здесь нет.

Я изменил сообщение, заменив эти выражения нейтральными. Я по-прежнему считаю, что нападать лично на другого участника дискуссии («Себастьяно кое-что понимает в PCG и годами выступал против нее») совершенно неуместно. Я очень удивлен не для тебя.

В третий и последний раз обсуждение SplitMix в любом направлении мне нисколько не помогает. Я могу понять, почему вы думаете, что это обеспечивает необходимый контекст, или что вы чувствуете себя обязанным ответить другому, но, пожалуйста, поверьте, я говорю вам правду, что он не предоставляет мне никакой информации, которая поможет мне принять здесь решение. У вас обоих есть свои сайты. Используй их.

Я изменил сообщение, заменив эти выражения нейтральными.

Спасибо.

Я по-прежнему считаю, что нападать лично на другого участника дискуссии («Себастьяно кое-что понимает в PCG и годами выступал против нее») совершенно неуместно. Я очень удивлен не для тебя.

Я бы тоже предпочел не видеть этого. Однако тон этого сообщения далеко не так плох.

Я был бы очень признателен, если бы вы оба придерживались конструктивных фактов @vigna и @imneme.

ХОРОШО. Начнем с нуля: вам нужен генератор с какими-то потоками на основе LCG с модулем степени 2 для удобства и скорости. В литературе говорится, что основание потоков на аддитивных константах LCG может привести к проблемам (как это происходит сейчас), но давайте предположим, что вы этого хотите.

Почему бы не взять LCG со 128-битным состоянием и хорошим множителем (не менее 65 бит) и не изменить старшие биты с помощью функции микширования из SplitMix, которая была тщательно протестирована в различных приложениях (хеширование, PRNG и т. Д.), дает отличные результаты?

Я почти уверен, что разница в скорости будет незначительной. И у вас есть некоторая (статистическая) гарантия, что результат будет зависеть от всех битов, что является проблемой здесь.

Мне кажется, что это больше похоже на подход «стоять на плече гигантов», чем на создание вручную функций микширования на генераторе, у которого есть проблемы с самокорреляцией.

@imneme То, что я мог бы использовать, - это запись в блоге о DXSM, на которую проще ссылаться, чем на этот комментарий в старом мегавопросе. Это не обязательно должно быть что-то большее, чем то, что указано в этом комментарии, но было бы хорошо включить текущий статус тестирования, о котором вы здесь говорили. Если вы хотите подвести итоги обсуждения мегапроблемы, которая привела к такому развитию, это, безусловно, было бы полезно, но не совсем необходимо.

@vigna

Почему бы не взять LCG со 128-битным состоянием и хорошим множителем (не менее 65 бит) и не изменить старшие биты с помощью функции микширования из SplitMix, которая была тщательно протестирована в различных приложениях (хеширование, PRNG и т. Д.), дает отличные результаты?

Прошу прощения, если это прозвучит нелепо (хотя, безусловно, будет указано), но это также искренне: я с нетерпением жду результатов реализации, анализа, тестов и PractRand на вашем веб-сайте или в arXiv. Мы (достаточно информированные) здесь практикующие, а не исследователи ГПСЧ, и не особенно хорошо подготовлены для выполнения этого предположения. Я вижу в этом смысл, но, учитывая другие ограничения моего личного времени, я не склонен тратить усилия, чтобы перенести это от предложения к реализации и анализу. Если вы обращаетесь с этим предложением к numpy, нам нужны исследователи PRNG для этой работы. Если вы действительно адресовали это предложение кому-то другому, используйте свой веб-сайт.

Случайная Generator -> BitGenerator -> SeedSequence архитектура в NumPy предназначена для подключения. Я думаю, что мы подошли к тому моменту обсуждения, когда нам нужен кто-то, чтобы открыть PR для BitGenerator, чтобы мы могли сравнить его практические атрибуты с теми, которые в настоящее время есть в NumPy. Как только он станет частью проекта, мы сможем продолжить его тестирование и, возможно, решим сделать его по умолчанию. Это решение, я надеюсь, будет основано на

  • отсутствие предвзятости (и другие критерии? уступаю экспертам)
  • спектакль
  • вероятность различных типов конфликтов потоков через рекомендуемые нами нормативные интерфейсы: с использованием BitGenerator.spawn и SeedSequence .

Лично меня эта дискуссия потеряла, когда избегала обсуждения достоинств BitGenerators через код, использующий интерфейс spawn . Есть причина, по которой мы продвигаем это как лучшую практику, и я надеюсь, что обсуждение будущего PR будет сосредоточено на лучших практиках для пользователей NumPy .

Возможно, один из выводов здесь может заключаться в том, что мы должны разрешать только spawn в качестве метода, поскольку использование jumped или advance может противоречить передовой практике. Новый выпуск или нэп, посвященный этому, также могут быть продуктивными.

@mattip Коллизия младших битов дня рождения, отмеченная @vigna, также влияет на наш интерфейс SeedSequence.spawn() . Будьте уверены, что любая часть обсуждения, в котором я участвовал, имеет отношение к правильному использованию наших API.

Требуется всего лишь добавить около 8 строк в pcg64.c с некоторыми блоками #ifdef, чтобы использовать предпочтительный подход

Я бы, наверное, сказал об этом более подробно, особенно в отношении эмулированной 128-битной математики для тех платформ, которые в ней нуждаются.

https://github.com/rkern/numpy/compare/v1.17.4...rkern%3Awip/pcg64-dxsm

Мне это показалось проще, так как он использует "дешевый" 64-битный умножитель. Вы можете просто добавить новый выходной микшер (который является инвариантным), а затем ifdef вокруг последней строки случайного генератора, который принимает выходные данные LCG и затем применяет микшер.

https://github.com/bashtage/randomgen/commit/63e50a63f386b5fd725025f2199ff89454321f4c#diff -879bd64ee1e2b88fec97b5315cf77be1R115

Если бы кто-то хотел, вы могли бы даже добавить в этот момент Murmur Hash 3, были так склонны.

Будут ли компилироваться операторы if ? Я не думаю, что мы хотим, чтобы их было несколько внутри горячих петель.

Опять же, это сводится к разнице в назначении между randomgen и numpy . В randomgen есть смысл создавать параметризованные семейства, но в numpy я не думаю, что это хорошая идея связывать реализации устаревшего BitGenerator с активным по умолчанию BitGenerator . Если нам придется провести какое-либо обслуживание или рефакторинг для повышения производительности одного или другого, это только усугубит ситуацию, а не улучшит.

Согласитесь с Робертом здесь. Я не сомневаюсь, что добавлю новый битовый генератор в версию 1.19.0, это не повлияет на текущее поведение.

@bashtage Также обратите внимание, что pcg_cm_random_r() использует для вывода состояние до итерации, а не состояние после итерации , поэтому будет не так просто поддерживать тот же путь кода с #ifdef или if переключатели.

Будут ли компилироваться операторы if ? Я не думаю, что мы хотим, чтобы их было несколько внутри горячих петель.

Нет, в NumPy if else должно стать чем-то вроде

#if defined(PCG_DXSM)
    pcg_output_dxsm(state.high, state.low)
#else 
   <old way>
#endif

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

@bashtage Также обратите внимание, что pcg_cm_random_r() использует для вывода состояние до итерации, а не состояние после итерации , поэтому поддерживать один и тот же путь кода с помощью #ifdef или if переключатели.

Хм, я протестировал эталонную реализацию

https://github.com/bashtage/randomgen/blob/master/randomgen/src/pcg64/pcg_dxsm-test-data-gen.cpp

AFAICT (и я могу ошибаться)

https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp#L174

и

https://github.com/imneme/pcg-cpp/blob/master/include/pcg_random.hpp#L1045

означают, что в пути uint_128 всегда используется дешевый множитель.

Я не уверен, что вы пытаетесь там сказать.

Непонятно, что такое канонический PCG64 DXSM. В любом случае функция вывода использует только 64-битные операции. Версия, которая у вас есть, использует 64-битный множитель в другом месте, чтобы быть еще быстрее и возвращает до, а не после публикации. setseq_dxsm_128_64 кажется естественным продолжением существующего PCG64 и меняет только функцию вывода.

О, я вижу. Нет, вы использовали другой генератор C ++, чем тот, который я реализовал в C. Я реализовал эквивалент cm_setseq_dxsm_128_64 который использует «дешевый множитель» в итерации LCG, а не setseq_dxsm_128_64 который все еще в итерации LCG используется большой множитель. «Дешевый множитель» повторно используется внутри функции вывода DXSM, но это ортогональная ось проектирования.

Почему бы не предпочесть setseq_dxsm_128_64?

@imneme сказала, что она собиралась в конечном итоге изменить официальный pcg64 в версии C ++, чтобы он указывал на cm_setseq_dxsm_128_64 , а не на setseq_dxsm_128_64 . Дешевый множитель компенсирует некоторые дополнительные затраты на DXSM по сравнению с XSL-RR. И я думаю, что это вариант, который она тестировала пару месяцев.

Вывод предварительно повторенного состояния также является частью увеличения производительности .

Вот несколько таймингов:

In [4]: %timeit p.random_raw(1000000)
3.24 ms ± 4.61 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [5]: p = rg.PCG64(mode="sequence",use_dxsm=False)

In [6]: %timeit p.random_raw(1000000)
3.04 ms ± 8.47 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [7]: import numpy as np

In [8]: p = np.random.PCG64()

In [9]: %timeit p.random_raw(1000000)
3.03 ms ± 2.54 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Все на Ubuntu-20.04, компилятор по умолчанию.

На 6% медленнее. Мне кажется, это небольшая разница. Все тайминги в NumPy / randomgen довольно далеки от того, что вы можете получить в реальном жестком цикле нативного кода.

Сравнить

In [10]: x = rg.Xoroshiro128(mode="sequence")

In [11]: %timeit x.random_raw(1000000)
2.59 ms ± 35.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

к тому, что было опубликовано из кода C (на 150% медленнее ??).

Конечно, в контексте numpy это не имеет большого значения. Это имеет большее значение в контексте C ++, который привел к выделению кластер-месяцев для тестирования и назначению в качестве будущего значения pcg64 по умолчанию в официальном коде C ++. Таковы приблизительные мотивы для numpy , ИМО.

Разница между стоковым PCG64 и моим PCG64DXSM в моей ветке ("дешевый множитель", функция вывода DXSM, вывод предварительно повторенного состояния, отдельный путь кода):

[practrand]
|1> s = np.random.SeedSequence()

[practrand]
|2> pcg64 = np.random.PCG64(s)

[practrand]
|3> pcg64dxsm = np.random.PCG64DXSM(s)

[practrand]
|4> %timeit pcg64.random_raw(1000000)
100 loops, best of 3: 3.46 ms per loop

[practrand]
|5> %timeit pcg64dxsm.random_raw(1000000)
100 loops, best of 3: 2.9 ms per loop

Я все еще говорю, что между ними всего несколько (больше, чем у меня было) #ifdefs , даже со специальной реализацией для MSVC. Кроме того, пользователи RSN MS смогут использовать clang # 13816 👍.

Мы спорим из-за дублирования кода? Я бы предпочел иметь несвязные реализации, чем беспокоиться о нескольких строках кода, запутанных с помощью #ifdefs :)

По большей части это была просто шутка, хотя она действительно подчеркивала необходимость в абсолютно ясном заявлении, определяющем «PCG 2.0» (желательно где-нибудь, кроме проблем с GitHub NumPy).

Спасибо, @rkern et al.

@imneme То, что я мог бы использовать, - это запись в блоге о DXSM, на которую проще ссылаться, чем на этот комментарий в старом мегавопросе. Это не обязательно должно быть что-то большее, чем то, что указано в этом комментарии, но было бы хорошо включить текущий статус тестирования, о котором вы здесь говорили. Если вы хотите подвести итоги обсуждения мегапроблемы, которая привела к такому развитию, это, безусловно, было бы полезно, но не совсем необходимо.

У вас есть временные рамки? Я собирался сделать это в течение некоторого времени, и это вытеснялось другими вещами, поэтому на самом деле предложенный крайний срок был бы для меня полезным мотиватором. Возможно, через неделю? Два?

@rkern также процитировал @vigna , который написал:

Почему бы не взять LCG со 128-битным состоянием и хорошим множителем (не менее 65 бит) и не изменить старшие биты с помощью функции микширования из SplitMix, которая была тщательно протестирована в различных приложениях (хеширование, PRNG и т. Д.), дает отличные результаты?

FWIW, этот подход обсуждался в исходной статье PCG, где _FastHash_ использовалась в качестве стандартной хеш-функции, которая очень похожа на хеш-функцию multiply-xorshift. В моем тестировании это было не так быстро, как другие перестановки, но было высокого качества. Себастьяно также упомянул эту идею в своей критике PCG в 2018 году, и я обсуждаю ее в этом разделе своего ответа на эту критику.

В исходной версии своей критики PCG он заканчивает написанием собственного варианта PCG, который я процитирую ниже:

        #include <stdint.h>

        __uint128_t x;

        uint64_t inline next(void) {
            // Put in z the top bits of state
            uint64_t z = x >> 64;
            // Update state
            x = x * ((__uint128_t)0x2360ed051fc65da4 << 64 ^ 0x4385df649fccf645)
                  + ((__uint128_t)0x5851f42d4c957f2d << 64 ^ 0x14057b7ef767814f);
            // Compute mix
            z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
            z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
            return z ^ (z >> 31);
        }

С тех пор он обновил код в своей критике до еще более быстрой версии, которая использует более дешевый множитель и сокращает аддитивную константу до 64 бит,

        #include <stdint.h>

        __uint128_t x;

        uint64_t inline next(void) {
            // Put in z the top bits of state
            uint64_t z = x >> 64;
            // Update state (multiplier from https://arxiv.org/abs/2001.05304)
            x = x * ((__uint128_t)1 << 64 ^ 0xd605bbb58c8abbfd) + 0x14057b7ef767814f;
            // Compute mix
            z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
            z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
            return z ^ (z >> 31);
        }

Моя проблема с обоими этими вариантами заключается в том, что перестановка обратима, потому что усеченное состояние (верхняя половина) переставляется / скремблируется - вы можете запустить его в обратном направлении и расшифровать скремблирование, оставив вас с простым усеченным LCG со всеми присущими ему недостатками. Я предпочитаю переставлять / обманывать все состояние, а затем выводить его усечение. (Конечно, перестановка / скремблирование меньшего количества битов будет более быстрым способом - как обычно, есть компромиссы. Разумные люди могут не согласиться с одним важным.)

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

@charris Как вы PCG64DXSM доступной (но еще не по умолчанию) в 1.19.0? Что это за сроки? Я вижу, что у нас уже выпущена версия 1.19.0rc2, что не очень важно для внедрения новой функции. Опять же, я не волнуюсь по этому поводу. Я бы предпочел выпуск 1.19.0, просто документируя нашу политику в отношении изменений в default_rng() и вводя новые вещи в 1.20.0.

@rkern Последний RC должен быть

РЕДАКТИРОВАТЬ: При условии, конечно, что с новым кодом нет серьезных проблем, и не похоже, что есть. Меня также не слишком беспокоят проблемы с PCG64, похоже, вряд ли у кого-то возникнут проблемы при использовании наших рекомендуемых процедур.

@imneme Одна неделя была бы отличной. Две недели было бы хорошо. Благодаря!

Я задаю себе вопрос, который несколько не по теме. Мы хотим, чтобы наши битовые генераторы производили случайные биты, но, AFAICT, большинство тестов включает целые числа. Насколько хорошо существующие тесты действительно проверяют биты? Если бы кто-то, более знакомый с местностью, мог ответить на этот вопрос, я был бы очень признателен.

Тестируется поток битов. Мы сообщаем тестовой программе естественный размер слова ГПСЧ, который мы выводим, но только для того, чтобы она могла наилучшим образом сворачивать биты, чтобы наиболее эффективно вызывать ошибки, которые, как правило, возникают в младших или высоких битах слово в плохих ГПСЧ. Программное обеспечение, которое мы все обычно используем в наши дни, - это PractRand, и его тесты здесь немного задокументированы . Лучше всего читать статью о TestU01 , предыдущем «золотом стандарте» набора тестов. Его Руководство пользователя содержит более подробную информацию о тестах.

Прошу прощения, если это прозвучит нелепо (хотя, безусловно, будет указано), но это также искренне: я с нетерпением жду результатов реализации, анализа, тестов и PractRand на вашем веб-сайте или в arXiv. Мы (достаточно информированные) здесь практикующие, а не исследователи ГПСЧ, и не особенно хорошо подготовлены для выполнения этого предположения. Я вижу в этом смысл, но, учитывая другие ограничения моего личного времени, я не склонен тратить усилия, чтобы перенести это от предложения к реализации и анализу. Если вы обращаетесь с этим предложением к numpy, нам нужны исследователи PRNG для этой работы. Если вы действительно адресовали это предложение кому-то другому, используйте свой веб-сайт.

Я прекрасно понимаю вашу точку зрения. Код и тест были внизу страницы, комментируя проблемы PCG (http://prng.di.unimi.it/pcg.php) уже пару лет под названием LCG128Mix. Это занимает 2,16 нс на моем оборудовании, процессоре Intel (R) Core (TM) i7-7700 @ 3,60 ГГц, с gcc 9.2.1 и -fno-move-loop-invariants -fno-unroll-loops.

Код очень прост - он сочетает в себе стандартный LCG со стандартной функцией микширования (улучшенный финализатор MurmurHash3 Стаффорда). Я немного изменил его, чтобы получить программируемую константу:

    #include <stdint.h>
    __uint128_t x; // state
    __uint64_t c;  // stream constant (odd)

    uint64_t inline next(void) {
        // Put in z the top bits of state
        uint64_t z = x >> 64;
        // Update LCG state
        x = x * ((__uint128_t)1 << 64 ^ 0xd605bbb58c8abbfd) + c;
        // Compute mix
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
        z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
        return z ^ (z >> 31);
    }

Как я объяснял ранее, 65-битная константа намного лучше любой 64-битной константы в качестве умножителя из-за теоретических проблем с множителями, меньшими, чем квадратный корень из модуля.

Если вас интересует более принципиальный дизайн, я проведу тесты PractRand. Однако вы должны принять во внимание, что эта функция микширования дала отличный генератор, SplitMix, даже с гораздо более слабым базовым генератором (он был просто аддитивным) и с меньшим состоянием (64 бита). Так что он будет просто «лучше», чем SplitMix, который передает PractRand на 32 ТБ.

А базовый генератор - это LCG, поэтому у вас есть все обычные навороты 60-х годов: прыжки, расстояния и т. Д. Но также у вас есть статистическая гарантия, что каждый бит результата зависит от каждого бита старших 64 битов штат.

Если вы имеете в виду тесты с некоторыми другими генераторами или с использованием других компиляторов, дайте мне знать.

Но, пожалуйста, так же искренне: только в том случае, если вам действительно интересно рассматривать дизайн «стоящий на плечах гигантов» с использованием только стандартных компонентов. У меня тоже есть личные ограничения, и я рад внести свой вклад, но я бы не хотел тратить время на генератор, который не может быть рассмотрен.

Кстати, чтобы дать более ощутимую меру того, как улучшить качество задействованных умножителей, я вычислил спектральные оценки, от f₂ до f₈, текущего 64-битного умножителя, используемого PCG DXS и некоторыми альтернативами.

Спектральные оценки - это стандартный способ судить о доброте множителя. 0 - плохо, 1 - отлично. Каждая оценка описывает, насколько хорошо распределяются пары, тройки, 4-кортежи и т. Д. В выходных данных.

Эти семь чисел можно возобновить в классическом измерении, минимальном или взвешенном (первая оценка плюс вторая, деленная на два, и т. Д., Нормализованные), чтобы сделать первые оценки более важными, как предлагает Кнут в TAoCP. , а это минимальная и взвешенная мера для текущего множителя:

0xda942042e4dd58b  0.633  0.778

Есть гораздо лучшие 64-битные константы, чем эта:

0xff37f1f758180525  0.761  0.875

Если вы перейдете к 65 битам, по существу, с той же скоростью (по крайней мере, для LCG128Mix такая же скорость), вы получите более взвешенную меру:

0x1d605bbb58c8abbfd  0.761  0.899

Причина в том, что 64-битные множители имеют внутренний предел для их оценки f₂ (≤0,93), что, как отметил Кнут, является наиболее актуальным:

0xda942042e4dd58b5  0.795
0xff37f1f758180525  0.928
0x1d605bbb58c8abbfd  0.992

Таким образом, первый множитель имеет посредственную оценку f₂. Второй множитель очень близок к оптимальному для 64-битного множителя. 65-битный умножитель не имеет этих ограничений и имеет оценку, очень близкую к 1, что в целом является наилучшим возможным.

Для полноты картины вот все оценки:

 0xda942042e4dd58b5  0.794572 0.809219 0.911528 0.730396 0.678620 0.632688 0.639625
 0xff37f1f758180525  0.927764 0.913983 0.828210 0.864840 0.775314 0.761406 0.763689 
0x1d605bbb58c8abbfd  0.991889 0.907938 0.830964 0.837980 0.780378 0.797464 0.761493

Вы можете пересчитать эти баллы или найти свой собственный множитель с кодом, который мы с Гаем Стилом распространили: https://github.com/vigna/CPRNG . Лучшие множители взяты из соответствующей статьи.

PCG, вероятно, будет прекрасной программой по умолчанию для numpy, но я не думаю, что она выдержит испытание временем, так как есть более многообещающие, но менее проверенные способы сделать это. Я предлагаю одно из следующих.

Полхаотический SFC64 - один из самых быстрых из статистически надежных генераторов с разумно большим минимальным периодом. SFC64 не имеет функций перехода, но может _без накладных расходов на скорость быть расширенным для поддержки 2 ^ 63 гарантированных уникальных потоков_. Просто добавьте последовательность Вейля с выбранной пользователем аддитивной константой k (должно быть нечетным) вместо того, чтобы просто увеличивать счетчик на единицу. Каждое нечетное k дает уникальный полный период. Для хранения константы Вейля требуются дополнительные 64 бита состояния:

typedef struct {uint64_t a, b, c, w, k;} sfcw64_t; // k = stream

static inline uint64_t sfcw64_next(sfcw64_t* s) {
    enum {LROT = 24, RSHIFT = 11, LSHIFT = 3};
    const uint64_t out = s->a + s->b + (s->w += s->k);
    s->a = s->b ^ (s->b >> RSHIFT);
    s->b = s->c + (s->c << LSHIFT);
    s->c = ((s->c << LROT) | (s->c >> (64 - LROT))) + out;
    return out;
}

Состояние 320 битов иногда нежелательно, поэтому я попытался сжать его до 256 бит снова. Обратите внимание на измененную функцию вывода, которая лучше использует последовательность Вейля для битового микширования. Он использует хаотическое / структурированное состояние 128/128 бит, что, кажется, обеспечивает хороший баланс:
/ EDIT: удалено rotl64 () из output func + cleanup, 6 августа:

typedef struct {uint64_t a, b, w, k;} tylo64_t;

static inline uint64_t tylo64_next(tylo64_t* s) {
    enum {LROT = 24, RSHIFT = 11, LSHIFT = 3};
    const uint64_t b = s->b, out = s->a ^ (s->w += s->k);
    s->a = (b + (b << LSHIFT)) ^ (b >> RSHIFT);
    s->b = ((b << LROT) | (b >> (64 - LROT))) + out;
    return out;
}

В настоящее время он прошел 4 ТБ в тестировании PractRand без аномалий, и я кратко провел тест Vigna с весовым коэффициентом Хэмминга до сих пор без проблем (хотя прохождение этих тестов не является гарантией почти истинного случайного вывода, а скорее проверяет, является ли prng ошибочным или нет ).

Примечание: предположительно со статистической точки зрения преимуществом является использование (уникальной) случайной константы Вейля с примерно 50% набора битов, но только дальнейшее тестирование или анализ покажет, насколько это важно.

/ Правки: зачистки.

@ tylo-work SFC64 уже есть в NumPy вместе с Philox, речь идет о генераторе по умолчанию.

Хорошо, я не знал точно, какие из них были реализованы, так что речь идет только о выборе наиболее подходящих из них? Достаточно честно, и спасибо за разъяснения.

Я постараюсь тщательно протестировать предложенный мной генератор, чтобы увидеть, как он сочетается с другими, и пока он выглядит очень хорошо с точки зрения скорости, качества вывода, простоты / размера / портативности и для массового параллельного использования. Но я был бы счастлив, если бы другие его тоже протестировали.

Версия в NumPy - это стандартный вариант с k=1 .

https://github.com/numpy/numpy/blob/ad30b31af0bb3fbfdc0f11486807edc76cec2680/numpy/random/src/sfc64/sfc64.h#L25 -L33

Я не думаю, что мы возобновляем обсуждение ГПСЧ по умолчанию с нуля. У нас есть очень конкретная проблема с нашим текущим ГПСЧ, и мы ищем доступные, тесно связанные варианты, которые решают эту конкретную проблему. Одна из наших проблем заключается в том, что текущий ГПСЧ по умолчанию предоставляет определенные функции ГПСЧ, такие как возможность перехода, которые вариант, который его заменяет, все еще должен предоставлять. SFC64 (наш или ваш) не имеет этой функции.

Возможно, что @bashtage захочет принять PR для randomgen, чтобы добавить ваши варианты SFC64 с потоком Weyl.

@ tylo-work Если вас интересует параллельное выполнение, вы можете посмотреть на реализацию SeedSequence в NumPy.

Я не думаю, что мы возобновляем обсуждение ГПСЧ по умолчанию с нуля. У нас есть очень конкретная проблема с нашим текущим ГПСЧ, и мы ищем доступные, тесно связанные варианты, которые решают эту конкретную проблему.

Предполагая, что вам нужно что-то вроде PCG-DXS, есть дополнительные улучшения, которые вы можете сделать с помощью только лучших констант (и очень незначительного замедления). Например, PCG-DXS скоро провалит два разных типа тестов на двух чередующихся коррелированных подпоследовательностях с одинаковыми младшими 112 битами состояния:

rng=PCGDXS_int112, seed=0x4d198651
length= 128 gigabytes (2^37 bytes), time= 5700 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/64]TMFn(0+2):wl             R= +57.3  p~=   2e-27     FAIL !!
  [Low8/64]FPF-14+6/64:(1,14-0)     R= +17.5  p =  8.0e-16    FAIL
  [other failures in the same tests]
  ...and 1893 test result(s) without anomalies

Обратите внимание, что мы говорим только о ≈65536 коррелированных последовательностях - нечего бояться.

Но вы можете улучшить генератор, выбрав лучший множитель, например 0x1d605bbb58c8abbfd, и лучший микшер, например 0x9e3779b97f4a7c15. Первое число - это 65-битный умножитель, который имеет гораздо лучшие спектральные показатели. Второе число - это просто золотое сечение в 64-битном представлении фиксированной точки, и это, как известно, обладает хорошими свойствами смешивания (см. Knuth TAoCP по мультипликативному хешированию); например, он используется библиотекой Eclipse Collections для смешивания хэш-кодов.

В результате вы не справитесь только с FPF для того же количества данных:

rng=PCG65-DXSϕ_int112, seed=0x4d198651
length= 128 gigabytes (2^37 bytes), time= 5014 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low8/64]FPF-14+6/64:(0,14-0)     R= +16.1  p =  1.5e-14    FAIL
  [other failures in the same test]
  ...and 1892 test result(s) without anomalies

Фактически, если мы пойдем дальше на 2 ТБ, PCG-DXS не пройдёт _три_ типов тестов для тех же чередующихся коррелированных подпоследовательностей:

rng=PCGDXS_int112, seed=0x4d198651
length= 2 terabytes (2^41 bytes), time= 53962 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/32]TMFn(0+0):wl             R= +50.2  p~=   4e-23     FAIL !!
  [Low8/64]FPF-14+6/64:(1,14-0)     R=+291.1  p =  4.7e-269   FAIL !!!!!!
  [Low8/64]Gap-16:B                 R= +19.5  p =  1.4e-16    FAIL !
  [other failures in the same tests]
  ...and 2153 test result(s) without anomalies

тогда как PCG65-DXSϕ по-прежнему не работает только с FPF:

rng=PCGDXS65ϕ_int112, seed=0x4d198651
length= 2 terabytes (2^41 bytes), time= 55280 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low8/64]FPF-14+6/64:(0,14-0)     R=+232.1  p =  2.0e-214   FAIL !!!!!!
  [other failures in the same test]
  ...and 2153 test result(s) without anomalies

Рано или поздно, конечно же, PCG65-DXSϕ выйдет из строя Gap и TMFn. Но вам нужно увидеть гораздо больше результатов, чем с PCG-DXS.

Это полный код для PCG65-DXSϕ, который представляет собой просто PCG-DXS с лучшими константами:

#include <stdint.h>

__uint128_t x; // State
uint64_t c; // Additive constant

static inline uint64_t output(__uint128_t internal) {
    uint64_t hi = internal >> 64;
    uint64_t lo = internal;

    lo |= 1;
    hi ^= hi >> 32;
    hi *= 0x9e3779b97f4a7c15;
    hi ^= hi >> 48;
    hi *= lo;
    return hi;
}

static uint64_t inline next(void) {
    __uint128_t old_x = x;
    x = x *  ((__uint128_t)1 << 64 ^ 0xd605bbb58c8abbfd) + c;
    return output(old_x);
}

Незначительное замедление связано с инструкцией сложения (вызванной 65-битным умножителем) и наличием двух 64-битных констант для загрузки.

Я не одобряю генераторы такого типа в целом, но PCG65-DXSϕ заметно лучше, чем PCG-DXS в отношении сокрытия корреляции.

@Vigna , к сведению, я также провел некоторое тестирование чередования и заметил, что xoshiro256 ** довольно быстро выходит из строя при создании 128 или более чередующихся потоков. С 256 быстро вышло из строя. Цель теста - проверить, насколько хорошо работают ГПСЧ, когда каждый поток был инициализирован с некоторыми линейными зависимостями. По сути, состояние инициализируется как s[0]=s[1]=s[2]=s[3] = k1 + stream*k2 . Затем 12 выходов пропускаются, что в основном и инициализируется sfc64.

Я понимаю, что это не рекомендуемая инициализация для xoshiro, но все же интересно - и немного тревожно - что тесты казались хорошими для xoshiro с несколькими чередующимися потоками, но не удавались со многими.

seed: 1591888413
RNG_test using PractRand version 0.95
RNG = RNG_stdin64, seed = unknown
test set = core, folding = standard (64 bit)
...
rng=RNG_stdin64, seed=unknown
length= 2 gigabytes (2^31 bytes), time= 29.6 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/64]FPF-14+6/16:(1,14-1)     R=  +7.2  p =  3.7e-6   unusual
  [Low1/64]FPF-14+6/16:all          R=  +9.6  p =  1.8e-8   very suspicious
  ...and 261 test result(s) without anomalies

rng=RNG_stdin64, seed=unknown
length= 4 gigabytes (2^32 bytes), time= 55.5 seconds
  Test Name                         Raw       Processed     Evaluation
  [Low1/64]FPF-14+6/16:(0,14-0)     R= +13.4  p =  4.7e-12   VERY SUSPICIOUS
  [Low1/64]FPF-14+6/16:(1,14-0)     R=  +9.4  p =  2.6e-8   suspicious
  [Low1/64]FPF-14+6/16:(2,14-1)     R=  +7.7  p =  1.3e-6   unusual
  [Low1/64]FPF-14+6/16:all          R= +17.4  p =  8.8e-16    FAIL !
  ...and 275 test result(s) without anomalies

Я также попытался ослабить инициализацию для SFC64 и TYLO64, чтобы пропустить только 2 выхода, но они все еще казались нормальными.
По производительности: xoshiro256 ** работает на моей машине на 33% медленнее, чем две другие. TYLO64 обновляет только 196 бит переменных состояния. Вот тестовая программа:

int main()
{
    //FILE* f = freopen(NULL, "wb", stdout);  // Only necessary on Windows, but harmless.
    enum {THREADS = 256};
    uint64_t seed = 1591888413; // <- e.g. this fails. // (uint64_t) time(NULL); 
    fprintf(stderr, "seed: %lu\n", seed);

    static tylo64_t tyl[THREADS];
    static sfc64_t sfc[THREADS];
    static uint64_t xo[THREADS][4];

    for (size_t i = 0; i < THREADS; ++i) {
    tyl[i] = tylo64_seed(seed + (12839732 * i), 19287319823 * i);
    sfc[i] = sfc64_seed(seed + (12839732 * i));
    xo[i][0] = xo[i][1] = xo[i][2] = xo[i][3] = seed + (12839732 * i);
    for (int j=0; j<12; ++j) xoshiro256starstar_rand(xo[i]);
    }
    static uint64_t buffer[THREADS];
    size_t n = 1024 * 1024 * 256 / THREADS;

    while (1/*n--*/) {
        for (int i=0; i<THREADS; ++i) {
        //buffer[i] = tylo64_rand(&tyl[i]);
        //buffer[i] = sfc64_rand(&sfc[i]);
            buffer[i] = xoshiro256starstar_rand(xo[i]);
        }
        fwrite((void*) buffer, sizeof(buffer[0]), THREADS, stdout);
    }
    return 0;
}

Я включу соответствующий код заголовка:

typedef struct {uint64_t a, b, w, k;} tylo64_t; // k = stream

static inline uint64_t tylo64_rand(tylo64_t* s) {
    enum {LROT = 24, RSHIFT = 11, LSHIFT = 3};
    const uint64_t b = s->b, w = s->w, out = (s->a + w) ^ (s->w += s->k);
    s->a = (b + (b << LSHIFT)) ^ (b >> RSHIFT);
    s->b = ((b << LROT) | (b >> (64 - LROT))) + out;
    return out;
}

/* stream in range [0, 2^63) */
static inline tylo64_t tylo64_seed(const uint64_t seed, const uint64_t stream) {
    tylo64_t state = {seed, seed, seed, (stream << 1) | 1};
    for (int i = 0; i < 12; ++i) tylo64_rand(&state);
    return state;
}

static inline uint64_t rotl(const uint64_t x, int k) {
    return (x << k) | (x >> (64 - k));
}
static inline uint64_t xoshiro256starstar_rand(uint64_t* s) {
    const uint64_t result = rotl(s[1] * 5, 7) * 9;
    const uint64_t t = s[1] << 17;
    s[2] ^= s[0];
    s[3] ^= s[1];
    s[1] ^= s[2];
    s[0] ^= s[3];
    s[2] ^= t;
    s[3] = rotl(s[3], 45);
    return result;
}

@ tylo-work Я ценю анализ, но мне действительно нужна эта проблема, чтобы сосредоточиться. Если вы хотите продолжить эту линию обсуждения, я рекомендую вам опубликовать свою работу в собственном репозитории Github и сделать здесь еще один пост, приглашая сюда людей. Все остальные, пожалуйста, ответьте там. Спасибо за сотрудничество.

@imneme @rkern Время выхода 1.19 на исходе.

@rkern Похоже, что PCG64DXSM не войдет в 1.19.0, я выпущу его в эти выходные. Если бы вы могли написать примечание о нашей политике изменений / предстоящих изменениях, о которых вы упомянули выше, я был бы признателен.

Извините, я занимался другими не связанными с этим вопросами. Основываясь на нашем обсуждении, я не думаю, что небольшая задержка является большой проблемой, поскольку PCG64DXSM планировался как альтернативный вариант, а не как новый вариант по умолчанию (по крайней мере, на данный момент).

Теперь, когда запускается 1.20, не пора ли вернуться к этому и перейти на DXSM?

У нас еще будет некоторое время, чтобы сделать ход перед ветвлением, но, возможно, будет хорошо начать его в течение следующей недели или около того. @bashtage Я думаю, у вас есть готовый PCG64DXSM и для этого в основном нужно решение переключить переключатель на поток по умолчанию?

Судя по тому, что он видит, это звучало так, как будто мы должны просто сделать это для 1.20, если он у нас есть в наличии.

IIRC, мы ждали ссылки, которую можно было бы связать. Но если люди, получающие случайные числа, довольны изменением, мы должны его использовать. Нужен ли нам специальный код для окон?

Это просто другая константа и другая функция скремблирования. Нет ничего более нового, чем то, что @rkern написал для оригинальной реализации PCG64 в Windows. Я думаю, что было принято решение иметь полностью автономный PCG64DXSM, а не делиться каким-то кодом (для производительности).

Вероятно, имело бы смысл начать с ветки WIP rkern .

Я сказал, что напишу об этом в блоге, что, я думаю, хотел @rkern , но я занимался некоторыми другими вопросами, и этого еще не произошло (извините). Между тем, перестановка DXSM проходит испытания и продолжает казаться улучшением по сравнению с оригиналом. Из замечаний ранее в теме, я думаю, что @rkern, возможно, понравилась бы еще более сильная перестановка вывода, но это либо стоит вам скорости, либо (если вы срезаете углы, чтобы набрать скорость) добавляет тривиальную предсказуемость.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги