Numpy: Выберите новый PRNG BitGenerator по умолчанию

Созданный на 27 мая 2019  ·  166Комментарии  ·  Источник: numpy/numpy

13163 принесет долгожданную замену инфраструктуры ГПСЧ numpy. В интересах сохранения управляемости этого PR мы объединим его в master до того, как все решения будут приняты, например, какой BitGenerator будет назначен по умолчанию.

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

С другой стороны, выбор по умолчанию не имеет , что много последствий. Мы не говорим о BitGenerator по умолчанию, лежащем в основе вспомогательных функций numpy.random.* . Согласно NEP 19, они остаются псевдонимами устаревшего RandomState , чей BitGenerator остается MT19937 . Единственное место, где используется значение по умолчанию, - это когда Generator() создается без аргументов; т.е. когда пользователь запрашивает Generator с произвольным состоянием, предположительно, чтобы затем вызвать для него метод .seed() . Вероятно, это может быть довольно редко, так как было бы так же просто явно создать его экземпляр с помощью засеянных BitGenerator которые им действительно нужны. Законный выбор здесь может быть на самом деле не выдвигать не по умолчанию и всегда требуют от пользователя указать BitGenerator .

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

ИМО, есть несколько основных вариантов (с моими комментариями, пожалуйста, не стесняйтесь не соглашаться; я не пытался перенести все соответствующие комментарии из # 13163):

По умолчанию нет

Всегда требовать Generator(ChosenBitGenerator(maybe_seed)) . Это немного недружелюбно, но, поскольку это довольно удобный способ правильно инициализировать генератор для воспроизводимости, люди все равно могут это сделать, даже если у нас есть значение по умолчанию.

MT19937

Это был бы хороший консервативный выбор. Это, конечно, не хуже, чем статус-кво. Поскольку Mersenne Twister по-прежнему широко считается «стандартным» выбором, он может помочь академическим пользователям, которым необходимо, чтобы их статьи рецензировались людьми, которые могут сомневаться в «нестандартных» вариантах, независимо от конкретных качеств ГПСЧ. «Никого не уволили за то, что наняли IBM». Основные недостатки MT19937 в основном заключаются в том, что он медленнее, чем некоторые из доступных альтернатив, из-за его очень большого состояния и что он не проходит некоторые статистические тесты качества. Выбирая другой ГПСЧ, у нас есть _возможность_ (но не _обязанность_, IMO), чтобы быть здесь самоуверенными и попытаться изменить "стандарт", если мы того пожелаем.

PCG64

Это, вероятно, тот, который я буду использовать чаще всего лично. Основным недостатком является то, что он использует 128-битную целочисленную арифметику, которая эмулируется в C, если компилятор не предоставляет такой целочисленный тип. Две основные платформы, для которых это так, - это 32-разрядные ЦП и 64-разрядный MSVC, который просто не поддерживает 128-разрядные целые числа, даже если ЦП поддерживает. Лично я не предлагаю, чтобы производительность 32-разрядных процессоров, которые становились все более редкими, зависела от нашего выбора. Однако производительность MSVC важна, поскольку нашим сборкам Windows нужен именно этот компилятор, а не другие компиляторы Windows. Вероятно, это можно решить с помощью некоторых встроенных функций сборки / компилятора, но кто-то должен их написать. Тот факт, что это _только_ MSVC, для которого мы должны это делать, делает это несколько более приемлемым, чем в других случаях, когда мы сталкиваемся со сборкой.

Xoshiro256

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

15 - Discussion numpy.random

Самый полезный комментарий

Очень вдохновленный этой веткой, у меня есть кое-какие новости…

Задний план

По многим параметрам pcg64 довольно хорош; например, согласно обычным критериям статистического качества, он получает чистый счет здоровья. Это было проверено различными способами; совсем недавно я прогнал его до полпетабайта с PractRand. Он хорошо работает в обычных случаях использования.

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

Итак, около 25 дней назад я начал думать о создании нового члена семейства PCG ...

Цель

Моя цель состояла в том, чтобы разработать нового члена семейства PCG, который мог бы стать заменой нынешнему варианту pcg64 . Как таковой:

  • Функция вывода должна шифровать биты больше, чем XSL RR (потому что это позволит избежать проблем, которые возникли в этом потоке).
  • Производительность должна быть примерно такой же (или более высокой), чем у текущего pcg64 .
  • Дизайн должен быть в духе PCG (то есть не быть тривиально предсказуемым и, следовательно, не позволять легко отменять _ любую_ работу функции вывода).

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

Спойлер

Рад сообщить об успехе! Примерно 25 дней назад, когда я впервые подумал об этом, я на самом деле был в отпуске. Когда я вернулся около десяти дней назад, я попробовал свои идеи и был рад обнаружить, что они работают хорошо. Последующее время в основном было потрачено на различные виды тестирования. Вчера был достаточно доволен, что вставил код в C ++ версию PCG. Тесты на небольших размерах показывают, что он намного лучше, чем XSL RR, и конкурирует с RXS M, но на самом деле он лучше всего подходит для больших размеров. Он также отвечает всем остальным целям.

Детали

FWIW, новая функция вывода (для случая 64-битного вывода):

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

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

Эта функция вывода основана на широко используемом xorshift-multiply. Выбор множителей: (а) уменьшить количество магических констант и (б) предотвратить отмену перестановки (если у вас нет доступа к младшим битам), а также предоставить все «рандомизированные- само по себе »качество, которое обычно имеют функции вывода PCG.

Прочие изменения

Также случается, что 0xda942042e4dd58b5 является множителем LCG для этого PRNG (и всех генераторов PCG с префиксом cm_ префиксом 0x2360ed051fc65da44385df649fccf645 используемым pcg64 , эта константа на самом деле все еще довольно хороша с точки зрения свойств спектрального теста, но ее дешевле умножать, потому что 128-битное × 64-битное проще, чем 128 бит × 128 бит. Я без проблем использовал эту константу LCG в течение нескольких лет. При использовании варианта с дешевым умножителем я запускаю функцию вывода в состоянии до итерации, а не в состоянии после итерации, для большего параллелизма на уровне инструкций.

Тестирование

Я его тщательно протестировал (PractRand и TestU01), и я им доволен. Тесты включали сценарии, описанные в этом потоке (например, использование группы генераторов на последовательных парах или увеличение на 2 ^ 64 и чередование их выходных данных - я также без проблем протестировал группу из четырех и группу из 8192 до 8 ТБ. как ручей и его противоположный аналог).

Скорость

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

Доступность

Пользователи заголовка C ++ могут получить доступ к этому новому члену семейства _now_ как pcg_engines::cm_setseq_dxsm_128_64 ; в какой-то момент в будущем я переключу pcg64 с pcg_engines::setseq_xsl_rr_128_64 на эту новую схему. В настоящее время я планирую сделать это этим летом в рамках повышения версии PCG 2.0.

Официальные объявления

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

Ваш выбор ...

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

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

Что делает компилятор Intel Windows для 128-битных целых чисел? Насколько медленнее компилируется PCG64 с MSVC по сравнению с MT1993 в Windows? Я подозреваю, что функция перехода вперед будет широко использоваться, поэтому было бы неплохо иметь ее по умолчанию.

Что делает компилятор Intel Windows для 128-битных целых чисел?

Не совсем уверен; Я не знаю, есть ли какие-то последствия ABI, которые ICC хотели бы ограничить. Если мы просто хотим получить представление о сгенерированной сборке, которую мы могли бы использовать, то это удобный ресурс: https://godbolt.org/z/kBntXH

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

Вы, скорее, имеете в виду настраиваемые потоки? Это хороший аргумент, но мне интересно, не может ли он стать другим. Если наш выбор значения по умолчанию действительно имеет большое значение, то, возможно, если мы выберем один из этих более полнофункциональных PRNG, люди будут более широко использовать эти функции в коде библиотеки, не документируя, что им требуются эти «расширенные» функции, потому что, в конце концов, они доступны "стандартные". Но затем, если другой пользователь попытается использовать эту библиотеку с менее функциональным BitGenerator за скорости или по другим причинам, то он упадет на кирпичную стену. В мире No default или MT19937 библиотеки с большей вероятностью задумаются и задокументируют необходимые им расширенные функции.

С другой стороны, такая возможность сделает BitGenerator s без устанавливаемых потоков менее желательными, и мне _до_ нравится идея продвижения того, что считается лучшей практикой в ​​этом направлении (чисто лично; я не чувствую обязательство заставить NumPy-the-project разделять это понятие). Это может помочь избежать некоторых злоупотреблений, которые я вижу у людей .seed() ing в середине их кода. Но опять же, все, что основано на представлении о том, что наличие дефолта значительно изменит поведение людей, поэтому все эти опасения, вероятно, довольно ослаблены.

Насколько медленнее компилируется PCG64 с MSVC по сравнению с MT1993 в Windows?

В тестах, опубликованных @bashtage в # 13163, скорость PCG64 почти вдвое меньше, чем у MT19937, что довольно разочаровывает по производительности MSVC и его друзей. В Linux это на 23% быстрее.

Что делает компилятор Intel Windows для 128-битных целых чисел?

Другие компиляторы, такие как Clang, GCC и компилятор Intel, реализуют 128-битные целые числа в 64-битных системах так же, как они реализовали 64-битные целые числа в 32-битных системах. Все те же техники, без новых идей. Microsoft не стала делать это для MSVC, поэтому компилятор не поддерживает 128-битные целые числа.

В результате для MSVC существующая реализация PCG64 в # 13163 вручную реализует 128-битную математику, вызывая встроенные функции Microsoft, такие как функции Intel, такие как

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

Я рад, что вам нравится забегать вперед, но мне любопытно, почему вы думаете, что это будет широко использовано. (Лично мне очень нравится distance , который показывает, насколько далеко друг от друга находятся два ГПСЧ. Это в версии PCG для C ++, но не в версии C. Это было бы достаточно тривиально, чтобы добавить его, хотя, если бы интерес.)

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

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

Microsoft не стала делать это для MSVC, поэтому компилятор не поддерживает 128-битные целые числа.

Так что это повредит нашим колесам, OTOH, многие люди в Windows получают свои пакеты от Anaconda или Enthought, оба из которых используют Intel, а люди, которые действительно заботятся о производительности, вероятно, работают на Linux, Mac или, возможно, AIX.

РЕДАКТИРОВАТЬ: И, возможно, если Microsoft обеспокоена, они могут предложить вознаграждение за решение проблемы.

FWIW, вот сборка, которую clang сгенерирует для критической функции, включая биты, необходимые для распаковки / повторной упаковки uint128_t в структуру uint64_t s: https: // godbolt.org/z/Gtp3os

Очень здорово, @rkern. Есть ли шанс сделать то же самое, чтобы увидеть, что MSVC делает с рукописным 128-битным кодом?

Очень здорово, @rkern. Есть ли шанс сделать то же самое, чтобы увидеть, что MSVC делает с рукописным 128-битным кодом?

Это не красиво. ~ https://godbolt.org/z/a5L5Gz~

К сожалению, забыли добавить -O3 , но это все равно некрасиво: https://godbolt.org/z/yiQRhd

Это не так уж и плохо. У вас не было оптимизации, поэтому ничего не было встроено. Я добавил /Ox (может есть вариант получше?). Я также исправил код, чтобы использовать встроенную встроенную функцию поворота ( _rotr64 ), поскольку, по-видимому, MSVC неспособен определить идиому поворота C.

Тем не менее, все равно что-то вроде крушения поезда. Но я думаю, будет справедливо сказать, что при небольшом внимании код PCG64 можно настроить для компиляции на MSVC во что-то, что не будет для всех совершенно неудобным.

Чтобы разрешить объединение всего остального, почему бы сейчас не выбрать вариант «нет по умолчанию»? Это дает нам возможность принять решение по умолчанию позже (даже после одного или нескольких выпусков) без нарушения совместимости.

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

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

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

Изменить: уточнить, какие разработчики должны знать, что они делают

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

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

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

О чем "что-то" вы думаете? Я не могу понять ваш аргумент.

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

О чем "что-то" вы думаете? Я не могу понять ваш аргумент.

@mattip относится к изменению генератора битов по умолчанию.

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

Например, если вы использовали

g = Generator()
g.bit_generator.seed(1234)

и основной битовый генератор был изменен, тогда это было бы неправильно.

Если бы вы сделали более разумную вещь и использовали

Generator(BitGenerator(1234))

тогда вы бы этого не увидели.

ИМО, при рассмотрении выбора значения по умолчанию мы должны думать, что он исправлен до тех пор, пока не будет обнаружена фатальная ошибка в базовом генераторе бит или Intel не добавит QUANTUM_NI к своим чипам, что дает много улучшений OOM в случайной производительности.

Я понимаю, что здесь я немного посторонний, но не думаю, что разумно ожидать, что выбор ГПСЧ по умолчанию будет фиксированным навсегда и никогда не изменится. (В C ++, например, std::default_random_engine остается на усмотрение реализации и может изменяться от выпуска к выпуску.)

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

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

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

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

(FWIW, это то, что я сделал в PCG. 32-битный PRNG PCG по умолчанию в настоящее время является вариантом XSH-RR [доступный как pcg_setseq_64_xsh_rr_32_random_r в библиотеке C и класс pcg_engines::setseq_xsh_rr_64_32 в C ++ библиотека], но в принципе, если вам действительно нужна воспроизводимость в будущем, вы должны явно указать XSH-RR, а не использовать pcg32_random_r или pcg32 которые являются псевдонимами и в принципе могут быть обновлены до чего-то другого. .)

На самом деле это не навсегда (весь этот проект на 90% основан на реальном, искреннем и выполненном навсегда обещании, сделанном около 14 лет назад), но, как вы говорите, изменение потребностей (а) веская причина для изменения и (б) будет взять хотя бы несколько лет, дать цикл амортизации.

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

Одна вещь, которая, конечно, не запрещена, - это улучшение кода PRNG после выпуска в качестве входа в систему, поскольку он дает те же значения. Например, если мы выбрали ГПСЧ, который использовал uint128, мы могли бы позволить MS добавить поддержку uint128 (большой шанс) или добавить сборку для Win64 в будущей версии.

Например, если вы использовали

g = Generator()
g.bit_generator.seed(1234)

и основной битовый генератор был изменен, тогда это было бы неправильно.

Правильно, похоже, это спор с @ eric-wieser для параметра «Нет по умолчанию», который я не могу согласовать с первоначальным утверждением «Большинство наших пользователей не являются экспертами по случайным числам, мы должны предоставить им значения по умолчанию. . "

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

В настоящее время:

Generator() # OK
Generator(DefaultBitGenerator(seed)) # OK
Generator(seed) # error

_my_ предпочтение:

Generator(1234) == Generator(DefaultBitGenerator(1234)
Generator(*args**kwargs) == Generator(DefaultBitGenerator(*args, **kwargs))

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

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

Да, у нас это есть. Пользователи могут получать BitGenerator s по имени (например, MT19937 , PCG64 и т. Д.) И создавать их экземпляры с помощью семян. Объекты BitGenerator реализуют основной унифицированный алгоритм PRNG с ограниченным набором методов для рисования однородных [0..1) float64 s и целых чисел (а также любых забавных возможностей jumpahead / stream, которые они имеют) . Класс Generator о котором мы говорим, берет предоставленный объект BitGenerator и обертывает его, чтобы обеспечить все неоднородные распределения, гауссианы, гаммы, биномы и т. Д. строгие гарантии совместимости потоков для BitGenerator s. Мы не будем ни избавляться ни от чего (что выпущено), и не будем их менять.

Центральный вопрос по умолчанию: «Что делает код g = Generator() без аргументов?» Прямо сейчас в PR он создает Xoshiro256 BitGenerator с произвольным состоянием (т. Е. Получено из хорошего источника энтропии, такого как /dev/urandom ). Вариант «Нет по умолчанию» делает это ошибкой; пользователям придется явно указать BitGenerator которые они хотят. @ eric-wieser считает, что "Нет по умолчанию" - это категорически _безопасный_ вариант для первого выпуска. Более поздний выпуск, предоставляющий значение по умолчанию, не вызовет проблем так же, как изменение существующего по умолчанию.

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

Напротив, @bashtage, похоже, заботится о генераторе по умолчанию, которому предоставляется семя.

@rkern , если вас интересует только случай _no arguments_, когда семя автоматически генерируется из доступной энтропии, тогда действительно не имеет большого значения, что такое базовый генератор - он может меняться ежечасно, поскольку результаты никогда не будут воспроизводимы ( разные пробеги получат разные семена).

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

g = Generator()
g.bit_generator.seed(seed)

Это несколько утомительно, поэтому я предположил, что, возможно, большинство людей все равно выберут Generator(PCG64(<seed>)) , так как это примерно так же удобно с точки зрения набора текста. Однако @bashtage правильно отмечает некоторое сопротивление при принятии лишнего решения.

Итак, я полагаю, что перед нами _также__ стоит более широкий вопрос: «Какими способами мы хотим, чтобы пользователи создавали один из этих способов? И если эти способы имеют настройки по умолчанию, какими должны быть эти значения по умолчанию?» У нас есть открытое пространство для дизайна, и предложения @bashtage для Generator(<seed>) или Generator(DefaultBitGenerator(<seed>)) все еще возможны.

@bashtage Как вы думаете, насколько поможет документация? То есть, если бы мы сказали вверху « PCG64 - наше предпочтительное значение по умолчанию BitGenerator » и использовали бы Generator(PCG64(seed)) последовательно во всех примерах (когда специально не демонстрируют другие алгоритмы)?

Я мог бы быть более убежденным в наличии default_generator(<seed>) _function_ вместо Generator(<seed>) или g=Generator();g.seed(<seed>) . Затем, если нам действительно нужно его изменить и мы не хотим что-то ломать, мы могли бы просто добавить новую функцию и добавить предупреждения к старой. Я мог бы порекомендовать пометить его experimental для первого выпуска, что даст нам время посмотреть на эту инфраструктуру в дикой природе, прежде чем брать на себя твердые обязательства.

А как насчет того, чтобы создать объект DefaultBitGenerator , который не раскрывает никаких деталей своего внутреннего состояния? Это будет прокси для одного из других объектов-генераторов битов, но, в принципе, он может заключать в оболочку любой из них - за исключением, конечно, конкретной последовательности сгенерированных чисел. Мы надеемся, что это отвлечет пользователей от программных предположений о том, что они могут делать с BitGenerator по умолчанию, но позволит нам использовать улучшенный алгоритм.

Я согласен с @bashtage, что было бы гораздо Generator , например, np.random.Generator(1234) . Это, конечно, будет использовать DefaultBitGenerator .

В документации для Generator мы могли бы дать полную историю того, каким был генератор битов по умолчанию в каждой прошлой версии NumPy. Это в основном предложение @imneme , и я думаю, что этого будет достаточно для воспроизводимости.

(Только что видел это изменение в предыдущем комментарии)

К сожалению, забыли добавить -O3 , но это все равно некрасиво: https://godbolt.org/z/yiQRhd

Для MSVC это не -O3 , это /O2 или /Ox (но не /O3 !).

В документации для Generator мы могли бы дать полную историю того, какой генератор битов по умолчанию был в каждой прошлой версии NumPy. Это в основном предложение @imneme , и я думаю, что этого будет достаточно для воспроизводимости.

На самом деле, даже лучше было бы включить явный аргумент version , такой как аргумент protocol pickle , в Generator / DefaultBitGenerator . Затем вы можете написать что-то вроде np.random.Generator(123, version=1) чтобы указать, что вам нужны случайные числа "версии 1" (что бы это ни было), или np.random.Generator(123, version=np.random.HIGHEST_VERSION) (поведение по умолчанию), чтобы указать, что вам нужен последний / самый большой битовый генератор. (что бы это ни было).

Предположительно version=0 будет MT19937 который NumPy использовал до сих пор, а version=1 может быть любым новым значением по умолчанию, которое мы выберем.

А как насчет фактического создания объекта DefaultBitGenerator, который не раскрывает никаких деталей своего внутреннего состояния? Это будет прокси для одного из других объектов-генераторов битов, но, в принципе, он может заключать в оболочку любой из них - за исключением, конечно, конкретной последовательности сгенерированных чисел. Мы надеемся, что это отвлечет пользователей от программных предположений о том, что они могут делать с BitGenerator по умолчанию, но позволит нам использовать улучшенный алгоритм.

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

На самом деле, даже лучше было бы включить явный аргумент version , например pickle , в Generator / DefaultBitGenerator .

Я не такой фанат. В отличие от случая pickle , у этих вещей есть значащие имена, которые мы можем использовать, и у нас уже есть реализованный механизм.

Я не такой фанат. В отличие от случая pickle , у этих вещей есть значащие имена, которые мы можем использовать, и у нас уже есть реализованный механизм.

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

  • np.random.Generator(seed, version=0) против np.random.Generator(seed, version=1)
  • np.random.Generator(MT19937(seed)) против np.random.Generator(PCG64(seed))

Я думаю, можно с уверенностью предположить, что большинство наших пользователей очень мало знают об относительных достоинствах алгоритмов ГСЧ. Но даже не читая никаких документов, они могут с уверенностью предположить, что version=1 (более новое значение по умолчанию) в большинстве случаев должно быть лучше, чем version=0 . Для большинства пользователей это действительно все, что им нужно знать.

Напротив, такие имена, как MT19937 и PCG64 , действительно значимы только для экспертов или людей, которые уже прочитали нашу документацию :).

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

И в любом случае такой уровень воспроизводимости между релизами - это то, от чего мы отказались в NEP 19 . Аргументы против версионирования дистрибутивов применимы и здесь.

Несколько мыслей о дефолте:

  • 99,9% пользователей не интересуются базовыми алгоритмами или хотят знать их, им просто нужны случайные числа. Так что +1 за самоуверенный выбор по умолчанию, пожалуйста, не заставляйте пользователей выбирать.
  • dSFMT кажется просто более быстрой версией, чем MT19937 (было бы неплохо указать в документации, насколько быстро и удалить "SSE2"). Поскольку мы в любом случае не гарантируем воспроизводимость битового потока, различия во внутреннем состоянии не очень интересны, и dSFTM следует предпочесть MT19937 даже если выигрышный аргумент здесь - "облегчить жизнь во время обзора статьи" .
  • Производительность имеет значение для значительной части пользовательской базы. Статистические свойства генераторов имеют значение только для очень небольшой части пользователей. Все включенные генераторы подходят для обычных случаев использования. Итак, +1 за выбор самого быстрого по умолчанию.

Извините, но 32-битная версия все еще имеет значение в Windows - см. Https://github.com/pypa/manylinux/issues/118#issuecomment -481404761

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

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

Для некоторых криминалистических исследований и обсуждения см .: https://arxiv.org/pdf/1810.10985.pdf

Учебники предоставляют методы, которые неявно или явно предполагают, что ГПСЧ могут быть заменены истинными переменными IIDU [0,1) без внесения существенной ошибки [20, 7, 2, 16, 15]. Мы показываем здесь, что это предположение неверно для алгоритмов во многих обычно используемых статистических пакетах, включая MATLAB, модуль случайных чисел Python, R, SPSS и Stata.

@kellieotto , @pbstark - есть ли у вас мнение о том, какой ГПСЧ мы должны выбрать здесь, чтобы дать наилучшую возможную основу для перестановки и начальной загрузки?

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

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

Для некоторых криминалистических исследований и обсуждения см .: https://arxiv.org/pdf/1810.10985.pdf

Очень интересная статья. Делается вывод, что NumPy - это почти единственная библиотека, которая понимает все правильно (вверху страницы 9), в отличие от R, Python stdlib & co.

Было бы очень полезно получить еще больше конкретных примеров, чем в статье. Если наш текущий генератор по умолчанию также выйдет из строя в какой-то момент, когда это произойдет? Примеры, такие как функция R sample генерирующая 40% четных чисел и 60% нечетных чисел при отрисовке ~ 1,7 миллиарда выборок. Какой здесь эквивалент начальной загрузки / повторной выборки?

В последней версии R (3.6) исправлен подход усечения и случайных битов.
для генерации случайных целых чисел. Mersenne Twister остается по умолчанию
ГПСЧ, однако.

@Kellie Ottoboni [email protected], и я думаю, что ГПСЧ по умолчанию в
научные языки и статистические пакеты должны быть криптографически
безопасный (CS-PRNG, например, SHA256 в режиме счетчика), с возможностью падения
вернуться к чему-то более быстрому, но более низкого качества (например, Mersenne Twister)
если этого требует скорость.

Мы работали над CS-PRNG для Python:
https://github.com/statlab/cryptorandom

Производительность невелика (пока). Похоже, что узким местом является преобразование типов.
в Python для преобразования двоичных строк (хеш-вывод) в виде целых чисел. Мы
работает над реализацией, которая переносит большую часть работы на C.

Привет,
Филип

В понедельник, 27 мая 2019 г., в 6:27 Ральф Гоммерс [email protected]
написал:

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

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

Для некоторых криминалистических исследований и обсуждения см .:
https://arxiv.org/pdf/1810.10985.pdf

Очень интересная статья. Делается вывод, что NumPy - это почти единственный
библиотека, которая понимает это правильно (начало страницы 9), в отличие от R, Python stdlib & co.

Было бы очень полезно получить еще больше конкретных примеров, чем в
бумага. Если наш текущий генератор по умолчанию также выйдет из строя в какой-то момент,
когда это? Примеры, такие как примерная функция R, генерирующая даже 40%
числа и 60% нечетных чисел при отрисовке ~ 1,7 миллиарда образцов. Что за
Эквивалент начальной загрузки / повторной выборки здесь?

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWJEIA4CTLLHVGZVKBLPXPOUFA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFMVREXWG43
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWJW445QDPGZDGXMPA3PXPOUFANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Производительность имеет значение для значительной части пользовательской базы. Статистические свойства генераторов имеют значение только для очень небольшой части пользователей. Все включенные генераторы подходят для обычных случаев использования. Итак, +1 за выбор самого быстрого по умолчанию

Во-первых, выбрать «самый быстрый» просто невозможно. @bashtage провела несколько тестов для текущего кода в # 13163, и это было повсеместно: dSFMT выигрывал в Windows и был значительно побежден PCG-64 и Xoroshiro 256 в Linux. И все это на одной машине с одним и тем же тестом. Различная аппаратная архитектура (даже версии в X86) будут иметь значение, как и разные тесты. (Как уже обсуждалось в этом потоке, PCG плохо справляется с тестами Windows из-за проблем с MSVC, что также может быть временным явлением, поскольку MSVC может улучшиться или люди могут решить его проблемы. Вероятно, подобные проблемы MSVC объясняют, почему Xoshiro был побит.)

Мне также интересно, насколько велика «значительная часть» пользователей, которым важна скорость. Сам Python в среднем примерно в 50 раз медленнее, чем C. Какая часть пользовательской базы NumPy запускает его на PyPy (что даст прирост скорости в 4 раза)? Некоторые, конечно, но я подозреваю, что не очень большое число.

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

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

Это правда, что все включенные PRNG подходят для большинства случаев использования, но это довольно низкая планка. Системы Unix поставлялись со шведским столом ГПСЧ C-библиотеки, которые все статистически ужасны, и тем не менее они широко использовались в течение многих лет, и мир не вращался вокруг его оси.

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

Для некоторых криминалистических исследований и обсуждения см .: https://arxiv.org/pdf/1810.10985.pdf

Учебники предоставляют методы, которые неявно или явно предполагают, что ГПСЧ могут быть заменены истинными переменными IIDU [0,1) без внесения существенной ошибки [20, 7, 2, 16, 15]. Мы показываем здесь, что это предположение неверно для алгоритмов во многих обычно используемых статистических пакетах, включая MATLAB, модуль случайных чисел Python, R, SPSS и Stata.

FWIW, есть хорошая статья @lemire об эффективной генерации числа в диапазоне без предвзятости. Я использовал это как отправную точку для исследования и запуска некоторых тестов в своей статье . (При генерации 64-битного кода метод Лемира действительно использует 128-битное умножение, чтобы избежать медленного 64-битного деления, со всеми знакомыми проблемами, которые могут возникнуть у пользователей MSVC.)

@pbstark @kellieotto Я с интересом прочитал вашу статью, когда она появилась на arXiv. Я был в гостях у друзей из BIDS, и они упомянули вашу работу. В разделе «Обсуждение» отмечается, что «до сих пор мы не нашли статистику с постоянным смещением, достаточно большим для обнаружения в O (10 ^ 5) повторениях» для MT19937. Вы уже его нашли? Вы нашли конкретный пример для 128-битного ГПСЧ, такого как PCG64? Мне кажется, что это разумный порог практической значимости, когда это соображение может начать перевешивать другие (ИМО), по крайней мере, с целью выбора универсального значения по умолчанию.

Приятной особенностью нашей новой структуры PRNG # 13163 является то, что она позволяет любому предоставить свой собственный BitGenerator который можно просто подключить. Он даже не должен быть в numpy, чтобы люди могли использовать его в своем numpy код. Я бы посоветовал вам взглянуть на реализацию cryptorandom как BitGenerator в C, чтобы мы могли сравнить его с другими вариантами.

Лично я ожидаю, что те, кто действительно заботится о скорости, при необходимости приложат дополнительные усилия (здесь их немного). Мы должны предоставить безопасные значения по умолчанию, и сейчас я предполагаю, что это означает безопасные значения по умолчанию для всех целей, за исключением криптографии (у нас, вероятно, должно быть предупреждение об этом в документации). Многие пользователи заботятся о скорости, но, честно говоря, именно поэтому я избегаю придавать ей слишком высокий приоритет.
Эта статья, в которой Numpy справился хорошо, кажется интересной (возможно, спасибо Роберту за то, что он правильно понял!), Но на самом деле это семплер, а не генератор бит.

@pbstark, может быть, вы захотите реализовать это как BitGenerator, совместимый с numpy / randomgen? Это, вероятно, самый простой способ ускорить его и сделать доступным для широкой аудитории в гораздо более полезной форме. Поскольку кажется, что вы и Келли Оттобони в Беркли, мы могли бы встретиться как-нибудь, чтобы начать? (Просто предложение, сначала я должен внимательно изучить код).

Что касается статьи _Random Sampling: Practice Makes Imperfect_, это хорошее чтение, но стоит помнить, что если бы у нас был 1 триллион ядер, производящих одно число за наносекунду в течение 100 лет, мы бы сгенерировали менее 2 ^ 102 чисел.

Для тривиально предсказуемых PRNG (даже с большими пространствами состояний, такими как Mersenne Twister) мы действительно можем знать, может ли когда-либо быть создана некоторая конкретная выходная последовательность (и найти семя, которое сгенерирует ее, если она существует, или задуматься, если она не будет ), но для других нетривиально предсказуемых ГПСЧ мы не можем (легко) узнать, какие выходные последовательности никогда не могут быть созданы, а какие есть, но достаточно редки, чтобы мы исчезающе маловероятно когда-либо нашли их в эоне поиска. (Как вы, возможно, знаете, у меня есть ГПСЧ, который, как я знаю, будет выдавать zip-файл с _Twelth Night_ в пределах 2 ^ 80 выходов, но удачи когда-либо его найти.)

Если вам действительно нужен криптопринг, тогда единственный выбор на современном оборудовании - это
AES, поскольку у него есть специальная инструкция. @lemire имеет реализацию
здесь https://github.com/lemire/testingRNG/blob/master/source/aesctr.h, что
работает так же быстро, как и некриптогенераторы. Также есть ChaCha20, который можно
быстро с SIMD. Однако оба будут медленными на старом оборудовании. ThreeFry и
Philox уже включены и представляют собой счетчики криптолита.

Крипто IMO переоценено с точки зрения рентабельности. Я не знаю ни одного
важный отзыв из-за проблем с ГПСЧ с Mt, который, как я полагаю, был
использовано в порядке 10e6 опубликованных статей. Единственные приложения, которые я видел
где ГПСЧ был действительно проблематичным, были случаи, когда период был таким
Мало того, что генератор завершил полный цикл. Даже здесь единственный
эффект заключался в уменьшении размера выборки исследования, которое воспроизводило основные
результаты после повторного запуска в системе с большим периодом.

27 мая 2019 г., пн, 19:50 Роберт Керн [email protected] написал:

@pbstark https://github.com/pbstark @kellieotto
https://github.com/kellieotto Я с интересом читаю вашу статью, когда она
появился на arXiv. Я был в гостях у друзей в BIDS, и у них
упомянул вашу работу. В разделе «Обсуждение» отмечается, что «пока мы не
нашел статистику с устойчивым смещением, достаточно большим, чтобы ее можно было обнаружить в
O (10 ^ 5) реплик "для MT19937. Вы уже нашли? Вы нашли
конкретный пример для 128-битного ГПСЧ, такого как PCG64? Мне кажется
быть разумным порогом практической значимости, если это соображение
может начать перевешивать другие (ИМО), по крайней мере, с целью выбора
универсальный дефолт.

Приятная особенность нашей новой структуры ГПСЧ # 13163
https://github.com/numpy/numpy/pull/13163 заключается в том, что он позволяет любому
предоставить собственный BitGenerator, который можно просто подключить. Он не
даже должны быть в numpy, чтобы люди могли использовать его в своем коде numpy. я мог бы
рекомендую вам взглянуть на реализацию крипторандома в качестве BitGenerator на C
так что мы можем сравнить его лицом к лицу с другими вариантами.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=ABKTSRO5PKW4MRFSBGUFUNTPXQUOLA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFVREXG43LVMWK3TUL52HS4DFVREXG43LVMVMWG4DFVREXG43LVMVMWK
или отключить поток
https://github.com/notifications/unsubscribe-auth/ABKTSRMRIHC4OYDR52HLTHDPXQUOLANCNFSM4HPX3CHA
.

Мне также интересно, насколько велика «значительная часть» пользователей, которым важна скорость. Сам Python в среднем примерно в 50 раз медленнее, чем C. Какая часть пользовательской базы NumPy запускает его на PyPy (что дает прирост скорости в 4 раза)? Некоторые, конечно, но я подозреваю, что не очень большое число.

Я подозреваю, что вы не обычный пользователь :) NumPy в основном состоит из C под капотом, и работает так же быстро, как и ваши собственные вещи на C (ну, в основном, быстрее). Кроме того, PyPy не готов к производству для научных приложений и в любом случае медленнее (потому что он ограничен использованием API CPython, который использует NumPy, поэтому он не может получить преимущества своего JIT).

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

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

В пн, 27 мая 2019 г., 19:46 imneme [email protected] написал:

Для некоторых криминалистических исследований и обсуждения см .:
https://arxiv.org/pdf/1810.10985.pdf

Учебники предоставляют методы, которые явно или неявно предполагают, что ГПСЧ могут
быть заменены на истинные переменные IIDU [0,1) без введения материала
ошибка [20, 7, 2, 16, 15]. Мы показываем здесь, что это предположение неверно для
алгоритмы во многих обычно используемых статистических пакетах, включая MATLAB,
Модуль случайных чисел Python, R, SPSS и Stata.

FWIW, есть хорошая статья https://arxiv.org/abs/1805.10941 от @lemire
https://github.com/lemire об эффективном создании числа в диапазоне
без предвзятости. Я использовал это как отправную точку для изучения и запуска некоторых
тесты тоже в моей статье
http://www.pcg-random.org/posts/bounded-rands.html . (При создании
64-битный, метод Лемира действительно использует 128-битное умножение, чтобы избежать медленного
64-битное деление со всеми знакомыми проблемами, которые могут возникнуть для MSVC
пользователей.)

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=ABKTSRKNAQAK4WIXG5SVLO3PXQUA3A5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFMVREXWG43
или отключить поток
https://github.com/notifications/unsubscribe-auth/ABKTSRKV3KYKRLNMBKNU4JLPXQUA3ANCNFSM4HPX3CHA
.

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

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

Я считаю, что PCG64 будет хорошим вариантом по умолчанию. Недостаток скорости в Windows не будет очевиден для людей, использующих Anaconda et. al., и, вероятно, в какой-то момент будет исправлен. Поскольку параллельное выполнение является новой вещью в Python, я также считаю, что наличие настраиваемых потоков является желательным свойством.

Я очень скептически отношусь к тому, что снижение скорости PCG64 в Visual Studio - это то, что нельзя стереть.

Это где-то тщательно оценивали?

Утверждение, что скорость имеет значение, не вызывает сомнений.

У меня вопрос - что безопасно?

Последовательно применяйте логику: «что быстро»? Я не очень понимаю, какие программы numpy на самом деле имеют производительность BitGenerator в качестве значительного узкого места. Если я использую BitGenerator который в два раза быстрее, получу ли я ускорение моего полного расчета на 5%? Наверное, даже не в этом. Python-not-be-as-as-as-as-as-C не проблема; просто даже полезные программы с ГПСЧ не тратят много времени на BitGenerator . Вероятно, достаточно любого из доступных вариантов.

Я очень скептически отношусь к тому, что снижение скорости PCG64 в Visual Studio - это то, что нельзя стереть.

Вверху я показываю, как clang компилирует PCG64 в сборку, которую мы можем украсть для 64-битного MSVC, так что нет, я не думаю, что MSVC в 64-битной Windows является непреодолимой проблемой.

Что может быть сложнее, так это PCG64 в 32-битных системах, из которых только 32-битная Windows может быть для нас практически важной. В этом случае речь идет не столько о MSVC, сколько о том, чтобы ограничиться 32-битным ISA.

Мы с @Kellie Ottoboni [email protected] отмечаем , что
даже для задач небольшого размера пространство состояний MT слишком мало, чтобы приблизиться
равномерные перестановки (n <2100) или однородные случайные выборки (n = 4e8, k = 1000).

Это влияет на все, от начальной загрузки до тестов перестановки и MCMC.
Разница между предполагаемым распространением и фактическим
распределение может быть сколь угодно большим (полное расстояние вариации приближается
2). Он большой и серьезный.

Мы не прилагали никаких усилий для того, чтобы сломать МП для "статистических" функций в
пара лет. Я почти уверен, что есть систематический способ сломать это
(поскольку расстояния распределения настолько велики).

Привет,
Филип

В понедельник, 27 мая 2019 г., в 12:26 Роберт Керн [email protected]
написал:

Утверждение, что скорость имеет значение, не вызывает сомнений.

У меня вопрос - что безопасно?

Последовательно применяйте логику: «что быстро»? У меня нет отличной идеи
какие numpy программы на самом деле имеют производительность BitGenerator как
значительное узкое место. Если я использую BitGenerator в два раза быстрее,
получу ли я ускорение моего полного расчета на 5%? Наверное, даже не в этом.
Python-not-be-as-as-as-as-as-C не проблема; просто это даже
Программы с тяжелым ГПСЧ, которые действительно полезны, не тратят много
время в BitGenerator. Вероятно, любой из доступных вариантов
достаточно.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWKSMPAG3GFUCUFRXCDPXQYVRA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFVREXG43VLWWK3TUL52HS4DFVREXG63V2HS4DFVREXG43VLH4DFVREXG43V2
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWIDCPAJJ6DJ3RO332LPXQYVRANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

@pbstark Я бы хотел увидеть конкретную реализацию проблемы (может быть искусственной, но не слишком надуманной), при которой MT или 128-битный ГПСЧ не работают и cryptorandom будет работать. Можете ли вы указать на набор данных, где метод повторной выборки дает неправильные выводы с помощью 128-битного ГПСЧ и правильные выводы с помощью cryptorandom ?

Переход на PCG64 ухудшает нижнюю границу размера проблемы,
поскольку его пространство состояний даже меньше, чем у MT. Конечно, это могло
по-прежнему производит "лучшую" случайность, поскольку может производить выборку подгруппы
группа перестановок более равномерна, чем MT. Но он должен сломаться, прежде чем
500 выбираем 10, а до 21 !.

Привет,
Филип

В понедельник, 27 мая 2019 г., в 12:30 Роберт Керн [email protected]
написал:

Я очень скептически отношусь к тому, что потеря скорости PCG64 в Visual Studio
то, что невозможно стереть.

Вверху я показываю, как clang компилирует PCG64 в сборку, которую мы можем украсть
для 64-битного MSVC, поэтому я не думаю, что MSVC в 64-битной Windows
непреодолимая проблема.

Что может быть сложнее, так это PCG64 в 32-битных системах, из которых только 32-битные
Windows по-прежнему может быть для нас практически важна. В таком случае меньше
о MSVC, чем об ограничении 32-битной ISA.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWJFCINQCYGFCI7ULI3PXQZGLA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFMVREXG6
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWK6QTB65Z4TJU76XKTPXQZGLANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

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

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

Я тоже хотел бы это увидеть.

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

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

В понедельник, 27 мая 2019 г., в 12:39 Роберт Керн [email protected]
написал:

@pbstark https://github.com/pbstark Я бы хотел увидеть конкретный
реализация задачи (может быть искусственной, но не слишком надуманной) на
какой MT или 128-битный PRNG не работает, и крипто-случайный будет работать. Ты можешь
укажите набор данных там, где метод повторной выборки дает неправильные
заключения с 128-битным ГПСЧ и правильные выводы с крипто-случайным?

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWODTUIPNMVOJB6QP3DPXQ2FPA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFMVREXGUG6
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWITAGQFZDQSIFNEHETPXQ2FPANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

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

В понедельник, 27 мая 2019 г., в 12:48 Себастьян Берг [email protected]
написал:

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

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWPOBTPYHC3XBINQYA3PXQ3HZA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS443DFMVREX54
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWOEB7KR2YJZWHRRAHLPXQ3HZANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

@pbstark MT не проходит ряд статистических тестов, которые проходят PCG (и другие генераторы).

@rkern

Если кто-то хочет, чтобы MSVC сгенерировал инструкцию ror, я думаю, что нужно использовать внутреннюю функцию "_rotr64".

Также можно предпочесть флаг «/ O2» для оптимизации.

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

Для @pbstark вот некоторые выходные данные PCG-64, инициализированные неизвестным вам семенем (на самом деле, я даже скажу вам поток, это 0x559107ab8002ccda3b8daf9dbe4ed480 ):

  64bit: 0x21fdab3336e3627d 0x593e5ada8c20b97e 0x4c6dce7b21370ffc
     0xe78feafb1a3e4536 0x35a7d7bed633b42f 0x70147a46c2a396a0
  Coins: TTTHHTTTHHTHTTTTHTHHTTTTTHTTHHTTHHTHHTHHHHHHHHTTHHTTHHTHHHHHTHTHH
  Rolls: 5 3 5 2 5 3 1 6 6 5 4 4 5 5 5 6 2 3 5 3 2 3 2 5 6 2 4 6 2 3 4 6 3
  Cards: 5h 3h 3c 8d 9h 7s Kh Ah 5d Kc Tc 6h 7h 8s Ac 5c Ad Td 8c Qd 2h As
     8h 2d 3s 5s 4d 6d 2s Jd 3d 4h Ks 6s Qc Js Th 9d 9c Ts Jh 4c 2c 9s
     6c 4s 7c 7d Jc Qs Kd Qh

Теперь предположим, что вы инициализируете другой генератор pcg случайно выбранным начальным числом. Выберем для аргумента 0xb124fedbf31ce435ff0151f8a07496d3 . Сколько результатов мы должны произвести, прежде чем обнаружим этот известный результат? Поскольку я знаю семя, которое использовал выше, я могу ответить на это (с помощью функции расстояния PCG) примерно 2,5 × 10 ^ 38 (или примерно 2 ^ 127,5) выходных данных. Для справки, 10 ^ 38 наносекунд - это в 230 миллиардов раз больше возраста Вселенной.

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

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

(Кроме того, не гарантируется, что криптографически безопасные ГПСЧ будут равнораспределены в k-мерном масштабе, и они не являются волшебной пулей для людей, которым нужны ГПСЧ, которые могут генерировать любую возможную последовательность. В тот момент, когда вы хотите получить больше битов из ГПСЧ, чем он принимает в качестве своего seed и сохраняет в качестве своего состояния, обязательно есть некоторые битовые последовательности, которые не могут быть сгенерированы (доказательство: по принципу голубятни). И если вы ограничиваете себя тем же количеством выходных данных, которое вы вводите в качестве seed, то что вы re действительно ищет хеш-функцию или, может быть, просто функцию идентификации, если ваш начальный ввод действительно случайный, а не PRNG.)

Из любопытства я завершил генератор битов счетчика AES, используя aesctr.h , время в нс на случайное значение:

+---------------------+--------------+----------+--------------+
|                     |   Xoshiro256 |    PCG64 |   AESCounter |
|---------------------+--------------+----------+--------------+
| 32-bit Unsigned Int |      3.40804 |  3.59984 |      5.2432  |
| Uniform             |      3.71296 |  4.372   |      4.93744 |
| 64-bit Unsigned Int |      3.97516 |  4.55628 |      5.76628 |
| Exponential         |      4.60288 |  5.63736 |      6.72288 |
| Normal              |      8.10372 | 10.1101  |     12.1082  |
+---------------------+--------------+----------+--------------+

Отличная работа, @bashtage.

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

Кроме того, это немного похоже на сравнение яблок с апельсинами. Помимо использования специализированных инструкций, код AES получает часть своей скорости от развертывания цикла - он фактически генерирует числа в блоках, а затем считывает их. Развертывание потенциально может ускорить любой ГПСЧ. FWIW, @lemire на самом деле имеет векторизованную версию PCG, которая использует инструкции AVX для одновременной генерации нескольких выходных данных.

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

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

Что я действительно имел в виду под опцией «Нет по умолчанию», так это «Без анонимного по умолчанию». Есть еще способы, которыми мы можем разработать API так, чтобы наиболее удобный способ получить начальный Generator - это тот, который называет ГПСЧ, который мы назначаем. Например, предположим, что мы _не_ включаем полный набор BitGenerator алгоритмов. Мы стараемся поддерживать минимальное количество numpy и оставляем завершенность scipy и другим сторонним библиотекам в целом, и это может быть хорошей идеей сделать это здесь. Прелесть нынешней архитектуры в том, что она позволяет нам переместить эти BitGenerator в другие библиотеки. Итак, допустим, мы предоставляем только MT19937 для поддержки устаревшего RandomState и BitGenerator который мы предпочитаем людям использовать. Допустим, это Xoshiro256 . Сделаем так, чтобы конструктор Generator.__init__() требовал BitGenerator . Но также давайте определим функцию np.random.xoshiro256_gen(seed) которая возвращает Generator(Xoshiro256(seed)) под крышками. Мы задокументировать эту удобную функцию , как способ получить затравку Generator .

Теперь перенесемся на несколько выпусков вперед. Допустим, мы оттолкнули PCG64 , ThreeFry и т. Д. На random-tng или scipy или какой-либо другой пакет, и один из них стал популярным благодаря дополнительные функции или новые статистические ошибки находятся в Xoshiro256 . Мы решили, что хотим обновить мнение numpy о том, какие BitGenerator люди должны использовать до PCG64 . Затем мы добавляем класс PCG64 BitGenerator и добавляем функцию np.random.pcg64_gen(seed) . Мы добавляем предупреждение об устаревании к np.random.xoshiro256_gen(seed) чтобы сказать, что это больше не предпочтительный алгоритм: мы рекомендуем, чтобы новый код использовал np.random.pcg64_gen(seed) , но чтобы продолжать использовать алгоритм Xoshiro256 без предупреждения, они должны явно использовать Generator(Xoshiro256(seed)) .

Я думаю, это позволяет избежать некоторых проблем, которые возникают у меня с «анонимным» API (например, Generator(seed) , Generator(DefaultBitGenerator(seed)) , np.random.default_gen(seed) ). Мы можем поддерживать алгоритмы, которые больше не являются предпочтительными, на неограниченный срок. Нам никогда не придется заставлять наш самоуверенный «предпочтительный» конструктор делать что-то другое, когда мы меняем свое мнение. Поскольку для различения вещей мы используем настоящие имена, а не номера версий, вы всегда знаете, как обновить код, чтобы воспроизвести старые результаты (если вы по какой-то причине не переносите безобидные предупреждения). Вы даже можете взять код без происхождения или записанного количества выпусков и выполнить это обновление. В то же время, ограничивая количество алгоритмов до абсолютного минимума и делая лучшие практики _ самым простым_ способом работы с библиотекой, мы можем эффективно выразить мнение numpy.

Как это звучит? Мы все же должны приложить много усилий, чтобы сделать твердый выбор в пользу этих BitGenerator для первого выпуска. Это все еще имеет значение.

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

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

Если кто-то хочет, чтобы MSVC сгенерировал инструкцию ror, я думаю, что нужно использовать внутреннюю функцию "_rotr64".

Также можно предпочесть флаг «/ O2» для оптимизации.

Благодаря! @imneme указал

@seberg Есть ли у вас какие-либо цитаты из вашей области, которые вызвали вашу настороженность? Например, статья, которая показала, что свойство эквираспределения k = 623 MT19937 устраняет проблемы в моделировании нелинейной динамики, которые вызывали меньший ГПСЧ? Я мог бы предоставить более конкретное заверение, взяв это в качестве ссылки. В общем, мой взгляд на равнораспределение заключается в том, что вы обычно хотите, чтобы равнораспределение PRNG было близко к максимуму, разрешенному размером состояния PRNG. В _practice_, если ваш ГПСЧ достаточно велик для ваших целей в других отношениях (проходит PractRand, имеет период больше квадрата числа выборок, которые вы планируете нарисовать, и т. Д.), Я никогда не видел особых причин для беспокойства по поводу точный k . У других могут быть разные мнения, и, возможно, в вашей области есть конкретные проблемы, о которых я не знаю. Если это так, то есть конкретные решения!

Из любопытства я завернул генератор битов счетчика AES, используя aesctr.h

Я могу ошибаться, но не верю, что это поможет решить проблемы k! перестановок для значительных k . Счетчик по-прежнему представляет собой 128-битное число, и как только он перевернется, вы достигнете конца периода. @pbstark защищает ГПСЧ с очень большим периодом, большинство из которых просто являются CS-RNG.

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

Хотя некоторые считают максимальное равнораспределение желательным свойством, это также можно считать недостатком (и есть статьи, в которых говорится об этом). Если у нас есть _k_-битный ГПСЧ и каждая _k_-битовая последовательность встречается ровно один раз, это в конечном итоге приведет к нарушению проблемы дня рождения, которая говорит, что мы ожидаем увидеть повторение вывода примерно после 2 ^ (k / 2) выходов. ( Я написал статистический тест проблемы дня рождения, основанный на этих идеях . Он правильно обнаружил статистически неправдоподобное отсутствие каких-либо повторов в SplitMix, 64-битный ГПСЧ с 64-битным выходом и Xoroshiro64 +, 32-битный выход 64-битный -состояние двухмерно-равнораспределенного ГПСЧ, среди прочего.)

Интересно, что, хотя очень практично написать статистический тест, который не сможет выполнить ГПСЧ из-за отсутствия 64-битных повторов (или слишком большого количества повторов - мы ожидаем распределения Пуассона), наоборот _ непрактично написать тест, который будет обнаружить пропущенные 36,8% всех 64-битных значений, если мы ничего не знаем о том, какие из них пропущены.

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

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

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

Есть множество свойств, по которым конечные ГПСЧ не соответствуют истинным ГСЧ. И есть много вычислений, которые мы хотим сделать, которые теоретически зависят от этих свойств истинных ГСЧ (или, по крайней мере, мы не доказали строго, насколько мы можем их расслабить). Но многие из этих недостатков имеют очень небольшое, практически незаметное влияние на результаты реальных вычислений, которые мы выполняем. Эти нарушения не являются диспозитивными, принципами «все или ничего». У них есть размер эффекта, и мы можем терпеть более или менее эффект.

Вы, конечно, убедительно показываете, что ГПСЧ определенного размера не могут равномерно генерировать все k! перестановки для некоторых k . Шаг, который мне не хватает, - это то, как эта неспособность сгенерировать все эти перестановки влияет на конкретное вычисление, которое мне было бы интересно выполнить. Мне не хватает теста, который вы бы порекомендовали добавить в PractRand или TestU01, который продемонстрировал бы проблему людям.

Одна из линий анализа, которую я нашел очень информативной из статьи @imneme о PCG, заключалась в том, чтобы получить несколько версий каждого PRNG с меньшим состоянием и увидеть, где именно они начали давать сбои TestU01. Это дает нам способ обычно сравнивать архитектуры ГПСЧ, а не просто говорить «все или ничего»: «X PRNG проходит» или не работает. Это также позволяет нам оценить, какой у нас запас при используемых размерах состояний (которые проходят TestU01 при некотором большом количестве ГиБ выборок). Есть ли конкретные вычисления, которые вы можете сделать, чтобы продемонстрировать проблему с 8-битными ГПСЧ? 16-битные ГПСЧ? Затем мы увидим, сможет ли этот новый тест сообщить нам информацию о PRNG раньше, чем это делает TestU01 / PractRand в настоящее время. И это было бы очень полезно.

С другой стороны, если требуется _ больше_ данных, извлеченных из ГПСЧ, чтобы показать сбой на основе этих перестановок, чем точка отказа малых ГПСЧ для текущего набора тестов в PractRand, тогда я бы пришел к выводу, что эта проблема не имеет практического значения. беспокойство, и мы могли бы использовать «передает PractRand» в качестве хорошего прокси для определения того, будет ли проблема перестановки иметь практический эффект на мои вычисления.

Но пока у меня в руках не будет программа, которую я могу запустить и показать людям проблему, мне неудобно настаивать на CS-PRNG на этой основе. Я не смог бы убедительно объяснить свой выбор.

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

Таким образом, я бы (шутливо) заявил, что порядок, в котором происходит тасование, также не должен быть предопределен! Ясно, что вы должны требовать, чтобы он выводил все 32! тасует во всех возможных порядках - (32!)! это то, что вам нужно! Конечно, для этого потребуется 3,8 × 10 ^ 18 эксабайт для состояния и такое же количество энтропии для инициализации, но, безусловно, стоит знать, что все есть.

... Мы добавляем предупреждение об устаревании в np.random.xoshiro256_gen(seed) чтобы сказать, что это больше не предпочтительный алгоритм: мы рекомендуем, чтобы новый код использовал np.random.pcg64_gen(seed) , но чтобы продолжать использовать алгоритм Xoshiro256 без предупреждений, они должны явно использовать Generator(Xoshiro256(seed))

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

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

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

Что я действительно имел в виду под опцией «Нет по умолчанию», так это «Без анонимного по умолчанию».

Так вы хотите, чтобы пользователи знали имя ГПСЧ, которое они используют?

Посмотрите на это с точки зрения пользователей. Достаточно сложно заставить их перейти от np.random.rand & co к np.random.RandomState() а затем использовать методы. Теперь мы собираемся представить лучшую систему, и что они увидят, это np.random.xoshiro256_gen() ? Это было бы серьезным регрессом с точки зрения удобства использования.

Так вы хотите, чтобы пользователи знали имя ГПСЧ, которое они используют?

Нет, это для смягчения проблем, связанных с наличием API «назначенной движущейся цели», такого как default_generator(seed) , над которым работали люди (например, аргумент @shoyer version ).

Поддержание совместимости потоков (что отрицает NEP 19) вторично по отношению к поломке API. Различные BitGenerator имеют разные эффективные API, в зависимости от их наборов функций (в первую очередь, settable-streams, jumpahead, хотя могут быть и другие, в зависимости от того, насколько параметризуем PRNG). Таким образом, некоторые изменения в нашем выборе PRNG по умолчанию фактически нарушат код (то есть больше не запускаются или больше не работают правильно), а не только изменят значения, которые выходят.

Например, допустим, мы сначала выбираем PCG64 . Он имеет 128-битное состояние, 2 ^ 127 настраиваемых потоков и реализует jumpahead; красиво и полнофункционально. Итак, люди начинают писать default_generator(seed, stream=whatever) . Теперь предположим, что будущая работа обнаружит в ней серьезный статистический недостаток, который заставляет нас захотеть переключиться на что-то другое. Следующий PRNG, который мы продвигаем по умолчанию, должен иметь> = 128-битное состояние (легко; я бы не рекомендовал что-то меньшее в качестве универсального значения по умолчанию), jumpahead (сложно!),> = 2 ^ 127 настраиваемых потоков (ууу, мальчик!), чтобы не нарушить использование default_generator() которые уже существуют в коде. Теперь, может быть, мы сможем жить с этим храповым механизмом.

@shoyer предположил, что, возможно, мы могли бы сделать BitGenerator по умолчанию всегда намеренно ограниченными функциями с наименьшим общим знаменателем. Это сработает! Но он также упустил бы возможность продвигать настраиваемые потоки для решения проблемы параллельных потоков, как хотел бы

Теперь мы собираемся представить лучшую систему, и что они увидят, это np.random.xoshiro256_gen() ? Это было бы серьезным регрессом с точки зрения удобства использования.

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

Я также в порядке, если мы решим жить с храповым механизмом и избегать механизма version .

Я считаю, что "по умолчанию" мы можем оставить его как деталь реализации, чтобы Generator() всегда работал. Я хотел бы обсудить это с серьезным предупреждением о том, что единственный способ получить всегда воспроизводимые результаты (вплоть до изменений в Генераторе) - это использовать синтаксис Generator(BitGenerator(kwarg1=1,kwargs2=b,...))

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

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

@bashtage в # 13650 Я запретил доступ к Generator().bit_generator способом, который по-прежнему позволяет травление без прямого доступа к state . Он передает слегка переписанный test_pickle таким образом, чтобы его можно было использовать в Python Thread s

У меня вопрос - что безопасно? Есть просто разные степени квазислучайности с разными свойствами. До сих пор я не видел, чтобы кто-то приводил конкретный пример, ни здесь, ни в других вопросах, PR или темах.

«Проходит PractRand в _N_ ГиБ» для некоторых _N_ (512, 1024) является приемлемым определением, если вам нужен четкий критерий «прошел / не прошел». Если вам нужен конкретный пример, МП и его варианты будут исключены на основании этого критерия. Из-за этого мы также удалили некоторых старших членов семьи Ксороширо из PR.

Если вам нужно более сложное представление о статистическом качестве, которое позволяет ранжировать алгоритмы по порядку , я рекомендую вам Раздел 3 статьи @imneme PCG, в которой используются профили вариантов алгоритмов с сокращенным состоянием, чтобы понять, как большой «запас» у каждого полного алгоритма. Это очень похоже на то, как криптографы анализируют различные криптоалгоритмы. Любой изучаемый жизнеспособный вариант должен соответствовать базовому критерию «не нарушен», но это не поможет вам ранжировать претендентов. Вместо этого они создают версии алгоритмов соперника с уменьшенным числом раундов и смотрят, насколько сокращенными должны стать ваши алгоритмы, прежде чем они смогут его сломать. Если полный алгоритм N-раундов нарушен на N-1, тогда запаса будет очень мало, и криптографы, вероятно, избежали бы этого. Точно так же, если 128-битный BitGenerator проходит PractRand, но его 120-битная версия не работает, это, вероятно, довольно рискованно.

@mattip Это кажется разумным. Хотя кто-то где-то будет

import gc
state = [o for o in gc.get_objects() if 'Xoshiro256' in str(o)][0].state

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

Он передает слегка переписанный test_pickle таким образом, чтобы его можно было использовать в Python Thread s

Стоит отметить, что это нерешенная проблема (# 9650) - в идеале Generator() будет повторно загружаться в дочерние потоки. IIRC это практично только в Python> = 3.7

Я считаю, что "по умолчанию" мы можем оставить его как деталь реализации, чтобы Generator() всегда работал. Я хотел бы обсудить это с серьезным предупреждением о том, что единственный способ получить всегда воспроизводимые результаты (вплоть до изменений в Генераторе) - это использовать синтаксис Generator(BitGenerator(kwarg1=1,kwargs2=b,...))

Нам нужно различать два типа воспроизводимости. Во-первых, я дважды запускаю свою программу с одним и тем же семенем и получаю одинаковые результаты. Это то, что нам нужно поддерживать. Другой - это воспроизводимость между версиями numpy, от которой мы отказались, по крайней мере, в самом строгом смысле.

Generator() аргументов, т.е. «дайте мне произвольно выбранный PRNG, который рекомендует numpy», не является основным вариантом использования. Это не требует особой поддержки. «Дайте мне ГПСЧ, который рекомендует numpy с _this_ seed», и это то, что мы обсуждаем варианты. Нам нужен способ для numpy, чтобы выразить мнение о том, как получить заполненный PRNG, и этот способ должен быть простым и удобным для пользователей (иначе они не будут его использовать). Мне нравится давать название алгоритму (хотя и с помощью более удобной функции), но @rgommers считает, что это слишком далеко, и я сочувствую этому.

Генератор без аргументов (), то есть «дайте мне произвольно выбранный PRNG, который рекомендует numpy», не является основным вариантом использования. Это не требует особой поддержки. «Дайте мне PRNG, который рекомендует numpy с этим семенем», и это то, что мы обсуждаем варианты. Нам нужен способ для numpy, чтобы выразить мнение о том, как получить заполненный PRNG, и этот способ должен быть простым и удобным для пользователей (иначе они не будут его использовать). Мне нравится называть алгоритм (хотя и с помощью более удобной функции), но @rgommers считает, что это слишком далеко, и я с пониманием отношусь к этому.

Я бы сказал, что пользователи на самом деле плохо оснащены, чтобы давать хорошие семена. Например, сколько пользователей знают, как правильно засеять Mersenne Twister? Это не так просто, как вы думаете - если вы не скармливаете ему 624 случайных 32-битных целых числа (чтобы обеспечить 19937 бит состояния), вы делаете это неправильно.

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

Если речь идет только о правильном пути, тогда я за
Generator(BitGenerator(**kwargs)) так как это будет использоваться только
пользователи полупрограммного обеспечения, которые заботятся о воспроизведении.

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

Просто чтобы выбросить еще один, метод класса Generator.seeded(seed[, bit_generator]) where bit generator is a string. This would allow the pattern of switching from one value to None to warn if the default was going to change, like lstsq. I would also only support a limited pallatte of but generators initially (i.e. 1). Doesn't make it easy to expose advanced features I suppose. In a perfect world it would use kwarg only to allow any keyword argument to be used which avoids most depreciation problems. Of course, this doesn't really need to be a class function, just seded`.

Во вторник, 28 мая 2019 г., 16:38 Роберт Керн [email protected] написал:

Я считаю, что "по умолчанию" мы можем оставить его как реализацию
подробно, чтобы генератор () всегда работал. Я бы обсудил это с
Сильное предупреждение о том, что единственный способ получить всегда воспроизводимые результаты
(до изменений в Генераторе) - использовать синтаксис
Генератор (BitGenerator (kwarg1 = 1, kwargs2 = b, ...))

Нам нужно различать два типа воспроизводимости. Один
что я дважды запускаю свою программу с одним и тем же семенем и получаю одинаковые результаты.
Это то, что нам нужно поддерживать. Другой - воспроизводимость
версии numpy, от которых мы отказались, по крайней мере, в самом строгом смысле.

Генератор без аргументов (), т.е. «дайте мне произвольно выбранный ГПСЧ, который
numpy рекомендует "не является основным вариантом использования. Для этого не требуется много
служба поддержки. "Дайте мне PRNG, который рекомендует numpy с этим семенем",
и мы обсуждаем варианты. Нам нужен способ для numpy
выразить мнение о том, как получить посевной ГПСЧ, и этот способ должен быть
легко и удобно для пользователей (иначе они не будут его использовать). Мне нравится
именование алгоритма (пусть и более удобной функцией), но
@rgommers https://github.com/rgommers считает, что это слишком далеко, и
Я сочувствую этому.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=ABKTSRKA4SSNW6XZEVFUMCDPXVGW5A5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFVREXG63VLWWK3TUL52HS4DFVREXG43VLWWK3TUL52HS4DFVREXG43VL
или отключить поток
https://github.com/notifications/unsubscribe-auth/ABKTSROCMLHG6E6BLWI6TWDPXVGW5ANCNFSM4HPX3CHA
.

Во вторник, 28 мая 2019 г., 16:38 Роберт Керн [email protected] написал:

Я считаю, что "по умолчанию" мы можем оставить его как реализацию
подробно, чтобы генератор () всегда работал. Я бы обсудил это с
Сильное предупреждение о том, что единственный способ получить всегда воспроизводимые результаты
(до изменений в Генераторе) - использовать синтаксис
Генератор (BitGenerator (kwarg1 = 1, kwargs2 = b, ...))

Нам нужно различать два типа воспроизводимости. Один
что я дважды запускаю свою программу с одним и тем же семенем и получаю одинаковые результаты.
Это то, что нам нужно поддерживать. Другой - воспроизводимость
версии numpy, от которых мы отказались, по крайней мере, в самом строгом смысле.

Генератор без аргументов (), т.е. «дайте мне произвольно выбранный ГПСЧ, который
numpy рекомендует "не является основным вариантом использования. Для этого не требуется много
служба поддержки. "Дайте мне PRNG, который рекомендует numpy с этим семенем",
и мы обсуждаем варианты. Нам нужен способ для numpy
выразить мнение о том, как получить посевной ГПСЧ, и этот способ должен быть
легко и удобно для пользователей (иначе они не будут его использовать). Мне нравится
именование алгоритма (пусть и более удобной функцией), но
@rgommers https://github.com/rgommers считает, что это слишком далеко, и
Я сочувствую этому.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=ABKTSRKA4SSNW6XZEVFUMCDPXVGW5A5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFVREXG63VLWWK3TUL52HS4DFVREXG43VLWWK3TUL52HS4DFVREXG43VL
или отключить поток
https://github.com/notifications/unsubscribe-auth/ABKTSROCMLHG6E6BLWI6TWDPXVGW5ANCNFSM4HPX3CHA
.

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

Для управления версиями генератора битов по умолчанию в Generator мы могли бы использовать bit_version=1 вместо version=1 , хотя я тоже согласен отказаться от идеи version . Я не думаю, что пользователям нужно будет часто явно устанавливать генераторы битов.

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

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

пользователи просто собираются придерживаться RandomState.

Это нормально, они не первые последователи. Я очень стараюсь (может быть, слишком сильно?) Для минимально возможного жизнеспособного API, так как мы всегда можем расширить API, но гораздо сложнее его сжать. Нюансы между Generator(Philox()) , Generator(seed(3)) и Generator(bit_version=1) немного трудно увидеть, пока это не станет известно конечным пользователям.

Давайте выпустим первую версию без Generator(seed) и получим отзывы.

Давайте выпустим первую версию без Generator(seed) и получим отзывы.

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

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

Я говорю то же самое, но меня это мало привлекает. Как вы говорите: «Ну, если что-то является плохой идеей, это не значит, что люди не захотят этого делать!»

Частично проблема усугубляется огромным состоянием MT, которое действительно требует сериализации в файл. Просто сложно сделать этот файловый танец самым простым доступным API, чтобы пользователи захотели его использовать. Будет лучше с ГПСЧ по умолчанию с гораздо меньшим состоянием. 128 бит - это размер UUID, который достаточно мал для распечатки в шестнадцатеричном формате и копирования-вставки. Так что хорошим шаблоном может быть написание вашей программы так, чтобы она по умолчанию использовала хорошее начальное значение энтропии, а затем распечатывала свое состояние таким образом, чтобы вы могли скопировать и вставить его при следующем запуске программы.

❯ python secret_prng.py
Seed: 0x977918d0c7da45e5168f72005586500c
...
Result = 0.7223650399276123

❯ python secret_prng.py
Seed: 0xe8962534e5fb585483b86119fcb852ce
...
Result = 0.10640984721018876

❯ python secret_prng.py --seed 0xe8962534e5fb585483b86119fcb852ce
Seed: 0xe8962534e5fb585483b86119fcb852ce
...
Result = 0.10640984721018876

Не уверен, что такой шаблон обеспечит простоту и надежность в будущем.

NumPy 1.следующий

class Generator:
    def __init__(bitgen_or_seed=None, *, bit_generator='pcg64', inc=0):

NumPy 1.20.x

`` питон
Генератор классов:
def __init __ (bitgen_or_seed = None, *, bit_generator = None, inc = None):
если bit_generator не равен None или inc не равен None:
warn ('По умолчанию меняется с PCG64 на AESCtr. Ключевое слово inc'
'аргумент устарел и будет расти в будущем', FutureWarning)
`` ''

NumPy 1.22

`` питон
Генератор классов:
def __init __ (bitgen_or_seed = None, *, bit_generator = 'aesctr', inc = None, counter = 0):
если bit_generator == 'pcg64' или inc не равен None:
поднять исключение ('PCG больше не поддерживается, и inc был удален')
`` ''

Не уверен, что такой шаблон обеспечит простоту и надежность в будущем.

Как я уже отмечал выше в https://github.com/numpy/numpy/issues/13635#issuecomment -496589421, я думаю, что это было бы неожиданностью и разочарованием для большинства пользователей. Я бы предпочел предоставить явный объект BitGenerator, чем планировать запуск предупреждений, если пользователи не установили все необязательные аргументы. Это действительно должно быть последним средством в случаях, когда мы обнаруживаем, что API-интерфейсы работают не так, как мы ожидали.

Проблема в переходном периоде. Каждый, кто использует настройки по умолчанию, внезапно получит предупреждения о невозможности переключения в течение переходного периода. Или, по крайней мере, вы переводите их из «состояния с низкой энергией» Generator(seed) в менее удобное «состояние с высокой энергией» Generator(seed, bit_generator='aesctr') . Поскольку целью этого API было обеспечение удобного «состояния с низким энергопотреблением», мы не достигли своей цели во время этого перехода. Однажды мы сделали это с помощью одной из наших функций гистограммы, IIRC, и это был кошмар.

Это характерно для всех устаревших версий, которые пытаются изменить значение аргументов на месте. Устарениями, которые _переносят_ вас с одной функции на другую, гораздо легче управлять, и это то, что я защищал.

Давайте выпустим первую версию без Generator(seed) и получим отзывы.

Под "первой версией" вы подразумеваете полную версию numpy? Или просто объединить PR (что с тех пор и произошло)?

Если выпуск полный numpy, нам еще нужно определить некоторые вещи, например, сколько BitGenerator s мы включаем. Если мы включим полный текущий комплект, то мы раскрыли некоторые варианты.

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

+1 согласен

Нет, это для смягчения проблем, связанных с наличием API «назначенной движущейся цели», такого как default_generator (seed), с которым люди работали (например, аргумент версии @shoyer ).

Поддержание совместимости потоков (что отрицает NEP 19) вторично по отношению к поломке API. У разных BitGenerator разные эффективные API

Хорошо, теперь для меня это имеет больше смысла.

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

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

Что-то вроде np.random.generator или np.random.default_generator .

сколько BitGenerator мы включаем

Не могли бы вы открыть отдельный вопрос с предложением отказаться от тех, которые, по вашему мнению, мы должны удалить из включенных в настоящее время (MT19937, DSFMT, PCG32, PCG64, Philox, ThreeFry, Xoshiro256, Xoshiro512)?

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

Что ж, эта проблема больше связана с тем, «какой из них следует продвигать как выдающийся BitGenerator », что влияет на выбор по умолчанию, но также и какие из них следует включить или удалить. Механизм, с помощью которого мы предоставляем значения по умолчанию (если мы предоставляем значения по умолчанию), добавляет некоторые ограничения, так что все это более или менее необходимо решать вместе. Это большой беспорядок, и после всей работы, которую вы проделали, направляя этот PR, я уверен, что вы устали, увидев еще одну мега-ветку, в которой никто не вносит код, так что вам мои соболезнования. :-)

Что касается алгоритмов как таковых, я уже дал свои рекомендации: мы должны оставить MT19937 для RandomState и целей сравнения, а мне нравится PCG64 в целях рекомендаций.

Я немного напортачил с Compiler Explorer, и я думаю, что реализовал PCG64 для 64-битного MSVC с использованием встроенных функций таким образом, чтобы компилятор генерировал сборку, близкую к clang uint128_t math: https: // godbolt .org / z / ZnPd7Z

На данный момент у меня не настроена среда разработки для Windows, поэтому я не знаю, действительно ли она _правильна _... @bashtage, не попробовать ?

Без патча:

Uniforms per second
************************************************************
PCG64            62.77 million

С патчем:

Uniforms per second
************************************************************
PCG64           154.50 million

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

Для сравнения с исходным GCC и в режиме сравнения:

Time to produce 1,000,000 Uniforms
************************************************************
Linux-64, GCC 7.4                      PCG64            4.18 ms
Linux-64, GCC 7.4, Forced Emulation    PCG64            5.19 ms
Win64                                  PCG64            6.63 ms
Win32                                  PCG64           45.55 ms

вау, это кажется действительно плохим. Возможно, нам следует расширить информацию на странице сравнений, чтобы продемонстрировать производительность msvc2017-on-win {64, 32} и gcc7.4-on-linux {64, 32} на одном компьютере (я просто предполагаю, что вы используете msvc2017, вам, вероятно, следует где-то включить эту информацию).

Win32 здесь безнадежен. Я подозреваю, что 32-битный Linux также будет довольно ужасным, но у меня нет 32-битной системы Linux, которую можно было бы легко протестировать.

Я определенно вижу повод для рекомендации для людей, которые придерживаются 32-разрядной машины (скорее всего, Windows из-за корпоративной ИТ-политики). Этот recco ясен: DSFMT для 32 бит (или MT19937 тоже подойдет). Хотя тесты были бы хороши.

Как бы то ни было, я довольно скептически отношусь к часто повторяющимся утверждениям PCG о множественных независимых случайных потоках. Проводил ли кто-нибудь серьезный статистический анализ, подтверждающий заявление о независимости? (На самом деле, я думаю, что статья О'Нила относится только к «отдельным» потокам, не претендуя на независимость.)

Я думаю, есть веские основания для скептицизма: для данного множителя LCG все эти отдельные потоки просто связаны посредством масштабирования [*]. Таким образом, для любых двух потоков LCG с одним и тем же множителем один из них будет просто постоянным кратным (по модулю 2**64 или 2**32 в зависимости от случая) другого, хотя и с разными начальными точками. Перестановочная часть PCG поможет немного скрыть это, но на самом деле не было бы ничего удивительного, если бы были статистически обнаруживаемые корреляции.

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

[*] Пример: предположим, что x[0], x[1], x[2], ... - это стандартный 64-битный поток LCG с x[i+1] := (m*x[i] + a) % 2**64 . Установите y[i] := 3*x[i] % 2**64 для всех i . Тогда y[i] - это поток LCG с y[i+1] := (m*y[i] + 3*a) % 2**64 , поэтому, просто масштабируя исходный поток, вы создали один из этих отдельных потоков LCG с тем же множителем, но с другой аддитивной константой. Используя другие нечетные множители вместо 3 и предполагая, что нас интересуют только полнопериодные LCG (и поэтому a является нечетным), вы получите все возможные полные- период LCG с этим множителем.


РЕДАКТИРОВАТЬ: исправлено неправильное утверждение о количестве классов сопряженности.

Я думаю, что наиболее полный публичный анализ потоков PCG находится здесь: http://www.pcg-random.org/posts/critiquing-pcg-streams.html

@imneme Не могли бы вы развить свой последний совет? переписка с Дэвидом Блэкманом показывает, что может быть проще, чем я думал, создать «соседние» потоки с коррелированными инициализациями, такими как постоянное начальное число и потоки 1,2,3,4. Если вы собираетесь использовать несколько потоков в в то же время, пока я рекомендую, чтобы идентификатор потока и семя были разными и не имели очевидных корреляций друг с другом, что означает, что не делайте их обоих 1,2,3,4 ».

Означает ли это, что вы считаете нормальным иметь одно хорошее семя (например, полученное из источника энтропии), а затем идентификаторы 1,2,3,4 потока? Или следует выбирать как начальное значение, так и идентификатор потока случайным образом из хорошего источника энтропии?

Некоторые номера Linux-32 (Ubuntu 18.04 / GCC 7.4)

Time to produce 1,000,000 Uniforms
***************************************************************
Linux-64, GCC 7.4                      PCG64            4.18 ms
Linux-64, GCC 7.4, Forced Emulation    PCG64            5.19 ms
Win64                                  PCG64            6.63 ms
Win32                                  PCG64           45.55 ms
Linux-32, GCC 7.4                      PCG64           25.45 ms

Так что он в два раза быстрее Win-32, но медленнее. Все 4 тайминга были сделаны на одной машине.

Other Linux-32/GCC 7.4 Timing Results
-----------------------------------------------------------------
DSFMT            6.99 ms
MT19937         13.09 ms
Xoshiro256      17.28 ms
numpy           15.89 ms

NumPy - это NumPy 1.16.4. DSFMT - единственный генератор с хорошей производительностью на 32-бит (x86). Это должно быть четко задокументировано для любых 32-битных пользователей. MT19937 также является относительно хорошим выбором для 32-разрядного пользователя.

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

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

Мне было любопытно, насколько «P» часть PCG смягчает потенциальные проблемы, связанные с коррелированными потоками.

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

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

Вот график, который показывает, насколько помогает перестановка:

streams

Это диаграмма разброса 10000 поплавков от одного такого потока против 10000 от его отрицательного двойника. Не страшно, но и не здорово: есть явные артефакты.

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

Для записи вот источник:

import matplotlib.pyplot as plt
import numpy as np

from pcgrandom import PCG64

gen1, gen2 = PCG64(), PCG64()
multiplier, increment, state = gen1._get_core_state()
new_increment, new_state = -increment % 2**128, -state % 2**128
gen2._set_core_state((multiplier, new_increment, new_state))

xs = np.array([gen1.random() for _ in range(10**4)])
ys = np.array([gen2.random() for _ in range(10**4)])
plt.scatter(xs, ys, s=0.1)
plt.show()

PCG64 - это генератор, который О'Нил назвал PCG-XSL-RR (раздел 6.3.3 документа PCG). Пакет pcgrandom отсюда

Я думал, что стандартный способ получить независимые потоки - использовать jumpahead ().
Повторное заполнение для получения «независимых» потоков в целом опасно.

Генераторы счетчиков / хешей имеют тривиальную головку jumpahead (). Есть ли PCG?

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

Привет,
Филип

(РЕДАКТИРОВАТЬ по seberg: удалена цитата по электронной почте)

@pbstark : Это не просто повторное заполнение: два основных генератора LCG на самом деле различны: x ↦ mx + a (mod 2 ^ 128) и x ↦ mx + b (mod 2 ^ 128) для разных приращений a и b. Статья О'Нила о PCG продает идею возможности создавать различные потоки, изменяя этот шаг LCG (см. Раздел 4.3.2 документа).

Но простота LCG означает, что изменение этой аддитивной константы _does_ просто приводит к скачку на некоторую неизвестную величину в исходном генераторе в сочетании с простым линейным преобразованием (умножение на константу или просто добавление константы в некоторых случаях).

Это не причина не использовать PCG, и я ни на минуту не утверждаю, что он не подходит для нового основного PRNG NumPy; Я просто не хочу, чтобы людей привлекали обещания «независимых» случайных потоков. В лучшем случае идея настраиваемых потоков для PCG предлагает удобный способ сделать что-то, эквивалентное быстрому прыжку, плюс бонусное дополнительное мультипликативное или аддитивное преобразование.

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

На заметку о том, сколько включать: консенсус имел тенденцию к тому, что было бы неплохо иметь еще несколько битовых генераторов (конечно, была бы хороша небольшая документация). Бремя обслуживания не кажется слишком большим. В конце концов, я предполагаю, что мы пойдем с тем, что предложат Кевин и Роберт.

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

@mdickinson , я сам пытался воспроизвести ваш график и не смог. Я использовал каноническую версию кода

#include "pcg_random.hpp"
#include <iostream>
#include <random>

int main() {
    std::random_device rdev;
    pcg_detail::pcg128_t seed = 0;
    pcg_detail::pcg128_t stream = 0;
    for (int i = 0; i < 4; ++i) {
        seed   <<= 32;           
        seed   |= rdev();
        stream <<= 32;           
        stream |= rdev();
    }
    pcg64 rng1(seed,stream);
    pcg64 rng2(-seed,-stream);
    std::cerr << "RNG1: " << rng1 << "\n";
    std::cerr << "RNG2: " << rng2 << "\n";
    std::cout.precision(17);
    for (int i = 0; i < 10000; ++i) {
        std::cout << rng1()/18446744073709551616.0 << "\t";
        std::cout << rng2()/18446744073709551616.0 << "\n";
    }
}

Когда я запускаю это, он выводит (чтобы обеспечить воспроизводимость):

RNG1: 47026247687942121848144207491837523525 203756742601991611962280963671468648533 41579532896305845786243518008404876432
RNG2: 47026247687942121848144207491837523525 136525624318946851501093643760299562925 52472962479578397910044896975270170620

и точки данных, по которым можно построить следующий график:

corr1

Если вы поймете, что я делаю иначе, это будет полезно.

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

@mdickinson вводит вычисленное состояние и компонентам , минуя обычную процедуру инициализации , которая имеет два шага вперед.

Я написал небольшой сценарий драйвера, который чередует несколько потоков PCG32, созданных различными способами, для подачи в PractRand. Он использует master numpy на Python 3. Когда я напрямую ввожу состязательные внутренние состояния / приращения, PractRand быстро выходит из строя. Мне неясно, сможем ли мы найти разумные способы найти состязательные _seeds_ (которые фактически проходят через процедуру инициализатора) для достижения состязательных состояний.

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

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

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

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

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

Я не эксперт, но со стороны криптографии то, что предлагается, недоступно в:
https://cryptography.io/en/latest/ от службы криптографии Python ?

На их странице о генерации случайных чисел также упоминается:

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

Думаю, возможно добавление массивов в поколение. Я должен задаться вопросом, действительно ли потенциальное бремя обслуживания, связанное с криптографической устойчивостью, того стоит и уместно в NumPy, по сравнению, скажем, с взаимодействием с pyca и, возможно, с мыслью о стороннем генераторе / плагине для этого. Думаю, Натаниэль уже упоминал о подобном беспокойстве ранее.

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

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

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

Но, глядя на картину в целом, я думаю, имеет смысл взглянуть на проблемы, с которыми пользователи сталкиваются на практике. Какой совет мы им даем и т. Д. Большинство пользователей не осознают, что _для всех ГПСЧ (прошлых и будущих) _ 32-битное начальное значение является абсолютно ужасной идеей и приводит к тривиально обнаруживаемому смещению независимо от того, какой ГПСЧ используется. Конечно, мы можем отмахнуться от этого и вместо этого потратить свое время, беспокоясь о том, удастся ли кому-то инициализировать Mersenne Twister до состояния, состоящего в основном из нулей (или состояния из всех нулей, когда LFSR вообще не работают!), Или кто-то может инициализировать Xoshiro примерно до точки, в которой он повторяет один и тот же результат семь раз в пространстве из одиннадцати выходов, или создать два похожих потока PCG, или что-то еще, но все эти приспособления имеют в основном бесконечно малую (на практике нулевую) вероятность того, что произойдет, если генератор засевается случайными данными. Какими бы интеллектуально увлекательными и академически интересными ни были эти развлечения, думать о них, по большей части игнорируя тот факт, что пользователи обычно плохо понимают, что они делают, когда дело доходит до раздачи, - это возиться, пока горит Рим.

Если inc=1,2,3,4 - плохая идея, разве это не означает, что это должно быть либо очень четко задокументировано, либо, может быть, нам следует иметь немного другой API? Может быть, даже new_generator = (Bit)Generator().independent() , мы можем поставить на него предупреждение, если (базовый) битовый генератор не предоставляет отличного способа добиться этого.

Кроме того, в зависимости от того, насколько плохой 32-битный посев. Можем ли мы придумать хороший API для создания и хранения семени, чтобы заморозить его? Не знаю. Возможно даже «создать файл кэша замороженных семян, если он не существует».

Для PCG можно просто посеять -> uint64_t [2] -> splitmix64 (seed_by_array) -> uint128, что обеспечит распространение последовательных младших начальных чисел.

Для PCG можно просто посеять -> uint64_t [2] -> splitmix64 (seed_by_array) -> uint128, что обеспечит распространение последовательных младших начальных чисел.

Или просто используйте любой хороший целочисленный хеш. (Это должно быть взаимное предположение.) Есть много дешевых и коротких. Несколько раундов Multiply – XorShift - это нормально.

Что касается @mdickinson , я думаю, что он все еще хочет немного убедить, что зависимость от потока ограничена небольшим набором надуманных / враждебных настроек. И если это так, мы можем решить эту проблему с помощью надлежащих методов предотвращения таких случаев. В текущем коде, который у нас есть, есть некоторые плохие состояния, в которые пользователи могут легко впасть с текущими API. Я могу подтвердить вывод Дэвида Блэкмана о том, что установка seed=1 и inc=0,1,2,... создает корреляции. Мой последний драйвер PractRand для чередующихся потоков PCG32 может быть использован для демонстрации этого.

❯ ./pcg_streams.py --seed 1 --inc 0 |time ./RNG_test stdin32
[
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 12728272447693586011,
            "inc": 1
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 7009800821677620407,
            "inc": 3
        }
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0x470537d5
test set = normal, folding = standard (32 bit)

rng=RNG_stdin32, seed=0x470537d5
length= 128 megabytes (2^27 bytes), time= 4.0 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(2+0,13-3,T)                  R=  +9.6  p =  2.3e-4   mildly suspicious
  ...and 116 test result(s) without anomalies

rng=RNG_stdin32, seed=0x470537d5
length= 256 megabytes (2^28 bytes), time= 8.7 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(2+0,13-2,T)                  R= +26.1  p =  6.3e-13    FAIL           
  ...and 123 test result(s) without anomalies

./RNG_test stdin32  8.86s user 0.11s system 93% cpu 9.621 total

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

Я заметил, что мы не используем рекомендованное приращение по умолчанию, если оно не указано в конструкторе. Вероятно, нам следует это исправить. Возможно, это было бы хорошим базовым числом, из которого мы получаем фактическое приращение из данного идентификатора потока вместо 2*inc + 1 .

Мы можем попытаться создать некоторые инструменты, которые помогут людям использовать энтропийные семена по умолчанию и сохранить их. У меня есть один вопрос: можно ли сгенерировать приращения для нескольких потоков просто или нам также нужно выполнить их энтропийную выборку и сохранить их. Действительно удобно иметь возможность кодировать «начальное состояние» моделирования как единое число, которое можно скопировать из электронной почты коллеги, а не из непрозрачного файла. С этими меньшими ГПСЧ со 128 или 256 битами состояния я могу легко распечатать это в шестнадцатеричном формате в свой файл журнала, а затем просто скопировать и вставить в мою командную строку, когда я захочу воспроизвести его. Это больше, чем 32-битное целое число, но с ним можно справиться. Если мне также нужно выполнить энтропийную выборку всех идентификаторов потока, я должен отказаться от этого и убедиться, что я записываю все где-нибудь в файл состояния. Это может исключить некоторые варианты использования, которые мы обсуждали, когда мы хотим динамически создавать новые потоки. Если я могу просто увеличить счетчик, чтобы получить хороший идентификатор потока (возможно, получая его через хэш счетчика или что-то еще), тогда мне нужно записать только начальное семя, а не идентификаторы потока.

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

В среду, 29 мая 2019 г., в 15:19 Тайлер Редди [email protected]
написал:

Я не эксперт, но со стороны криптографии предлагается то, что
недоступно в:
https://cryptography.io/en/latest/ от службы криптографии Python
https://github.com/pyca ?

Их страница о генерации случайных чисел
https://cryptography.io/en/latest/random-numbers/ также упоминает:

Начиная с Python 3.6 стандартная библиотека включает секреты
https://docs.python.org/3/library/secrets.html модуль, который может быть
используется для генерации криптографически безопасных случайных чисел с определенными
помощники для текстовых форматов.

Думаю, возможно добавление массивов в поколение. Мне интересно, если
потенциальное бремя обслуживания, связанное с криптографическими
надежность действительно того стоит и уместна в NumPy vs. say
общается с pyca и, возможно, думает о стороннем
генератор / плагин для этого. Я думаю, что Натаниэль упомянул аналогичное беспокойство
ранее.

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

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWKXSSTX6QI7HJ65GYTPX36O3A5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFMVREXGODNVWWK3TUL52HS4DFMVREX
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWKGZMUB67VPCMFZYGTPX36O3ANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

@tylerjereddy Они предназначены для получения небольшого количества случайных битов из физического источника энтропии, которые непредсказуемы для злоумышленника (и для вас!). Они используются в криптографии для таких вещей, как векторы инициализации, одноразовые номера, ключи, которые все короткие. Все дело в том, что их невозможно воспроизвести, что противоречит целям численного моделирования np.random . На этой странице не говорится о _reproducible_ криптографически безопасных PRNG, которые также существуют и _ могут_ быть построены из примитивов, доступных в пакете cryptography . В _practice_, однако, у нас есть лучшие реализации этих алгоритмов, уже доступных нам в эффективном коде C, по крайней мере, те, которые были сформулированы и протестированы для целей моделирования. @bashtage реализовал несколько для этого фреймворка.

Я также хочу прояснить для команды numpy, что то, что предлагает @pbstark, - это не просто крипто-ГПСЧ. Скорее, он хочет один с _ неограниченным состоянием_, которое обеспечит математическое свойство, которое он ищет.

Большинство криптографических ГПСЧ, которые обычно рассматриваются для моделирования, _не_ имеют неограниченное состояние, которое хочет cryptorandom также ограничено уникальными начальными условиями 2**(256+64) из-за 256-битного состояния дайджеста фиксированного размера и 64-битного счетчика длины фиксированного размера. Это, вероятно, указывает путь к реализации действительно неограниченного ГПСЧ путем создания счетчика длины произвольного размера, но я никогда не видел, чтобы такой алгоритм был опубликован или протестирован.

С другой стороны, если вам просто нужен алгоритм PRNG, который имеет состояние произвольного размера, только тот, который фиксируется в начале на что-то большее, чем вам нужно, тогда расширенные генераторы PCG будут хорошо работать для этой задачи. Это явно не CS-PRNG, но они фактически удовлетворили бы желание

Есть и другие свойства стандартных ограниченных CS-PRNG, которые могут нам понадобиться, но они не являются очевидным значением по умолчанию, IMO.

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

В среду, 29 мая 2019 г., в 19:26 Роберт Керн [email protected]
написал:

@tylerjereddy https://github.com/tylerjereddy Они предназначены для получения
небольшое количество случайных битов из физического источника энтропии, которые
непредсказуемо для злоумышленника (и для вас!). Они используются в криптографии для
такие вещи, как векторы инициализации, одноразовые номера, ключи, которые все короткие. В
все дело в том, что их невозможно воспроизвести, а это
шансы на численное моделирование np.random. Эта страница
не говоря уже о воспроизводимых криптографически безопасных ГПСЧ, которые
также существуют вещи, которые могут быть построены из примитивов
доступно в пакете криптографии. Однако на практике мы имеем
улучшенные реализации этих алгоритмов, уже доступные нам в
эффективный код C, по крайней мере, тот, который был сформулирован и протестирован
в целях моделирования. @bashtage https://github.com/bashtage реализовано
несколько https://github.com/numpy/numpy/issues/13635#issuecomment-496287650
для этой структуры.

Я также хочу дать понять команде numpy, что @pbstark
https://github.com/pbstark предлагает не просто криптовалюту
ГПСЧ. Скорее, он хочет иметь неограниченное состояние , которое обеспечит
математическое свойство, которое он ищет.

Большинство криптографических ГПСЧ, которые обычно используются для моделирования
не имеют неограниченного состояния, в котором @pbstark
https://github.com/pbstark хочет. Обычно они основаны на
шифрование конечного счетчика. Как только этот счетчик вращается, вы попали в
конечный период. Технически его крипторандом
https://statlab.github.io/cryptorandom/ также ограничен до 2 ** (256 + 64)
уникальные начальные условия из-за состояния дайджеста фиксированного размера 256 бит и
счетчик длины 64 бита фиксированного размера. Это, вероятно, указывает путь к
реализация действительно неограниченного ГПСЧ путем создания счетчика длины
произвольного размера, но я никогда не видел, чтобы такой алгоритм был опубликован или
проверено.

С другой стороны, если вам просто нужен алгоритм ГПСЧ, имеющий
состояние произвольного размера, только одно, фиксированное в начале
что-то сверх того, что вам нужно, тогда расширенные генераторы PCG
http://www.pcg-random.org/party-tricks.html подойдет для этого
задача. Это явно не CS-PRNG, но они действительно удовлетворяют
@pbstark https://github.com/pbstark желание иметь огромные пространства состояний
по требованию. Тем не менее, я не рекомендую включать их в numpy.

Есть и другие свойства стандартных ограниченных CS-PRNG:
мы можем захотеть, но они не являются очевидным дефолтом, ИМО.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWLGAF6YVIWXYZ2LTT3PX43ONA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS443DFMVREX45
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWLIJD3UCVY3NXCLPKDPX43ONANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Боюсь, что онлайн-обновление SHA-256 работает не так. Состояние, которое он поддерживает, - это только 256-битный дайджест и 64-битный счетчик длины фиксированного размера, которые он добавляет при вычислении обновления. Он не содержит всего текста. Хеши сжимаем. Вот как он может эффективно обновлять каждый байт. По сути, существует множество инициализаций / прошлых историй, которые отображаются в одно и то же внутреннее состояние SHA-256, которое является конечным. Хотя циклы, безусловно, длинные, возможно, длиннее, чем 2**(256+64) , они, безусловно, существуют. И в любом случае у вас есть менее 2**(256+64) возможных начальных условий (для каждой длины текста от 0 до 2**64-1 вы можете иметь не более 2**256 внутренних хеш-состояний; после длина текста превышает 32 байта, должны быть коллизии а-ля ящик). В структуре данных просто больше нет битов.

Спасибо большое; понял. Я бы сказал иначе: состояние
пространство безгранично, но (по ячейкам) многие отдельные начальные состояния должны
производят неразличимые выходные последовательности.

В среду, 29 мая 2019 г., в 20:21 Роберт Керн [email protected]
написал:

Боюсь, что онлайн-обновление SHA-256 работает не так. Штат
что он поддерживает только 256-битный дайджест и 64-битный фиксированный размер
счетчик длины, который он добавляет при вычислении обновления. Это не держит
весь текст. Хеши сжимаем. Вот как он может эффективно
обновлять каждый байт. По сути, есть много инициализаций / прошлых
истории, которые отображаются в одно и то же внутреннее состояние SHA-256, которое является конечным.
Хотя циклы, безусловно, длинные, может быть, больше 2 (256 + 64), ониконечно существуют.
возможные начальные условия (для каждой длины текста от 0 до 2 64-1 вы можетеиметь не более 2 256 внутренних хэш-состояний; если длина текста превышает 32
байтов, должны быть коллизии а-ля ящик). Просто нет
больше бит в структуре данных.

-
Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWO6HRJZHTVBF2TLK3LPX5B3TA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFMVREPNVWWK3TUL52HS4DFMVREK3TUL52HPX3CHKYY3
или отключить поток
https://github.com/notifications/unsubscribe-auth/AANFDWM56HCQRZXDO3BAQHDPX5B3TANCNFSM4HPX3CHA
.

-
Филип Б. Старк | Заместитель декана по математическим и физическим наукам |
Профессор кафедры статистики |
Калифорнийский университет
Беркли, Калифорния 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Также случается, что есть только 2**(256+64) состояния, через которые он может пройти. Поскольку обновление каждый раз принимает одну и ту же форму, вы в конечном итоге попадаете в состояние, которое видели раньше, и входите в цикл с неизвестным (для меня), но конечным периодом. Будь то конечное число начальных состояний или конечный период, cryptorandom имеет и то и другое, и я думаю, что они меньше даже, чем MT19937 .

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

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

По-прежнему сильна на 512 ГБ:

❯ ./pcg_streams.py -i 0 |time ./RNG_test stdin32
[
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 10843219355420032665,
            "inc": 1
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 5124747729404067061,
            "inc": 3
        }
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0xb83f7253
test set = normal, folding = standard (32 bit)

rng=RNG_stdin32, seed=0xb83f7253
length= 128 megabytes (2^27 bytes), time= 4.0 seconds
  no anomalies in 117 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 256 megabytes (2^28 bytes), time= 8.6 seconds
  no anomalies in 124 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 512 megabytes (2^29 bytes), time= 16.9 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(2+2,13-2,T)                  R=  -8.0  p =1-2.1e-4   mildly suspicious
  ...and 131 test result(s) without anomalies

rng=RNG_stdin32, seed=0xb83f7253
length= 1 gigabyte (2^30 bytes), time= 33.8 seconds
  no anomalies in 141 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 2 gigabytes (2^31 bytes), time= 65.7 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(2+2,13-1,T)                  R=  -7.8  p =1-3.8e-4   unusual          
  ...and 147 test result(s) without anomalies

rng=RNG_stdin32, seed=0xb83f7253
length= 4 gigabytes (2^32 bytes), time= 136 seconds
  no anomalies in 156 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 8 gigabytes (2^33 bytes), time= 270 seconds
  no anomalies in 165 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 16 gigabytes (2^34 bytes), time= 516 seconds
  no anomalies in 172 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 32 gigabytes (2^35 bytes), time= 1000 seconds
  no anomalies in 180 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 64 gigabytes (2^36 bytes), time= 2036 seconds
  no anomalies in 189 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 128 gigabytes (2^37 bytes), time= 4064 seconds
  no anomalies in 196 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 256 gigabytes (2^38 bytes), time= 8561 seconds
  no anomalies in 204 test result(s)

rng=RNG_stdin32, seed=0xb83f7253
length= 512 gigabytes (2^39 bytes), time= 19249 seconds
  no anomalies in 213 test result(s)

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

❯ ./pcg_streams.py -n 3  | time ./build/RNG_test stdin32
[
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 18394490676042343370,
            "inc": 2891336453
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 12676019050026377766,
            "inc": 2891336455
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 6957547424010412162,
            "inc": 2891336457
        }
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0x4a9d21d1
test set = normal, folding = standard (32 bit)

rng=RNG_stdin32, seed=0x4a9d21d1
length= 128 megabytes (2^27 bytes), time= 3.2 seconds
  Test Name                         Raw       Processed     Evaluation
  DC6-9x1Bytes-1                    R= +19.4  p =  1.6e-11    FAIL           
  [Low8/32]DC6-9x1Bytes-1           R= +13.2  p =  4.6e-8    VERY SUSPICIOUS 
  ...and 115 test result(s) without anomalies

@imneme

Если вы поймете, что я делаю иначе, это будет полезно.

Я думаю, вам нужно заменить строку pcg64 rng2(-seed,-stream); в вашем коде на pcg64 rng2(-seed,-1-stream); , чтобы разрешить преобразование increment = 2 * stream + 1 . Отрицание приращения соответствует поразрядному отрицанию индекса потока. Если я внесу это изменение и запущу ваш код, я увижу нечто очень похожее на мой предыдущий сюжет. (И я подтверждаю, что если я не сделаю этого изменения, то все будет хорошо визуально.)

@imneme

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

Согласовано. Я думаю, что это дает очень чистую картину для LCG: для 64-битной LCG с фиксированным, хорошо выбранным множителем a у нас тогда есть пространство состояний размером 2^127 , состоящее из всех пар (x, c) целых чисел по модулю 2 ^ 64, где c - нечетное приращение. Функция обновления состояния - next : (x, c) ↦ (ax+c, c) , разделяющая пространство состояний на 2^63 непересекающихся циклов длиной 2^64 каждый. При заполнении просто выбирается начальная точка в этом пространстве состояний.

Тогда есть очевидное групповое действие, которое упрощает анализ и делает ясными отношения между различными потоками: группа обратимых одномерных аффинных преобразований на Z / 2^64Z имеет порядок точно 2^127 и действует транзитивно. (и точно так же) в пространстве состояний: аффинное преобразование y ↦ ey + f отображает пару (x, c) в (ex + f, ec + (1-a)f) . Это групповое действие коммутируется с функцией next , поэтому уникальный групповой элемент, преобразующий одну точку (x, c) в пространстве состояний в другую, (x2, c2) , также отображает последовательность, сгенерированную (x, c) к последовательности, сгенерированной (x2, c2) .

tl; dr: для фиксированного множителя любые две последовательности LCG с одинаковым множителем (с одинаковым приращением, как в случае опережающего просмотра или разными приращениями) связаны аффинным преобразованием. В неудачных случаях, которых мы хотим избежать, это аффинное преобразование представляет собой нечто ужасно простое, например, добавление 2 или умножение на -1 . В общем случае мы надеемся, что аффинное преобразование достаточно сложно, чтобы стандартные статистические тесты не могли обнаружить взаимосвязь между двумя потоками.

@mdickinson прекрасно освещает ситуацию. Перестановки PCG немного изменят ситуацию по сравнению с случаем LCG, но не сильно. Весь смысл перестановок PCG заключается в том, что мы можем выбирать, сколько скремблирования нужно делать. Поскольку усеченные 128-битные LCG уже проходят BigCrush, когда я выбрал перестановку для pcg64 я выбрал скромное количество скремблирования для LCG этого размера (XSL RR). Напротив, 64-битные LCG быстро не выдерживают нескольких статистических тестов, поэтому pcg32 использует немного больше скремблирования, но это все еще не самая сильная перестановка из статьи PCG. Как я уже упоминал в потоке запроса на вытягивание потока, я начал склоняться к более сильной перестановке PCG (RXS M) для варианта использования pcg32 . Это еще не значение по умолчанию, вы должны явно запросить эту версию, но есть большая вероятность, что я переключусь на значение по умолчанию, когда сделаю бамп основной версии для PCG. (RXS M - это верхняя половина RXS M XS, которую Vigna тщательно тестировала в этом размере, а также перестановку, которая нравится Дэвиду Блэкману).

Мы можем визуализировать разницу, когда обновленная версия программы тестирования ближних потоков использует обе схемы для pcg32 (XSH RR и RCS M [и необработанный базовый LCG тоже]):

#include "pcg_random.hpp"
#include <iostream>
#include <random>

// Create a "PCG" variant with a trivial output function, just truncation

template <typename xtype, typename itype>
struct truncate_only_mixin {
    static xtype output(itype internal)
    {
        constexpr size_t bits = sizeof(itype) * 8;
        return internal >> (bits/32);
    }
};

using lcg32 = pcg_detail::setseq_base<uint32_t, uint64_t, truncate_only_mixin>;

int main() {
    std::random_device rdev;
    uint64_t seed = 0;
    uint64_t stream = 0;
    for (int i = 0; i < 2; ++i) {
        seed   <<= 32;           
        seed   |= rdev();
        stream <<= 32;           
        stream |= rdev();
    }
    lcg32 rng1(seed,stream);
    lcg32 rng2(-seed,-1-stream);
    // pcg32 rng1(seed,stream);
    // pcg32 rng2(-seed,-1-stream);
    // pcg_engines::setseq_rxs_m_64_32 rng1(seed,stream);
    // pcg_engines::setseq_rxs_m_64_32 rng2(-seed,-1-stream);
    std::cerr << "RNG1: " << rng1 << "\n";
    std::cerr << "RNG2: " << rng2 << "\n";
    std::cout.precision(17);
    for (int i = 0; i < 10000; ++i) {
        std::cout << rng1()/4294967296.0 << "\t";
        std::cout << rng2()/4294967296.0 << "\n";
    }
}

Прежде чем мы начнем, давайте посмотрим на график, нарисованный @mdickinson, но только для LCG без перестановок, только с усечением:

corr-truncated-lcg

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

corr-truncated-lcg-good

Переходя к функциям вывода PCG, если мы используем XSH RR в патологическом случае, это будет выглядеть так - это большое улучшение по сравнению с графиком выше, но, очевидно, оно не полностью скрывает ужасность:

corr-pcg32-current

и это RXS M с той же базовой (плохо коррелированной) парой LCG:

corr-pcg32-future

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

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

В заключение, для NumPy, я думаю, имеет смысл просто учесть, что PCG64 хочет два 256 бит состояния (технически это 255, поскольку старший бит потока игнорируется) и называть это выполненным. Это также позволит избежать проблем, связанных с API, поскольку в одном BitGenerator у людей будет на одну функцию меньше, а в другом - нет.

(Но вы можете переключить 32-разрядный вариант PCG на вариант RXS M. Для источника C вам нужна последняя версия, поскольку я раньше не беспокоился о том, чтобы явно указывать RXS M в коде C, делая его доступным только в C ++ воплощение.)

[Извините, если это больше, чем вы когда-либо хотели знать! Ну, не то, что жаль. ;-)]

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

Я описал этот вариант использования ранее. Существуют веские причины UX для написания стохастической программы, которая принимает одно короткое «семя» ввода (то есть что-то примерно того размера, которого они могут скопировать и вставить из электронного письма в командную строку), что затем делает вывод программы детерминированным. @stevenjkern отметил мне в разговоре оффлайн, что такого рода взаимодействие необходимо для работы с регулирующими органами, которые должны были проверять его программное обеспечение. Если вам пришлось использовать файл _output_ запуска программы, чтобы воспроизвести результат, в таких обстоятельствах это выглядит немного подозрительно. Регулирующему органу придется глубоко погрузиться в код (который на самом деле может быть им недоступен), чтобы убедиться, что информация в файле действительно кошерная.

Теперь у нас есть хорошие инструменты в Python для динамического развертывания N параллельных процессов, чтобы выполнить часть работы, затем собрать результаты и перейти к основному процессу (затем развернуть M процессов и т. Д.). В отличие от более старых, менее гибких схем, таких как MPI, мы не просто запускаем фиксированные N процессов в начале. В этих случаях я мог видеть энтропийное заполнение каждого из N PRNG и сохранение их в файл, потому что в программе есть только одно место, которое это делает. По крайней мере, с точки зрения программирования это не так уж сложно. Теперь у нас есть гораздо более гибкие инструменты для параллелизма. Заполнение ГПСЧ теперь является узким местом, мешающим нам использовать эту гибкость в стохастических программах. Больше нет единой точки ответственности, где мы могли бы вести бухгалтерский учет на основе файлов.

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

Что вы думаете об использовании хорошего биективного хеша над 2**63 / 2**127 для получения приращений из счетной последовательности 0,1,2,3, ... при сохранении состояния? Вы предвидите проблемы с этим? Что вы думаете об объединении приращения хеширования с последующей большой перемычкой, чтобы переместить состояние в дальнюю часть нового цикла? Возможно, мы сможем переместить это дополнительное обсуждение по электронной почте или по другой проблеме и сообщить об этом.

@rkern , может быть что-то приятное в возможности давать очень короткие начальные числа для ГПСЧ, но (независимо от ГПСЧ) это ужасная идея. Если вы предоставляете _k_ битов начального ввода, а затем запрашиваете _k_ бит (либо сразу, либо сначала пропускаете _j_ бит [для некоторого произвольного _j_], а затем читаете _k_ бит), даже если все 2 ^ _k_ целые числа являются допустимыми входами, а не все 2 ^ _k_ выходы можно наблюдать (потому что функция от битов внутрь к битам не гарантированно будет [и действительно не может быть] биекцией). Ожидаемое распределение является биномиальным (2 ^ k, 2 ^ -k), которое мы можем аппроксимировать как распределение Пуассона, и, таким образом, значения 2 ^ k / e не будут наблюдаться. Это верно независимо от того, что такое ГПСЧ. Все, что мы можем сделать, это сделать так, чтобы _k_ был достаточно большим, чтобы было совершенно непрактично выяснять, чего не хватает.

Проблема усугубляется, когда все, скажем, используют один и тот же ГПСЧ (например, Mersenne Twister) и выбирают начальные числа из одного и того же небольшого набора (например, числа меньше 10000), потому что вместо произвольного конкретного предубеждения для каждой программы это предвзятость "для всех, кто делает это". Например, предположим, что вы выбираете четырехзначное начальное число, а затем берете разумное количество чисел из Mersenne Twister (скажем, меньше миллиона). В этой ситуации я могу заверить вас, что неудачное число 13 _ никогда не появится_ ни в одном из 10 миллиардов выходных данных (фактически, около 10% 32-битных целых чисел будут отсутствовать), а число 123580738 перепредставлено с коэффициентом 16. Это именно то, что мы ожидаем от случайной выборки из десяти миллиардов 32-битных целых чисел, но это настоящая проблема, если все будут использовать одну и ту же выборку. У нас была бы точно такая же проблема, если бы каждый выбрал девятизначные числа и набрал только 10000 чисел.

Тот факт, что многие люди хотят что-то делать, не делает это хорошей идеей. (Это не означает, что можно просто говорить людям, что они делают это неправильно или хотят не того. Вы должны выяснить, что им на самом деле нужно (например, воспроизводимые результаты из аргумента командной строки short-is - возможно, правильное решение - разрешить заполнение из UUID и небольшого целого числа; некоторые идеи о том, как красиво скремблировать эти вещи, чтобы получить исходные данные, можно найти в этом сообщении в перешло в

(Вот код, с которым можно поиграть, так как он довольно короткий ...)

// mtbias.cpp -- warning, uses 4GB of RAM, runs for a few minutes
// note: this is *not* showing a problem with the Mersenne Twister per se, it is
// showing a problem with simplistic seeding

#include <vector>
#include <iostream>
#include <random>
#include <cstdint>

int main() {
    std::vector<uint8_t> counts(size_t(std::mt19937::max()) + 1);
    for (size_t seed=0; seed < 10000; ++seed) {
        std::mt19937 rng(seed);
        for (uint i = 0; i < 1000000; ++i) {
            ++counts[rng()];
        }
    }
    size_t shown = 0;
    std::cout << "Never occurring: ";
    for (size_t i = 0; i <= std::mt19937::max(); ++i) {
        if (counts[i] == 0) {
            std::cout << i << ", ";
            if (++shown >= 20) {
                std::cout << "...";
                break;
            }
        }
    }
    std::cout << "\nMost overrepresented: ";
    size_t highrep_count = 0;
    size_t highrep_n = 0;
    for (size_t i = 0; i <= std::mt19937::max(); ++i) {
        if (counts[i] > highrep_count) {
            highrep_n = i;
            highrep_count = counts[i];
        }
    }
    std::cout << highrep_n << " -- repeated " << highrep_count << " times\n";
}

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

Предположим, что я могу заставить людей использовать хорошие 128-битные начальные числа для части состояния PCG64 , так или иначе. Есть ли у вас какие-либо комментарии по поводу получения потоков из одного и того же состояния? В целом я не собираюсь выводить больше чисел, чем мы бы из одного потока PCG64 . Я просто хочу иметь возможность рисовать эти числа в разных процессах без согласования каждого розыгрыша. Использование специального 63-битного хеш-кода multiply-xorshift пока работает довольно хорошо (сейчас у меня 32 ГиБ) для 8192 чередующихся потоков.

@imneme

Было бы полезно определить, что мы подразумеваем под словом «короткое».

Я думаю, что @rkern написал «что-то о размере, который они могут скопировать и вставить из электронной почты в командную строку». Я могу представить довольно большие числа несколькими символами, например 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.

Я просто хочу иметь возможность рисовать эти числа в разных процессах без согласования каждого розыгрыша. Использование специального 63-битного хеш-кода multiply-xorshift пока работает довольно хорошо (сейчас у меня 32 ГиБ) для 8192 чередующихся потоков.

Вы пробовали чередовать потоки n используя одно качественное начальное число, продвигая состояние на какое-то достаточно большое число (скажем, 2 ** 64, что и используется PCG64.jumped )? Кажется, это самый простой способ слабо координировать большие потоки n в кластере, используя что-то вроде PCG64(seed).jumped(node_id)

где node_id равно 0,1,2, ...

Есть ли ГПСЧ, который действительно хорош для создания простых независимых потоков с использованием чего-то вроде индекса? Я полагаю, что MLFG может, но мне это не понравилось, так как это был 63-битный генератор.

@bashtage , это действительно неправильный способ. Правильный способ - взять начальное число, если вы хотите добавить небольшое целое число, используйте хеш-функцию для его хеширования. Как упоминалось ранее, я ранее (независимо от PCG) написал серьезную функцию смешивания [edit: fix link к правильному сообщению] смешивать различные виды энтропии, большой и малой. Вам не обязательно использовать мой, но я бы рекомендовал вам сделать что-то в этом роде.

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

(к сожалению, я связался не с тем сообщением в блоге; я редактировал предыдущее сообщение, но, если вы читаете по электронной почте, я имел в виду ссылку на это сообщение в блоге)

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

Что касается раздачи, все генераторы MT используют авторские процедуры инициализации, PCG использует ваши, а остальные, так что что-то вроде

seed = np.array(required_size, dtype=np.uint64)
last = 0
for i in range(len(user_seed))
    if i < len(user_seed)
        last = seed[i] = splitmix64(last ^ user_seed[i])
    else:
        last = seed[i] = splitmix64(last)

Я полагаю, это можно улучшить.

Следует ли этого отговорить?

Я не видел jumped . Это ужасно для лежащей в основе LCG.

Предположим, у нас есть множитель M для 0x96704a6bb5d2c4fb3aa645df0540268d . Если мы вычислим M ^ (2 ^ 64), мы получим 0x6147671fb92252440000000000000001 что является ужасным множителем LCG. Таким образом, если бы вы взяли каждый 2 ^ 64-й элемент из 128-битного LCG, это было бы ужасно (младшие биты - это просто счетчик). Стандартные функции перестановки PCG предназначены для шифрования обычного вывода LCG, а не для шифрования счетчиков.

PCG64 в настоящее время тестируется с использованием Practrand до половины петабайта, и дальнейший анализ показывает, что вы можете читать многие петабайты без проблем, связанных с степенью двойки. Увы, если вы перескочите вперед, чтобы пропустить вперед огромные точные степени двойки, обычные (несколько скромные) перестановки PCG не смогут в достаточной степени компенсировать патологическую последовательность от пропуска лежащих в основе LCG огромных расстояний, подобных этой. Вы можете поднять ставку силы перестановок, чтобы исправить это, и на самом деле я и Vigna независимо сравнили стандартные перестановки PCG со стандартными целочисленными хэш-функциями, которые, вероятно, так и поступили бы (в конце концов, они являются основой SplitMix, которая _is_ просто фишка). Когда я изучал это в 2014 году с Fast Hash, скорость казалась не такой уж большой, но когда Vigna сделал это совсем недавно с murmurhash, он заявил, что производительность превосходит стандартную PCG (!).

Если вы действительно хотите иметь скачок вперед 2 ^ 64, я думаю, вам нужно переключиться на более сильную перестановку функций вывода (что, как мы видели, можно сделать с небольшими затратами). Но если вы чувствуете, что это больше не делает его действительно «стандартным» PCG, и хотите сохранить обычную перестановку вывода, тогда, вероятно, нужно отказаться от jumped() .

(Кстати, патологический скачок вперед применим и к другим ГПСЧ. Известно, что SplitMix имеет некоторые плохие приращения, и разумно предположить, что, хотя обычное приращение (также известное как «гамма») 0xbd24b73a95fb84d9, продвигается на 2 ^ 32 даст вам приращение 0x95fb84d900000000, что не так хорошо. Для LFSR плохой скачок вперед, вероятно, не является степенью двойки, но я почти уверен, что будут скачки, когда базовая матрица заканчивается патологически. разреженный.)

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

❯ ./pcg_streams.py --jumped -n 4 | time ./RNG_test stdin32
[
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 10149010587776656704,
            "inc": 2891336453
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 1158608670957446464,
            "inc": 2891336453
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 10614950827847787840,
            "inc": 2891336453
        }
    },
    {
        "bit_generator": "PCG32",
        "state": {
            "state": 1624548911028577600,
            "inc": 2891336453
        }
    }
]
RNG_test using PractRand version 0.93
RNG = RNG_stdin32, seed = 0xeedd49a8
test set = normal, folding = standard (32 bit)

rng=RNG_stdin32, seed=0xeedd49a8
length= 128 megabytes (2^27 bytes), time= 2.1 seconds
  Test Name                         Raw       Processed     Evaluation
  BCFN(2+0,13-3,T)                  R= +58.7  p =  1.3e-27    FAIL !!!       
  BCFN(2+1,13-3,T)                  R= +48.0  p =  1.5e-22    FAIL !!        
  BCFN(2+2,13-3,T)                  R= +16.0  p =  2.3e-7   very suspicious  
  DC6-9x1Bytes-1                    R= +53.5  p =  1.8e-32    FAIL !!!       
  [Low8/32]DC6-9x1Bytes-1           R= +27.4  p =  1.1e-17    FAIL !         
  ...and 112 test result(s) without anomalies

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

Что ж, вот что мы здесь пытаемся решить. :-) Мы были достаточно довольны тем, что просто раскрыли все функции, которые предоставляет каждый ГПСЧ, при условии, что свойства, предоставляемые каждым алгоритмом, хорошо изучены. Мы также разумно довольны утверждением: «Вот ГПСЧ по умолчанию, который мы рекомендуем; у него есть множество полезных функций; у других их может не быть».

Идея использования хэша для получения нового состояния из данного состояния и идентификатора потока для любого алгоритма представляет интерес. Вы знаете, насколько хорошо это изучено? Это похоже на исследовательскую задачу, чтобы убедиться, что он хорошо работает для всех алгоритмов. Я бы не стал утверждать, что «вот общая процедура получения независимых потоков для всех наших PRNG». Меня больше устраивает «вот общий API для получения независимых потоков; каждый ГПСЧ реализует его любым способом, подходящим для алгоритма, и может не реализовать его, если алгоритм его не поддерживает».

С другой стороны, если дело просто в выделении достаточного количества циклов ЦП для тестирования чередующихся потоков для каждого BitGenerator выходе N GiB на PractRand, это не слишком обременительно.

Идея использования хэша для получения нового состояния из данного состояния и идентификатора потока для любого алгоритма представляет интерес. Вы знаете, насколько хорошо это изучено? Это похоже на исследовательскую задачу, чтобы убедиться, что он хорошо работает для всех алгоритмов. Я бы не стал утверждать, что «вот общая процедура получения независимых потоков для всех наших PRNG». Меня больше устраивает «вот общий API для получения независимых потоков; каждый ГПСЧ реализует его любым способом, подходящим для алгоритма, и может не реализовать его, если алгоритм его не поддерживает».

Я не знаю, можно ли это назвать «исследовательской проблемой» (и, следовательно, «хорошо изученной»), но C ++ 11 (который в основном касался использования проверенных и надежных давно установленных методов) предоставляет концепцию _SeedSequence_ (и конкретная реализация std::seed_seq ), задача которой - предоставить данные заполнения для полностью произвольных PRNG.

В общем, почти все ГПСЧ ожидают инициализации / заполнения случайными битами. Нет ничего особенно волшебного в случайных битах, исходящих (скажем) из random.org и случайных битах, исходящих из чего-то более алгоритмического (CS PRNG, хеш-функция и т. Д.).

Довольно просто представить себе набор ГПСЧ из одной и той же схемы, все засеянные своими случайными битами. Вы можете думать о том, что мы делаем, как о точках выбора (или на самом деле интервалы до определенной максимальной длины, соответствующей количеству случайных чисел, которые мы ожидаем правдоподобно запросить, например, 2 ^ 56) в строке (например, строка с 2 ^ 255 точками). Мы можем вычислить вероятность того, что, если мы попросим _n_ интервалов, один будет перекрываться с другим. Это довольно простая вероятность - я не уверен, что вы могли бы опубликовать статью об этом, потому что (насколько я понимаю) никого никогда не волнуют статьи, содержащие элементарную математику. ( @lemire может не согласиться!)

[Я бы сказал, что вы, как правило, не должны _не_ делать, это заполнять ГПСЧ случайными битами, выходящими из самого себя. Мне это кажется слишком кровосмесительным.]

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

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

По сути, если мы думаем о PRNG как о функциях перехода и функциях вывода, эта new_state = seed_seq(old_state||streamID) - это просто еще одна функция перехода, которую мы отбрасываем на один шаг. Мы должны быть уверены, что операции, задействованные в этом seed_seq , достаточно отличаются от функций перехода в каждом алгоритме ГПСЧ (или их обратных) и, возможно, должны быть уверены в других вещах. Я бы не хотел использовать что-то построенное, скажем, из wyhash для инициализации wyrand . Как вы говорите, вы не хотите использовать сам ГПСЧ для предоставления битов самому себе. Вот почему я думаю, что необходимо некоторое исследование, чтобы убедиться в этом для всех наших ГПСЧ (исследование, которое я надеялся не проводить сам).

С другой стороны, new_state = seed_seq(old_state||streamID) в этом отношении, вероятно, не хуже, чем предполагаемое использование _SeedSequence_ для нескольких потоков: вытаскивать два состояния последовательно. Если это так, то я мог бы положиться на опыт C ++, возможно, на вашу реализацию, и просто провести несколько эмпирических тестов с PractRand для всех наших алгоритмов, чтобы показать, что они не хуже, чем их однопоточные аналоги.

Было бы действительно неплохо заставить работать hash-jump, потому что это открывает некоторые варианты использования для создания PRNG без координации. Использование идентификаторов потоков требует некоторой связи или предварительного распределения. dask просил нечто подобное в прошлом.

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

Все это похоже на то, что использование 63-битного хеша для получения приращения PCG32 из последовательных идентификаторов потока ( range(N) ) похоже работает. 8192 перемежаемых потока передают PractRand на 2 ТиБ. Если мы действительно предоставляем идентификаторы потоков для генераторов PCG, мы можем использовать этот метод для получения приращений, даже если мы предлагаем людям использовать другие средства для воспроизводимого получения независимых потоков.

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

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

Но, FWIW, SplitMix делает это, это то, что делает операция split() . И мне не нравится, что он это делает.

Это может быть слишком много информации, но я расскажу немного о том, почему я был напуган (возможно, даже больше, чем следовало бы) функцией SplitMix split() . Исторически следует отметить, что SplitMix и PCG были разработаны независимо примерно в одно и то же время (SplitMix был опубликован 20 октября 2014 г., тогда как pcg-random.org был запущен в августе 2014 г. и связан с документом PCG 5 сентября 2014 г. ). Есть некоторые параллели между PCG и SplitMix (и различными другими PRNG, включая xor shift * и xorshift + Vigna - также выпущенные в 2014 году). Все они имеют довольно простые не очень хорошие функции перехода между состояниями, фиксированные функцией вывода скремблирования. Когда я писал статью о PCG, я знал, что кое-кому понравится функция split() но я не мог придумать, как это сделать; вместо этого я разработал быстрое доказательство того, что если у вас был _k_-битный ГПСЧ, где вы могли двигаться влево или вправо на каждом шаге, в пределах _k_ шагов вы должны быть в состоянии достичь состояния, в котором были раньше, тем самым доказав всю концепцию было непродуманным. Это наблюдение не попало в газету. Но в результате моих размышлений перед этим доказательством, в сноске к почти окончательному черновику статьи, я предположил, несколько причудливо, потому что вывод PCG был хешем / скремблированием / перестановкой своего состояния, если бы вы чувствуя себя непослушным, вы можете повторно установить генератор с его собственным выходом и уйти от ответственности. Я убрал его из окончательной версии, потому что подумал, что такая прихоть будет слишком большим красным флагом для рецензентов, учитывая, что повторная установка ГПСЧ с его собственным состоянием широко рассматривалась как неправильное использование ГПСЧ, того, что мы видим от людей, неопытных в их использовании.

Прочитав статью о SplitMix, мне понравилось, но я был очень удивлен, когда увидел split() . В нем было то, что я в основном считал шуткой, и сделал это особенностью палатки. Лишь несколько лет спустя я начал писать более подробно о том, что происходит, когда у вас есть такая операция.

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

Ради забавы, вот дамп пространства состояний крошечной версии SplitMix, исследующий всего три различных (и жестко фиксированных) способа объединения next() и split() :

Testing: SplitMix16: void advance() { rng = rng.split();}

Finding cycles...
- state 00000000 -> new cycle 1, size 4, at 000043b0 after 516 steps
- state 00000050 -> new cycle 2, size 41, at 00002103 after 2 steps
- state 000000cd -> new cycle 3, size 4, at 0000681a after 6 steps
- state 00000141 -> new cycle 4, size 23, at 00004001 after 11 steps
- state 00000dee -> new cycle 5, size 7, at 00007436 after 4 steps
- state 00008000 -> new cycle 6, size 90278, at 5e5ce38c after 46472 steps
- state 00030000 -> new cycle 7, size 6572, at 12c65374 after 10187 steps
- state 00030016 -> new cycle 8, size 3286, at 65d0fc0c after 402 steps
- state 00058000 -> new cycle 9, size 17097, at 2a2951fb after 31983 steps
- state 08040000 -> new cycle 10, size 36, at 08040000 after 0 steps
- state 08040001 -> new cycle 11, size 218, at 08040740 after 360 steps
- state 08040004 -> new cycle 12, size 10, at 38c01b3d after 107 steps
- state 08040006 -> new cycle 13, size 62, at 38c013a0 after 39 steps
- state 08040009 -> new cycle 14, size 124, at 08045259 after 24 steps
- state 08040019 -> new cycle 15, size 32, at 38c06c63 after 151 steps
- state 08040059 -> new cycle 16, size 34, at 38c00217 after 17 steps
- state 08040243 -> new cycle 17, size 16, at 38c06e36 after 13 steps
- state 123c8000 -> new cycle 18, size 684, at 77d9595f after 194 steps
- state 123c8002 -> new cycle 19, size 336, at 5de8164d after 141 steps
- state 123c9535 -> new cycle 20, size 12, at 123c9535 after 0 steps
- state 139f0000 -> new cycle 21, size 545, at 743e3a31 after 474 steps
- state 139f0b35 -> new cycle 22, size 5, at 139f0b35 after 0 steps
- state 139f1b35 -> new cycle 23, size 5, at 68d3c943 after 8 steps

Cycle Summary:
- Cycle 1, Period 4, Feeders 32095
- Cycle 2, Period 41, Feeders 188
- Cycle 3, Period 4, Feeders 214
- Cycle 4, Period 23, Feeders 180
- Cycle 5, Period 7, Feeders 12
- Cycle 6, Period 90278, Feeders 1479024474
- Cycle 7, Period 6572, Feeders 102385385
- Cycle 8, Period 3286, Feeders 5280405
- Cycle 9, Period 17097, Feeders 560217399
- Cycle 10, Period 36, Feeders 413
- Cycle 11, Period 218, Feeders 51390
- Cycle 12, Period 10, Feeders 1080
- Cycle 13, Period 62, Feeders 4113
- Cycle 14, Period 124, Feeders 4809
- Cycle 15, Period 32, Feeders 2567
- Cycle 16, Period 34, Feeders 545
- Cycle 17, Period 16, Feeders 87
- Cycle 18, Period 684, Feeders 95306
- Cycle 19, Period 336, Feeders 100263
- Cycle 20, Period 12, Feeders 7
- Cycle 21, Period 545, Feeders 163239
- Cycle 22, Period 5, Feeders 12
- Cycle 23, Period 5, Feeders 34

- Histogram of indegrees of all 2147483648 nodes:
      0  529334272
      1 1089077248
      2  528875520
      3     131072
      4      65536
Testing: SplitMix16: void advance() { rng.next(); rng = rng.split();}

Finding cycles...
- state 00000000 -> new cycle 1, size 36174, at 6b34fe8b after 21045 steps
- state 00000002 -> new cycle 2, size 4300, at 042a7c6b after 51287 steps
- state 0000000f -> new cycle 3, size 11050, at 0b471eb5 after 4832 steps
- state 0000001d -> new cycle 4, size 38804, at 2879c05c after 16280 steps
- state 00000020 -> new cycle 5, size 4606, at 46e0bdf6 after 7379 steps
- state 00046307 -> new cycle 6, size 137, at 0a180f87 after 89 steps
- state 00081c25 -> new cycle 7, size 16, at 177ed4d8 after 27 steps
- state 0044c604 -> new cycle 8, size 140, at 5e1f125b after 44 steps
- state 006e329f -> new cycle 9, size 18, at 006e329f after 0 steps
- state 13ebcefc -> new cycle 10, size 10, at 13ebcefc after 0 steps

Cycle Summary:
- Cycle 1, Period 36174, Feeders 975695553
- Cycle 2, Period 4300, Feeders 766130785
- Cycle 3, Period 11050, Feeders 110698235
- Cycle 4, Period 38804, Feeders 251133911
- Cycle 5, Period 4606, Feeders 43723200
- Cycle 6, Period 137, Feeders 4101
- Cycle 7, Period 16, Feeders 172
- Cycle 8, Period 140, Feeders 2310
- Cycle 9, Period 18, Feeders 124
- Cycle 10, Period 10, Feeders 2

- Histogram of indegrees of all 2147483648 nodes:
      0  529334272
      1 1089077248
      2  528875520
      3     131072
      4      65536
Testing: SplitMix16: void advance() { rng.next(); rng = rng.split(); rng = rng.split();}

Finding cycles...
- state 00000000 -> new cycle 1, size 40959, at 0069b555 after 49520 steps
- state 00000031 -> new cycle 2, size 1436, at 5f619520 after 2229 steps
- state 000003a4 -> new cycle 3, size 878, at 18d1cb99 after 1620 steps
- state 0000046c -> new cycle 4, size 2596, at 46ba79c0 after 1591 steps
- state 0000c6e2 -> new cycle 5, size 24, at 0212f11b after 179 steps
- state 000af7c9 -> new cycle 6, size 61, at 40684560 after 14 steps
- state 00154c16 -> new cycle 7, size 110, at 29e067ce after 12 steps
- state 0986e055 -> new cycle 8, size 4, at 2b701c82 after 7 steps
- state 09e73c93 -> new cycle 9, size 3, at 352aab83 after 1 steps
- state 19dda2c0 -> new cycle 10, size 1, at 78825f1b after 2 steps

Cycle Summary:
- Cycle 1, Period 40959, Feeders 2129209855
- Cycle 2, Period 1436, Feeders 5125630
- Cycle 3, Period 878, Feeders 7077139
- Cycle 4, Period 2596, Feeders 5997555
- Cycle 5, Period 24, Feeders 24221
- Cycle 6, Period 61, Feeders 1774
- Cycle 7, Period 110, Feeders 1372
- Cycle 8, Period 4, Feeders 23
- Cycle 9, Period 3, Feeders 4
- Cycle 10, Period 1, Feeders 3

- Histogram of indegrees of all 2147483648 nodes:
      0  829903716
      1  684575196
      2  468475086
      3  132259769
      4   32192209
      5      58402
      6      17026
      7       1982
      8        261
      9          1
Testing: SplitMix16: void advance() { rng.next(); rng.next(); rng = rng.split();}

Finding cycles...
- state 00000000 -> new cycle 1, size 55038, at 3e57af06 after 30005 steps
- state 00000005 -> new cycle 2, size 376, at 4979e8b5 after 6135 steps
- state 0000001e -> new cycle 3, size 10261, at 0cd55c94 after 1837 steps
- state 0000002d -> new cycle 4, size 3778, at 7f5f6afe after 3781 steps
- state 00000064 -> new cycle 5, size 2596, at 3bc5404b after 5124 steps
- state 0000012b -> new cycle 6, size 4210, at 525cc9f3 after 397 steps
- state 00000277 -> new cycle 7, size 1580, at 410010c8 after 1113 steps
- state 00001394 -> new cycle 8, size 916, at 7b20dfb0 after 193 steps
- state 00063c2d -> new cycle 9, size 51, at 6e92350b after 121 steps
- state 058426a6 -> new cycle 10, size 8, at 058426a6 after 0 steps
- state 0e5d412d -> new cycle 11, size 1, at 0e5d412d after 0 steps
- state 4c2556c2 -> new cycle 12, size 1, at 4c2556c2 after 0 steps

Cycle Summary:
- Cycle 1, Period 55038, Feeders 2027042770
- Cycle 2, Period 376, Feeders 28715945
- Cycle 3, Period 10261, Feeders 49621538
- Cycle 4, Period 3778, Feeders 13709744
- Cycle 5, Period 2596, Feeders 15367156
- Cycle 6, Period 4210, Feeders 10418779
- Cycle 7, Period 1580, Feeders 1782252
- Cycle 8, Period 916, Feeders 744273
- Cycle 9, Period 51, Feeders 2351
- Cycle 10, Period 8, Feeders 24
- Cycle 11, Period 1, Feeders 0
- Cycle 12, Period 1, Feeders 0

- Histogram of indegrees of all 2147483648 nodes:
      0  529334272
      1 1089077248
      2  528875520
      3     131072
      4      65536

и т.п.

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

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

Из любопытства, есть ли какой-нибудь (простой) способ определить, насколько далеко друг от друга находятся два состояния в PCG64?

Да, хотя мы его не раскрываем: http://www.pcg-random.org/useful-features.html#distance

Из любопытства, есть ли какой-нибудь (простой) способ определить, насколько далеко друг от друга находятся два состояния в PCG64?

Да, хотя мы его не раскрываем: http://www.pcg-random.org/useful-features.html#distance

В исходном коде C ++ функция расстояния даже сообщит вам расстояние между потоками, указав их точку наибольшего сближения (где единственная разница между потоками - добавленная константа).

Между прочим, для базового LCG мы можем использовать расстояние, чтобы определить, насколько коррелированными, по нашему мнению, будут позиции to. Короткое расстояние, очевидно, плохо (и плохо для любого ГПСЧ вообще), но расстояние с одним установленным битом тоже не очень хорошее, поэтому переход вперед на 2 ^ 64 ( 0x10000000000000000 ) с .jumped - плохая идея. В моем списке дел на PCG я пишу функцию « independence_score », которая смотрит на расстояние между двумя состояниями и сообщает вам, насколько оно выглядит случайным (с помощью веса Хэмминга и т. Д.) - в идеале мы хотим примерно половину биты должны быть нулями и половинными единицами, и они должны быть обильно разбросаны).

Один из способов _keep_ jumped с PCG64 - это не переходить на n * 0x10000000000000000 а вместо этого переходить на n * 0x9e3779b97f4a7c150000000000000000 (усеченный до 128 бит). Это даст вам все обычные свойства, которые вы хотите ( .jumped(3).jumped(5) == .jumped(8) ), без патологии для лежащей в основе LCG.

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

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

Рекомендую пропустить через 0x9e3779b97f4a7c15f39cc0605cedc835 (дробная часть ϕ), но 0xb7e151628aed2a6abf7158809cf4f3c7 (дробная часть e) тоже подойдет, или _любое_ случайное число.)

Я рекомендую wyhash (https://github.com/wangyi-fudan/wyhash), так как это самый быстрый и простой способ, прошедший BigCrush и PractRand. Код на c так же прост, как

inline  uint64_t    wyrand(uint64_t *seed){    
    *seed+=0xa0761d6478bd642full;    
    __uint128_t t=(__uint128_t)(*seed^0xe7037ed1a0b428dbull)*(*seed);    
    return  (t>>64)^t;    
}

@ wangyi-fudan, я не могу убедить себя, что это биекция.

Извините за мои ограниченные знания: почему биекция необходима / предпочтительна для ГПСЧ?
буду признателен за некоторые объяснения :-) @imneme

@ wangyi-fudan, если хеш-функция из 64-битных целых чисел в 64-битные целые числа не является взаимно однозначной (то есть функцией 1 к 1), то некоторые результаты генерируются более одного раза, а некоторые - нет. Это своего рода предвзятость.

я понимаю, что ты имеешь в виду. однако для 64-битного генератора случайных чисел R мы ожидаем одно столкновение после 1,2 * 2 ^ 32 случайных чисел (http://mathworld.wolfram.com/BirthdayAttack.html). с 2 ^ 64 случайными числами естественно иметь много столкновений. столкновения естественны, в то время как биекция не является естественно случайной. Если я знаю, что игровой стол (например, 3-битный ГПСЧ) имеет значение 0 в пределах 8 трасс, я осмелюсь сделать большую ставку на ноль после того, как заметил 5 ненулевых.

@ wangyi-fudan, в этом контексте мы говорили о способах перестановки идентификатора потока, чтобы потоки, подобные 1,2,3, выглядели более случайными (или более нормальными). В столкновениях в этом процессе нет ничего хорошего.

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

Мы можем вычислить вероятность того, что, если мы попросим _n_ интервалов, один будет перекрываться с другим. Это довольно простая вероятность - я не уверен, что вы могли бы опубликовать статью об этом, потому что (насколько я понимаю) никого никогда не волнуют статьи, содержащие элементарную математику.

Я думаю, тебе просто нужно быть Пьером Л'Экуайером. ;-) страница 15

Да, когда он объясняет основы, это считается нормальным!

@rkern @imneme Простота - это особенность как в программном обеспечении, так и в математике. То, что некоторых не впечатляет простая работа, не следует воспринимать как противоречащее доказательство.

@lemire : Мне нравится юмористический отрывок, в котором, как мне кажется, много правды, под названием « Как критиковать компьютерных ученых» . Основная идея, лежащая в основе произведения, состоит в том, что теоретики отдают предпочтение изысканности, а экспериментаторы - простоте. Так что, если ваша аудитория - один из экспериментаторов, она будет в восторге от простоты, но если ваша аудитория - один из теоретиков, то не так.

По умолчанию BitGenerator - PCG64 . Спасибо всем за ваш внимательный вклад. И выносливость!

Очень вдохновленный этой веткой, у меня есть кое-какие новости…

Задний план

По многим параметрам pcg64 довольно хорош; например, согласно обычным критериям статистического качества, он получает чистый счет здоровья. Это было проверено различными способами; совсем недавно я прогнал его до полпетабайта с PractRand. Он хорошо работает в обычных случаях использования.

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

Итак, около 25 дней назад я начал думать о создании нового члена семейства PCG ...

Цель

Моя цель состояла в том, чтобы разработать нового члена семейства PCG, который мог бы стать заменой нынешнему варианту pcg64 . Как таковой:

  • Функция вывода должна шифровать биты больше, чем XSL RR (потому что это позволит избежать проблем, которые возникли в этом потоке).
  • Производительность должна быть примерно такой же (или более высокой), чем у текущего pcg64 .
  • Дизайн должен быть в духе PCG (то есть не быть тривиально предсказуемым и, следовательно, не позволять легко отменять _ любую_ работу функции вывода).

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

Спойлер

Рад сообщить об успехе! Примерно 25 дней назад, когда я впервые подумал об этом, я на самом деле был в отпуске. Когда я вернулся около десяти дней назад, я попробовал свои идеи и был рад обнаружить, что они работают хорошо. Последующее время в основном было потрачено на различные виды тестирования. Вчера был достаточно доволен, что вставил код в C ++ версию PCG. Тесты на небольших размерах показывают, что он намного лучше, чем XSL RR, и конкурирует с RXS M, но на самом деле он лучше всего подходит для больших размеров. Он также отвечает всем остальным целям.

Детали

FWIW, новая функция вывода (для случая 64-битного вывода):

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

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

Эта функция вывода основана на широко используемом xorshift-multiply. Выбор множителей: (а) уменьшить количество магических констант и (б) предотвратить отмену перестановки (если у вас нет доступа к младшим битам), а также предоставить все «рандомизированные- само по себе »качество, которое обычно имеют функции вывода PCG.

Прочие изменения

Также случается, что 0xda942042e4dd58b5 является множителем LCG для этого PRNG (и всех генераторов PCG с префиксом cm_ префиксом 0x2360ed051fc65da44385df649fccf645 используемым pcg64 , эта константа на самом деле все еще довольно хороша с точки зрения свойств спектрального теста, но ее дешевле умножать, потому что 128-битное × 64-битное проще, чем 128 бит × 128 бит. Я без проблем использовал эту константу LCG в течение нескольких лет. При использовании варианта с дешевым умножителем я запускаю функцию вывода в состоянии до итерации, а не в состоянии после итерации, для большего параллелизма на уровне инструкций.

Тестирование

Я его тщательно протестировал (PractRand и TestU01), и я им доволен. Тесты включали сценарии, описанные в этом потоке (например, использование группы генераторов на последовательных парах или увеличение на 2 ^ 64 и чередование их выходных данных - я также без проблем протестировал группу из четырех и группу из 8192 до 8 ТБ. как ручей и его противоположный аналог).

Скорость

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

Доступность

Пользователи заголовка C ++ могут получить доступ к этому новому члену семейства _now_ как pcg_engines::cm_setseq_dxsm_128_64 ; в какой-то момент в будущем я переключу pcg64 с pcg_engines::setseq_xsl_rr_128_64 на эту новую схему. В настоящее время я планирую сделать это этим летом в рамках повышения версии PCG 2.0.

Официальные объявления

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

Ваш выбор ...

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

@imneme Обходит ли необходимость в быстром полном 64-битном умножителе? (Это очень быстро на x64, но немного медленнее на некоторых более слабых архитектурах.)

@lemire : 128-битное × 64-битное умножение, которое внутренне будет выполнено с помощью двух инструкций умножения на x86 (64-битный × 64-битный → 128-битный результат для младших битов и 64-битный × 64 -bit на старших битах. Оба умножения могут выполняться параллельно, и затем необходимо сложить два результата.)

Это все еще потенциально лучше, чем 128 × 128 бит. Хотя насколько лучше зависит от того, насколько хорошо идет планирование инструкций в данный момент.

Вы правы, что на ARM результат 64 × 64 → 128 бит - это фактически две инструкции.

(Конечно, вполне возможно объединить два 64-битных LCG и смешать их. Пространство для всех PRNG, которые могут существовать и хорошо работать, довольно велико.)

Быстрая и грубая реализация в нашем фреймворке предполагает небольшое улучшение производительности, по крайней мере, в 64-битном Linux:

Time to produce 1,000,000 64-bit unsigned integers
************************************************************
MT19937      5.42 ms
PCG64        2.59 ms
PCG64DXSM    2.41 ms
Philox       4.37 ms
SFC64        2.07 ms
numpy        5.41 ms
dtype: object

64-bit unsigned integers per second
************************************************************
MT19937      184.39 million
PCG64        386.01 million
PCG64DXSM    415.02 million
Philox       228.88 million
SFC64        483.94 million
numpy        184.79 million
dtype: object

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

Я предполагаю, что настоящий вопрос для вас, @imneme , - насколько вы будете раздражены именем numpy.random.PCG64 реализующим алгоритм 1.0? Релиз неизбежен и уже отложен, поэтому я не думаю, что мы собираемся менять алгоритм сейчас. Если производительность на 32-битных платформах особенно хороша, я думаю, мы могли бы добавить PCG64DXSM в следующем выпуске и, возможно, пересмотреть значение по умолчанию в нескольких выпусках в будущем.

Выбор за вами!

У меня нет проблем с доставкой вам версии 1.0 PCG64. Множество других людей использовали этот вариант.

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

(Тем не менее, если бы это был мой выбор, несмотря на возможные обвинения в безрассудстве, я бы, вероятно, отправил новый; я думаю, что задержка для его запуска и запуска в Numpy довольно минимальна. И риск очень низкий - он уже тщательно протестирован с BigCrush и протестирован с PractRand до 16 ТБ (включая cm_mcg_dxsm_64_32 что составляет четверть размера [без потоков, 32-битный вывод]), и, вероятно, достигнет 32 ТБ менее чем за неделю .)

[Рад, что выступление стало немного лучше. Пять лет назад использование предварительно повторенного состояния было пессимизацией для 128-битных размеров со 128-битными множителями. Но это было тогда, на машинах, на которых я тестировал, с тестами, которые я использовал.]

Я имел в виду больше об использовании имени PCG64 для варианта 1.0, когда вы собираетесь использовать это имя для ссылки на вариант 2.0.

@rkern Если это просто проблема наименования, то PCG64DXSM и PCG64 прекрасно их отделяют, не так ли?

Для numpy, конечно. Мне просто интересно, не предпочла бы @imneme , чтобы мы не PCG64 когда она собирается продвигать вариант 2.0 под этим именем в версии C ++. Я чутко отношусь к тому факту, что неправильное указание имен означает, что некоторые люди могут протестировать PCG64 numpy и сравнить это с утверждениями, которые будут сделаны на pcg-random.org по поводу версии 2.0. Cf почти любой разговор о ГПСЧ Боба Дженкина.

В разделе 6.3 документа PCG говорится:

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

И библиотеки C и C ++ структурированы таким образом. Библиотеки предоставляют

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

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

Если вы собираетесь поставлять ГПСЧ с именем PCG64 , я бы сказал, что в вашей документации достаточно указать, какой именно вариант PCG - другими словами, сказать, какому члену семейства он соответствует в нижнем -уровневый интерфейс библиотеки C или C ++.

Генератор по умолчанию реализован как np.random.default_gen() в https://github.com/numpy/numpy/pull/13840. ( @rkern для справки в будущем, вероятно, лучше явно называть PR - их легко пропустить в GitHub, если вы предоставите только обратную ссылку, поскольку для этого нет уведомлений.)

Одна мелочь: как насчет того, чтобы вместо этого называть это np.random.default_generator() ? gen мне кажется слишком коротким / неочевидным. Мне было бы любопытно, что думают другие.

как насчет того, чтобы вместо этого вызвать этот np.random.default_generator ()?

У меня была такая же мысль, но тогда np.random.default_generator() - это длинная шерсть, поэтому я поиграл с default_rng .

👍 Мне тоже нравится default_rng больше, чем default_gen . Я был бы доволен любым из них, хотя я все равно склоняюсь к default_generator .

: +1: для default_rng() .

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