Numpy: Entscheiden Sie sich für einen neuen PRNG BitGenerator-Standard

Erstellt am 27. Mai 2019  ·  166Kommentare  ·  Quelle: numpy/numpy

13163 wird den lang erwarteten Ersatz der PRNG-Infrastruktur von numpy einführen. Um diese PR überschaubar zu halten, werden wir sie zusammenführen, um sie zu meistern, bevor alle Entscheidungen abgeschlossen sind. So wird beispielsweise BitGenerator als Standard nominiert.

Wir müssen vor der ersten Veröffentlichung mit der neuen Infrastruktur eine Entscheidung treffen. Sobald wir veröffentlicht sind, bleiben wir eine Weile bei unserer Wahl, daher sollten wir sicher sein, dass wir mit unserer Entscheidung zufrieden sind.

Auf der anderen Seite, ist die Wahl des Standard hat nicht so viele Folgen. Wir sprechen nicht über die Standardeinstellung BitGenerator , die den Convenience-Funktionen numpy.random.* zugrunde liegt. Gemäß NEP 19 bleiben diese Aliase für das alte RandomState , dessen BitGenerator MT19937 bleibt. Der einzige Ort, an dem die Standardeinstellung eingeht, ist, wenn Generator() ohne Argumente instanziiert wird. dh wenn ein Benutzer ein Generator mit einem beliebigen Status anfordert, vermutlich um dann die .seed() -Methode darauf aufzurufen. Dies könnte wahrscheinlich ziemlich selten sein, da es ungefähr so ​​einfach wäre, es nur explizit mit den gesetzten BitGenerator instanziieren, die sie tatsächlich wollen. Eine legitime Wahl könnte darin bestehen, keinen Standard zu benennen und vom Benutzer immer die Angabe eines BitGenerator verlangen.

Trotzdem werden wir Empfehlungen haben, welche BitGenerator Leute die meiste Zeit verwenden sollten, und während wir Empfehlungen ziemlich frei ändern können, wird wahrscheinlich in Büchern, Blogs und Tutorials über die meisten geschrieben, über die man stolz ist , und derartige.

IMO, es gibt einige Hauptoptionen (mit meinem Kommentar können Sie bitte nicht zustimmen; ich habe nicht versucht, alle relevanten Kommentare von # 13163 zu portieren):

Kein Standard

Benötigen Sie immer Generator(ChosenBitGenerator(maybe_seed)) . Dies ist ein wenig unfreundlich, aber da es eine ziemlich bequeme Möglichkeit ist, den Generator für die Reproduzierbarkeit richtig zu initialisieren, kann dies ohnehin passieren, selbst wenn wir einen Standard haben.

MT19937

Dies wäre eine gute konservative Wahl. Es ist sicherlich nicht schlechter als der Status Quo. Da der Mersenne Twister immer noch allgemein als "Standard" -Wahl angesehen wird, kann er akademischen Benutzern, die ihre Papiere benötigen, helfen, von Personen überprüft zu werden, die möglicherweise "Nicht-Standard" -Entscheidungen in Frage stellen, unabhängig von den spezifischen Eigenschaften des PRNG. "Niemand wurde jemals entlassen, weil er IBM eingestellt hat." Die Hauptnachteile von MT19937 sind meistens, dass es aufgrund seines sehr großen Zustands langsamer ist als einige der verfügbaren Alternativen und dass es einige statistische Qualitätstests nicht besteht. Bei der Auswahl eines anderen PRNG haben wir eine Möglichkeit (aber keine Verpflichtung, IMO), die wir hier beurteilen können, und versuchen, "den Standard" zu verschieben, wenn wir dies wünschen.

PCG64

Dies ist wahrscheinlich diejenige, die ich persönlich am häufigsten verwenden werde. Der Hauptnachteil besteht darin, dass eine 128-Bit-Ganzzahlarithmetik verwendet wird, die in C emuliert wird, wenn der Compiler keinen solchen Ganzzahltyp bereitstellt. Die beiden Hauptplattformen, für die dies der Fall ist, sind 32-Bit-CPUs und 64-Bit-MSVC, die 128-Bit-Ganzzahlen selbst dann nicht unterstützen, wenn die CPU dies tut. Persönlich schlage ich nicht vor, dass die immer seltener werdenden 32-Bit-CPUs unsere Auswahl bestimmen. Die MSVC-Leistung ist jedoch wichtig, da unsere Windows-Builds diesen Compiler und keine anderen Windows-Compiler benötigen. Es kann wahrscheinlich mit einigen Assembly- / Compiler-Eigenschaften behoben werden, aber jemand müsste sie schreiben. Die Tatsache, dass es nur MSVC ist, für die wir dies tun müssen, macht dies etwas schmackhafter als zu anderen Zeiten, wenn wir mit der Montage konfrontiert sind.

Xoshiro256

Eine weitere moderne Wahl für ein kleines, schnelles PRNG. Es gibt einige bekannte statistische Macken, aber es ist unwahrscheinlich, dass sie für die meisten Anwendungen ein wesentlicher Faktor sind. Diese Macken lassen mich davor zurückschrecken, aber das ist meine persönliche Wahl für den Code, den ich schreiben werde.

15 - Discussion numpy.random

Hilfreichster Kommentar

Sehr inspiriert von diesem Thread, habe ich einige Neuigkeiten zu berichten…

Hintergrund

In vielerlei Hinsicht ist pcg64 ziemlich gut; Zum Beispiel erhält es unter den üblichen statistischen Qualitätsmaßstäben ein sauberes Gesundheitszeugnis. Es wurde auf verschiedene Arten getestet; Zuletzt habe ich es mit PractRand auf ein halbes Petabyte gebracht. Es funktioniert gut in normalen Anwendungsfällen.

ABER die Pathologien, die in diesem Thread auftauchten, passten nicht gut zu mir. Sicher, ich könnte sagen " gut, halte es nicht so ", aber der springende Punkt eines PRNG für allgemeine Zwecke ist, dass es robust sein sollte. Ich wollte es besser machen ...

Vor ungefähr 25 Tagen begann ich darüber nachzudenken, ein neues Mitglied der PCG-Familie zu entwerfen…

Tor

Mein Ziel war es, ein neues Mitglied der PCG-Familie zu entwerfen, das die derzeitige Variante pcg64 ersetzen könnte. So wie:

  • Die Ausgabefunktion sollte die Bits mehr als XSL RR verschlüsseln (da dadurch die Probleme vermieden werden, die in diesem Thread aufgetreten sind).
  • Die Leistung sollte ungefähr so ​​schnell (oder schneller) sein als die aktuellen pcg64 .
  • Das Design muss PCG-ish sein (dh nicht trivial vorhersehbar sein und daher nicht zulassen, dass _jedes_ der Arbeit der Ausgabefunktion leicht rückgängig gemacht werden kann).

Wie immer gibt es einen Kompromiss, da wir versuchen, so schnell wie möglich die bestmögliche Qualität zu erzielen. Wenn uns die Geschwindigkeit überhaupt nicht wichtig wäre, könnten wir mehr Schritte in der Ausgabefunktion haben, um eine stärker verschlüsselte Ausgabe zu erzeugen, aber der Punkt von PCG war, dass die zugrunde liegende LCG „fast gut genug“ war und wir sie nicht brauchten sich so viel Mühe geben, wie wir es mit einem Zähler tun würden, der um 1 erhöht wird.

Spoiler

Ich freue mich über Erfolge! Vor ungefähr 25 Tagen, als ich zum ersten Mal darüber nachdachte, war ich tatsächlich im Urlaub. Als ich vor ungefähr zehn Tagen zurückkam, probierte ich meine Ideen aus und stellte erfreut fest, dass sie gut funktionierten. Die folgende Zeit wurde hauptsächlich für verschiedene Arten von Tests aufgewendet. Gestern war zufrieden genug , dass ich schob den Code in das C ++ Version von PCG. Tests bei kleinen Größen zeigen, dass es viel besser als XSL RR ist und mit RXS M konkurriert, aber es glänzt tatsächlich bei größeren Größen. Es erfüllt auch alle anderen Ziele.

Einzelheiten

FWIW ist die neue Ausgabefunktion (für den 64-Bit-Ausgabefall):

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;
}

Diese Ausgabefunktion ist von der weit verbreiteten Xorshift-Multiplikation inspiriert. Die Wahl der Multiplikatoren besteht darin, (a) die Anzahl der magischen Konstanten niedrig zu halten und (b) zu verhindern, dass die Permutation rückgängig gemacht wird (wenn Sie keinen Zugriff auf Bits niedriger Ordnung haben) und auch das gesamte "randomisierte" an sich “Qualität, die PCG-Ausgabefunktionen normalerweise haben.

Andere Änderungen

Es ist auch der Fall, dass 0xda942042e4dd58b5 der LCG-Multiplikator für dieses PRNG ist (und alle PCG-Generatoren cm_ Präfix von 0x2360ed051fc65da44385df649fccf645 das von pcg64 , ist diese Konstante in Bezug auf die Spektraltest-Eigenschaften eigentlich immer noch ziemlich gut, aber billiger zu multiplizieren, da 128-Bit × 64-Bit einfacher ist als 128-Bit × 128-Bit. Ich habe diese LCG-Konstante mehrere Jahre ohne Probleme verwendet. Bei Verwendung der Billig-Multiplikator-Variante führe ich die Ausgabefunktion für eine größere Parallelität auf Befehlsebene im voriterierten Zustand und nicht im nachiterierten Zustand aus.

Testen

Ich habe es gründlich getestet (PractRand und TestU01) und bin damit zufrieden. Die Tests umfassten Szenarien, die in diesem Thread beschrieben wurden (z. B. einen Ganggenerator entweder auf sequentiellen Dämpfen oder um 2 ^ 64 vorgerückt und ihre Ausgabe verschachtelt - ich habe auch eine Vierergruppe und eine 8192-Gruppe auf 8 TB ohne Probleme getestet als Strom und sein Gegenstück zum Gegenland).

Geschwindigkeit

Ich könnte ausführlich über Geschwindigkeitstests und Benchmarks sprechen. Es gibt alle möglichen Faktoren, die beeinflussen, ob ein PRNG in einem bestimmten Benchmark schneller als ein anderes läuft. Insgesamt scheint diese Variante jedoch oft etwas schneller, manchmal viel schneller und gelegentlich etwas langsamer zu sein. Faktoren wie der Compiler und die Anwendung haben einen viel größeren Einfluss auf die Variabilität der Benchmarks.

Verfügbarkeit

Benutzer des C ++ - Headers können auf dieses neue Familienmitglied _now_ als pcg_engines::cm_setseq_dxsm_128_64 zugreifen. Irgendwann in der Zukunft werde ich pcg64 von pcg_engines::setseq_xsl_rr_128_64 auf dieses neue Schema umstellen. Mein aktueller Plan ist es, dies in diesem Sommer im Rahmen einer PCG 2.0-Version zu tun.

Formelle Ankündigungen

Insgesamt bin ich sehr zufrieden mit diesem neuen Familienmitglied und irgendwann später im Sommer wird es Blog-Posts mit mehr Details geben, die wahrscheinlich auf diesen Thread verweisen.

Deine Entscheidungen...

Natürlich muss man herausfinden, was man damit machen soll. Unabhängig davon, ob Sie es verwenden oder nicht, wäre ich ziemlich gespannt, ob es in Ihren Geschwindigkeits-Benchmarks besser oder schlechter abschneidet.

Alle 166 Kommentare

Was macht der Intel Windows Compiler für 128-Bit-Ganzzahlen? Wie viel langsamer ist PCG64 mit MSVC kompiliert als MT1993 unter Windows? Ich vermute, dass die Jump-Ahead-Funktion weit verbreitet sein wird, daher ist es möglicherweise gut, sie standardmäßig zu verwenden.

Was macht der Intel Windows Compiler für 128-Bit-Ganzzahlen?

Nicht ganz sicher; Ich weiß nicht, ob es ABI-Implikationen gibt, durch die ICC eingeschränkt werden möchte. Wenn wir nur eine Vorstellung von der generierten Assembly erhalten möchten, die wir verwenden könnten, ist dies eine praktische Ressource: https://godbolt.org/z/kBntXH

Ich vermute, dass die Jump-Ahead-Funktion weit verbreitet sein wird, daher ist es möglicherweise gut, sie standardmäßig zu verwenden.

Meinen Sie eher einstellbare Ströme? Das ist ein guter Punkt, aber ich frage mich, ob es vielleicht nicht andersherum geht. Wenn unsere Wahl der Standardeinstellung tatsächlich von großer Bedeutung ist, werden die Benutzer diese Funktionen im Bibliothekscode möglicherweise ausführlicher verwenden, ohne zu dokumentieren, dass sie diese "erweiterten" Funktionen benötigen, da sie diese schließlich benötigen sind "Standard" verfügbar. Wenn dann ein anderer Benutzer versucht, diese Bibliothek aus Gründen der Geschwindigkeit oder aus anderen Gründen mit einem weniger vorgestellten BitGenerator zu verwenden, stößt er gegen eine Mauer. In einer Welt von No default oder MT19937 würden Bibliotheken eher über die erweiterten Funktionen nachdenken und diese dokumentieren, die sie benötigen.

Auf der anderen Seite würde diese Möglichkeit dazu führen, dass die BitGenerator s ohne einstellbare Streams weniger wünschenswert aussehen, und ich mag die Idee, das, was als Best Practice in diese Richtung angesehen wird, voranzutreiben (rein persönlich; ich fühle mich nicht eine Verpflichtung, NumPy-the-project dazu zu bringen, diesen Gedanken zu teilen). Es könnte helfen, einige der Missbräuche zu vermeiden, die ich sehe, wenn Leute .seed() in der Mitte ihres Codes stehen. Aber auch hier ist alles, was auf der Vorstellung beruht, dass ein Ausfall das Verhalten der Menschen erheblich verändert, so dass all diese Bedenken wahrscheinlich ziemlich abgeschwächt werden.

Wie viel langsamer ist PCG64 mit MSVC kompiliert als MT1993 unter Windows?

In den von @bashtage in # 13163 veröffentlichten Benchmarks ist PCG64 fast halb so schnell wie MT19937, was für MSVC und Freunde eine ziemlich enttäuschende Leistung darstellt. Es ist im Vergleich zu 23% schneller unter Linux.

Was macht der Intel Windows Compiler für 128-Bit-Ganzzahlen?

Andere Compiler wie Clang, GCC und der Intel-Compiler implementieren 128-Bit-Ganzzahlen auf 64-Bit-Systemen genauso wie 64-Bit-Ganzzahlen auf 32-Bit-Systemen. Dieselben Techniken ohne neue Ideen. Microsoft hat sich nicht die Mühe gemacht, dies für MSVC zu tun, sodass keine 128-Bit-Ganzzahlen direkt vom Compiler unterstützt werden.

Infolgedessen implementiert die vorhandene Implementierung von PCG64 in # 13163 für MSVC 128-Bit-Mathematik von Hand, indem Microsoft Intrinsics wie _umul128 in x86_64 _mulx_u64 verwendet werden). Dadurch wird codiert, was GCC, Clang und der Intel-Compiler selbst tun würden. Das größte Problem ist wahrscheinlich, dass der Microsoft-Compiler diese Eigenschaften nicht sehr gut optimiert (hoffentlich sind sie zumindest inline?). Es ist möglich, dass handcodierte Assembler schneller gehen, aber die richtige Lösung wäre, wenn der Compiler nicht so teuflisch schlecht wäre.

Ich vermute, dass die Jump-Ahead-Funktion weit verbreitet sein wird, daher ist es möglicherweise gut, sie standardmäßig zu verwenden.

Ich bin froh, dass du gerne vorausspringst, aber ich bin gespannt, warum du denkst, dass es weit verbreitet ist. (Ich persönlich mag distance , was Ihnen sagt, wie weit zwei PRNGs voneinander entfernt sind. Das ist in der C ++ - Version von PCG, aber nicht in der C-Version. Es wäre trivial genug, es hinzuzufügen, wenn es welche gäbe Interesse.)

Ich bin froh, dass du gerne vorausspringst, aber ich bin gespannt, warum du denkst, dass es weit verbreitet ist.

Wahrscheinlich unbekannt mit der aktuellen Terminologie. Was ich meine, sind leicht zu erhaltende unabhängige Streams, mit denen Simulationen parallel ausgeführt werden können. Ich weiß nicht, wie viele Simulationsprobleme parallelisiert werden können, aber ich vermute, dass es eine Menge ist und angesichts der Anzahl der Kerne, die heutzutage auf einen Chip gelangen, könnte dies einen Geschwindigkeitsnachteil leicht ausgleichen.

Microsoft hat sich nicht die Mühe gemacht, dies für MSVC zu tun, sodass keine 128-Bit-Ganzzahlen direkt vom Compiler unterstützt werden.

Das wird unseren Rädern schaden, OTOH. Viele Leute unter Windows beziehen ihre Pakete von Anaconda oder Enthought, die beide Intel verwenden, und Leute, die sich wirklich für die Leistung interessieren, sind wahrscheinlich unter Linux, Mac oder vielleicht AIX.

EDIT: Und vielleicht, wenn Microsoft besorgt ist, könnten sie eine Prämie anbieten, um das Problem zu beheben.

FWIW, hier ist die Assembly, die clang für die kritische Funktion generieren würde, einschließlich der Bits, die zum Entpacken / Umpacken von uint128_t in die Struktur von uint64_t s benötigt werden: https: // godbolt.org/z/Gtp3os

Sehr cool, @rkern. Gibt es eine Chance, dass Sie dasselbe tun können, um zu sehen, was MSVC mit dem handgeschriebenen 128-Bit-Code macht?

Sehr cool, @rkern. Gibt es eine Chance, dass Sie dasselbe tun können, um zu sehen, was MSVC mit dem handgeschriebenen 128-Bit-Code macht?

Es ist nicht schön. ~ https://godbolt.org/z/a5L5Gz~

Hoppla, vergessen Sie, -O3 hinzuzufügen, aber es ist immer noch hässlich: https://godbolt.org/z/yiQRhd

Es ist nicht ganz so schlimm. Sie hatten keine Optimierung aktiviert, also wurde nichts eingebunden. Ich habe /Ox hinzugefügt (vielleicht gibt es eine bessere Option?). Ich habe auch den Code für die Verwendung des integrierten Rotations-Intrinsics ( _rotr64 ) korrigiert, da MSVC anscheinend nicht in der Lage ist, das C-Rotations-Idiom zu erkennen.

Trotzdem ein Zugunglück. Aber ich denke, es ist fair zu sagen, dass der PCG64-Code mit ein wenig Aufmerksamkeit so angepasst werden könnte, dass er auf MSVC zu etwas kompiliert wird, das nicht für alle peinlich ist.

Um alles andere zusammenzuführen, wählen Sie vorerst "no default". So können wir später (auch nach einer oder mehreren Versionen) die Entscheidung über die Standardeinstellung treffen, ohne die Kompatibilität zu beeinträchtigen.

Die meisten unserer Benutzer sind keine Experten für Zufallszahlen. Wir sollten ihnen Standardeinstellungen zur Verfügung stellen.

Was passiert, wenn wir etwas ändern, abgesehen von dem prosaischen "Jetzt müssen sie mehr Code eingeben"? In dem Fall, in dem der BitGenerator fest codiert ist (weil wir keine Standardeinstellung angegeben haben), muss jeder nicht anspruchsvolle Benutzer jetzt seinen Code umgestalten und hoffentlich die Nuancen seiner Wahl verstehen (beachten Sie, dass wir uns nicht einmal einig sind, was ist das Beste). Wenn wir jedoch einen Standard angeben, werden die Tests möglicherweise geräuschvoll unterbrochen, da der neue Standard oder die neue Version nicht mit Bitströmen kompatibel sind.

Zwischen der Annahme, dass der Bitstrom immer konstant sein wird, und der Annahme, dass die NumPy- Entwickler wissen, was sie tun, und den Standardwerten, die Best-of-Brand sein sollten, würde ich mich auf die Seite der zweiten Annahme stellen, selbst wenn dies der Fall ist bricht den ersten.

Bearbeiten: Klären Sie, welche Entwickler wissen sollten, was sie tun

Die meisten unserer Benutzer sind keine Experten für Zufallszahlen. Wir sollten ihnen Standardeinstellungen zur Verfügung stellen.

Nun, wir werden zumindest Empfehlungen dokumentieren, unabhängig davon, ob wir einen Standard haben oder nicht.

Was passiert, wenn wir etwas ändern, abgesehen von dem prosaischen "Jetzt müssen sie mehr Code eingeben"?

An was für ein "Etwas" denkst du? Ich kann Ihrem Argument nicht folgen.

Was passiert, wenn wir etwas ändern, abgesehen von dem prosaischen "Jetzt müssen sie mehr Code eingeben"?

An was für ein "Etwas" denkst du? Ich kann Ihrem Argument nicht folgen.

@mattip bezieht sich auf das Ändern des Standardbitgenerators.

Dies würde Benutzer, die es übernommen haben, verrückt machen und eine Codeänderung erfordern.

Zum Beispiel, wenn Sie verwendet haben

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

und der zugrunde liegende Bitgenerator wurde geändert, dann wäre dies falsch.

Wenn du das Vernünftigere getan und benutzt hast

Generator(BitGenerator(1234))

dann würdest du es nicht sehen.

IMO, Wenn wir über die Wahl der Standardeinstellung nachdenken, sollten wir denken, dass sie behoben ist, bis ein schwerwiegender Fehler im zugrunde liegenden Bitgenerator gefunden wird oder Intel seinen Chips ein QUANTUM_NI hinzufügt, was zu einer Verbesserung der zufälligen Leistung um viele OOM führt.

Mir ist klar, dass ich hier ein bisschen ein Außenseiter bin, aber ich denke nicht, dass es vernünftig ist zu erwarten, dass das, welches PRNG die Standardauswahl ist, für immer festgelegt ist und sich nie ändert. (In C ++ liegt beispielsweise std::default_random_engine im Ermessen der Implementierung und kann sich von Release zu Release ändern.)

Vielmehr muss es einen Mechanismus geben, um frühere Ergebnisse zu reproduzieren. Sobald eine bestimmte Implementierung vorhanden ist, ist es daher sehr unkühl, sie zu ändern (z. B. MT19937 _ist_ MT19937, Sie können sie nicht optimieren, um eine andere Ausgabe zu erzielen). [Und es ist auch uncool, eine bereits vorhandene Implementierung zu entfernen.]

Wenn sich die Standardeinstellung ändert, müssen Personen, die weiterhin alte Ergebnisse reproduzieren möchten, nach der vorherigen Standardeinstellung nach Namen fragen. (Sie können dies tun, indem Sie einen Mechanismus bereitstellen, um den Standard auszuwählen, der einer früheren Version entspricht.)

Das heißt, selbst wenn Sie den Standardgenerator gegen etwas anderes austauschen dürfen, muss er unbedingt besser sein - alle im Standardgenerator vorhandenen Funktionen sind eine Verpflichtung, diese Funktion in Zukunft zu unterstützen. Wenn Ihr Standardgenerator effiziente advance , können Sie das später nicht wirklich wegnehmen. (Sie können möglicherweise erweiterte Funktionen im Standardgenerator deaktivieren, um dieses Problem zu vermeiden.)

Zusammenfassend lässt sich sagen, dass es Möglichkeiten gibt, sicherzustellen, dass Verwendungen reproduzierbare Ergebnisse erzielen können, ohne sich an einen Vertrag zu binden, bei dem der Standard für immer unverändert bleibt. Es wird auch den Einsatz für die Wahl reduzieren, die Sie treffen.

(FWIW, das habe ich in PCG gemacht. Das Standard-PCG-32-Bit-PRNG ist derzeit die XSH-RR-Variante [Zugriff als pcg_setseq_64_xsh_rr_32_random_r in der C-Bibliothek und die Klasse pcg_engines::setseq_xsh_rr_64_32 in C ++ Bibliothek], aber wenn Sie wirklich zukunftssichere Reproduzierbarkeit wünschen, sollten Sie XSH-RR explizit angeben, anstatt pcg32_random_r oder pcg32 die Aliase sind und im Prinzip auf etwas anderes aktualisiert werden können .)

Es ist nicht wirklich für immer (dieses gesamte Projekt basiert zu 90% auf einem echten, echten und geehrten Versprechen für immer, das vor etwa 14 Jahren gemacht wurde), aber wie Sie sagen, muss der Wechsel (a) ein zwingender Grund für eine Änderung sein und (b) würde Nehmen Sie sich mindestens ein paar Jahre Zeit, um den Abschreibungszyklus anzugeben.

Es ist heute viel besser, sich so viel Mühe wie möglich zu geben.

Eine Sache, die natürlich nicht verboten ist, ist die Verbesserung des PRNG-Codes nach der Veröffentlichung, da er dieselben Werte erzeugt. Wenn wir beispielsweise ein PRNG verwenden, das uint128 verwendet, können wir MS die Unterstützung von uint128 (Fat Chance) hinzufügen oder in einer zukünftigen Version eine Assembly für Win64 hinzufügen.

Zum Beispiel, wenn Sie verwendet haben

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

und der zugrunde liegende Bitgenerator wurde geändert, dann wäre dies falsch.

Richtig, das scheint mit @ eric-wieser für die Option "Kein Standard" zu argumentieren, die ich nicht mit der ursprünglichen Aussage "Die meisten unserer Benutzer sind keine Experten für Zufallszahlen, wir sollten ihnen Standardeinstellungen geben." . "

Zwischen keinem Standard und einem freundlichen, vollständig angenommenen Standard würde ich immer Letzteres wählen:

Jetzt:

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

_Meine Vorliebe:

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

Jetzt denke ich nicht, dass dies eintreten wird, aber ich denke, eine Möglichkeit, die Verwendung von RandomState zu verlängern, besteht darin, dies nur Benutzern zur Verfügung zu stellen, die sich als Experten genug fühlen, um einen Bitgenerator zu wählen.

Zusammenfassend lässt sich sagen, dass es Möglichkeiten gibt, sicherzustellen, dass Verwendungen reproduzierbare Ergebnisse erzielen können, ohne sich an einen Vertrag zu binden, bei dem der Standard für immer unverändert bleibt. Es wird auch den Einsatz für die Wahl reduzieren, die Sie treffen.

Ja, das haben wir. Benutzer können BitGenerator s nach Namen greifen (z. B. MT19937 , PCG64 usw.) und sie mit Samen instanziieren. BitGenerator Objekte implementieren den einheitlichen PRNG-Kernalgorithmus mit einer begrenzten Anzahl von Methoden zum Zeichnen von einheitlichen [0..1) float64 s und ganzen Zahlen (sowie den unterhaltsamen Jumpahead- / Stream-Funktionen, die sie haben). . Die Generator -Klasse, über die wir sprechen, nimmt ein bereitgestelltes BitGenerator -Objekt und umschließt es, um alle ungleichmäßigen Verteilungen, die Gaußschen, die Gammas, die Binome usw. bereitzustellen strenge Stream-Kompatibilitätsgarantien für die BitGenerator s. Wir werden keine loswerden (die es bis zur Veröffentlichung schaffen) und sie auch nicht ändern.

Die zentrale Frage zum Standard lautet: "Was macht der Code g = Generator() ohne Argumente?" Im Moment wird in der PR ein Xoshiro256 BitGenerator mit einem beliebigen Zustand erstellt (dh aus einer guten Entropiequelle wie /dev/urandom ). Die Option "Keine Standardeinstellung" besteht darin, dies zu einem Fehler zu machen. Benutzer müssten die gewünschten BitGenerator explizit benennen. @ eric-wiesers Punkt ist, dass "No default" eine kategorisch sichere Option für die erste Version ist. Eine spätere Version mit einem Standard verursacht keine Probleme wie das Ändern eines vorhandenen Standards.

@rkern , Wenn Sie sich nur für den Fall ohne Argumente interessieren , in dem der generiert wird , spielt es keine Rolle, was der zugrunde liegende Generator ist - er könnte sich stündlich ändern, da die Ergebnisse niemals reproduzierbar wären ( unterschiedliche Läufe würden unterschiedliche Samen erhalten).

Im Gegensatz dazu scheint sich Startwert versehen ist.

@rkern , Wenn Sie sich nur für den Fall "keine Argumente" interessieren, in dem der Startwert automatisch aus der verfügbaren Entropie generiert wird, spielt es keine Rolle, was der zugrunde liegende Generator ist - er könnte sich stündlich ändern, da die Ergebnisse niemals reproduzierbar wären ( unterschiedliche Läufe würden unterschiedliche Samen erhalten).

Sie können das BitGenerator nach seiner Erstellung erneut setzen. Wenn also Generator() funktioniert, erwarte ich voll und ganz, dass Leute, die ein gesätes PRNG wollen, es einfach in die nächste Zeile setzen, wie im Beispiel von @bashtage :

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

Das ist etwas langweilig, weshalb ich oben vorgeschlagen habe, dass sich die meisten Leute normalerweise sowieso für Generator(PCG64(<seed>)) entscheiden würden, da es in Bezug auf die Eingabe genauso praktisch ist. @Bashtage stellt jedoch einen gewissen Widerstand fest, wenn eine zusätzliche Entscheidung getroffen wird.

Ich denke, wir haben auch eine umfassendere Frage vor uns: "Auf welche Weise möchten wir, dass Benutzer eine davon instanziieren? Und wenn diese Methoden Standardeinstellungen haben, wie sollten diese Standardeinstellungen sein?" Wir haben einige offene Designbereiche und @bashtages Vorschlag für Generator(<seed>) oder Generator(DefaultBitGenerator(<seed>)) ist immer noch möglich.

@bashtage Wie viel würde Ihrer Meinung nach Dokumentation helfen? Das heißt, wenn wir oben sagten " PCG64 ist unser bevorzugter Standard BitGenerator " und Generator(PCG64(seed)) in allen Beispielen konsistent verwendet haben (wenn andere Algorithmen nicht speziell demonstriert wurden)?

Ich könnte mehr davon überzeugt sein, eine default_generator(<seed>) _Funktion_ über Generator(<seed>) oder g=Generator();g.seed(<seed>) . Wenn wir es dann wirklich ändern mussten und nichts kaputt machen wollten, konnten wir einfach eine neue Funktion hinzufügen und der alten Warnungen hinzufügen. Ich könnte empfehlen, es für die erste Veröffentlichung als experimental zu markieren, damit wir etwas Zeit haben, diese Infrastruktur in freier Wildbahn zu beobachten, bevor wir eine feste Verpflichtung eingehen.

Was ist mit dem Erstellen eines DefaultBitGenerator -Objekts, das keine Details seines internen Status preisgibt? Dies wäre ein Proxy für eines der anderen Bitgeneratorobjekte, könnte aber im Prinzip jedes von ihnen umschließen - außer natürlich für seine spezifische Folge von generierten Zahlen. Dies würde Benutzer hoffentlich davon abhalten, programmatische Annahmen darüber zu treffen, was sie mit dem Standard-BitGenerator tun können, während wir weiterhin einen verbesserten Algorithmus verwenden können.

Ich stimme @bashtage zu, dass es viel freundlicher wäre, ganzzahlige Seeds direkt als Argumente für Generator , z. B. np.random.Generator(1234) . Dies würde natürlich DefaultBitGenerator .

In der Dokumentation zu Generator können wir einen vollständigen Verlauf des Standardbitgenerators in jeder früheren Version von NumPy angeben. Dies ist im Grunde der Vorschlag von

(Habe gerade diese Bearbeitung zu einem früheren Kommentar gesehen)

Hoppla, vergessen Sie, -O3 hinzuzufügen, aber es ist immer noch hässlich: https://godbolt.org/z/yiQRhd

Für MSVC ist es nicht -O3 , sondern /O2 oder /Ox (aber nicht /O3 !).

In der Dokumentation zu Generator können wir einen vollständigen Verlauf des Standardbitgenerators in jeder früheren Version von NumPy angeben. Dies ist im Grunde der Vorschlag von

Noch besser wäre es, ein explizites version -Argument wie das protocol -Argument in Generator / DefaultBitGenerator . Dann könnten Sie etwas wie np.random.Generator(123, version=1) schreiben, um anzuzeigen, dass Sie Zufallszahlen der Version 1 (was auch immer das ist) oder np.random.Generator(123, version=np.random.HIGHEST_VERSION) (Standardverhalten) möchten, um anzuzeigen, dass Sie den neuesten / größten Bitgenerator möchten (was auch immer das ist).

Vermutlich wäre version=0 das MT19937 , das NumPy bisher verwendet hat, und version=1 könnte jeder neue Standard sein, den wir auswählen.

Wie wäre es, wenn Sie tatsächlich ein DefaultBitGenerator-Objekt erstellen, das keine Details seines internen Status verfügbar macht? Dies wäre ein Proxy für eines der anderen Bitgeneratorobjekte, könnte aber im Prinzip jedes von ihnen umschließen - außer natürlich für seine spezifische Folge von generierten Zahlen. Dies würde Benutzer hoffentlich davon abhalten, programmatische Annahmen darüber zu treffen, was sie mit dem Standard-BitGenerator tun können, während wir weiterhin einen verbesserten Algorithmus verwenden können.

Hmmm. Das ist ansprechend. Es fühlt sich an, als würde es die Dinge überkomplizieren und diesem gordischen Knoten eine weitere Schleife hinzufügen (und dass uns ein Alexander-ähnlicherer Strich zur Verfügung stehen sollte), aber das ist wirklich das einzig schlechte, was ich dazu zu sagen habe. Dies erleichtert die verbleibenden Entscheidungen: Wir können uns auf statistische Qualität und Leistung konzentrieren.

Noch besser wäre es, ein explizites version Argument wie pickle in Generator / DefaultBitGenerator .

Ich bin weniger ein Fan davon. Im Gegensatz zum Fall pickle haben diese Dinge aussagekräftige Namen, die wir verwenden können, und wir haben den Mechanismus bereits implementiert.

Ich bin weniger ein Fan davon. Im Gegensatz zum Fall pickle haben diese Dinge aussagekräftige Namen, die wir verwenden können, und wir haben den Mechanismus bereits implementiert.

Betrachten Sie Folgendes aus der Sicht eines typischen NumPy-Benutzers:

  • np.random.Generator(seed, version=0) vs np.random.Generator(seed, version=1)
  • np.random.Generator(MT19937(seed)) vs np.random.Generator(PCG64(seed))

Ich denke, man kann davon ausgehen, dass die meisten unserer Benutzer nur sehr wenig über die relativen Vorteile von RNG-Algorithmen wissen. Aber auch ohne Dokumente zu lesen, können sie sicher erraten, dass version=1 (die neuere Standardeinstellung) in den meisten Fällen besser sein muss als version=0 . Für die meisten Benutzer ist das wirklich alles, was sie wissen müssen.

Im Gegensatz dazu sind Namen wie MT19937 und PCG64 wirklich nur für Experten oder Personen von Bedeutung, die unsere Dokumentation bereits gelesen haben :).

In Ihrem Anwendungsfall wählt niemand die version , die er _wollen_ möchte. Sie wählen nur die version , die sie benötigen, um die Ergebnisse einer bekannten Version zu replizieren. Sie suchen immer nach einem bestimmten Wert, der (implizit, weil wir ihn implizit zugelassen haben) in den Ergebnissen verwendet wurde, die sie replizieren möchten. Sie müssen nicht über die Beziehung zwischen mehreren Werten nachdenken.

Und auf jeden Fall haben wir dieses Maß an Reproduzierbarkeit zwischen den Releases in NEP 19 abgelehnt . Die Argumente gegen die Versionierung der Distributionen gelten auch hier.

Ein paar Gedanken zur Standardeinstellung:

  • 99,9% der Benutzer interessieren sich nicht für zugrunde liegende Algorithmen oder möchten diese kennen, sie möchten nur Zufallszahlen. Also +1, um eine Meinung für den Standard zu treffen, bitte lassen Sie die Benutzer nicht wählen.
  • dSFMT scheint einfach eine schnellere Version als MT19937 (wäre schön, in den Dokumenten anzugeben, wie schnell und "SSE2" entfernt wird). Da wir die Reproduzierbarkeit von Bitstreams ohnehin nicht garantieren, sind die internen Zustandsunterschiede nicht sehr interessant und dSFTM sollte MT19937 vorgezogen werden, selbst wenn das Hauptargument hier "das Leben während der Artikelüberprüfung erleichtern" lautet. .
  • Die Leistung ist für einen erheblichen Teil der Benutzerbasis von Bedeutung. Die statistischen Eigenschaften von Generatoren sind nur für einen sehr kleinen Teil der Benutzer von Bedeutung. Alle enthaltenen Generatoren sind für normale Anwendungsfälle geeignet. Also +1 für die Auswahl des schnellsten als Standard.

Entschuldigung - aber 32-Bit ist unter Windows immer noch wichtig - siehe https://github.com/pypa/manylinux/issues/118#issuecomment -481404761

Ich denke, wir sollten uns sehr um statistische Eigenschaften kümmern, da wir uns in einem großen Wandel hin zu einem stärkeren Einsatz von Resampling-Methoden in der Datenanalyse befinden. Wenn Python den Ruf hat, in dieser Angelegenheit etwas schlampig zu sein, auch wenn dies nur standardmäßig der Fall ist, könnte dies ein Hindernis für die Akzeptanz durch Personen sein, die Python für die Datenanalyse in Betracht ziehen. Ich würde mich sehr freuen, wenn Python das Paket der Wahl für Menschen wäre, die Permutation und Simulation ernst nehmen.

Ich denke, es ist in Ordnung, schnellere, nicht auf dem neuesten Stand der Technik befindliche Algorithmen anzubieten, aber nicht standardmäßig, soweit wir dies vermeiden und die Rückkompatibilität aufrechterhalten können.

Forensik und Diskussion finden Sie unter: https://arxiv.org/pdf/1810.10985.pdf

Lehrbücher enthalten Methoden, die implizit oder explizit davon ausgehen, dass echte IIDU [0,1) -Variablen durch PRNGs ersetzt werden können, ohne dass ein materieller Fehler auftritt [20, 7, 2, 16, 15]. Wir zeigen hier, dass diese Annahme für Algorithmen in vielen häufig verwendeten Statistikpaketen falsch ist, einschließlich MATLAB, Pythons Zufallsmodul, R, SPSS und Stata.

@kellieotto , @pbstark - Haben Sie eine Meinung darüber, welches PRNG wir hier wählen sollten, um die bestmögliche Grundlage für Permutation und Bootstrap zu schaffen?

Ich denke, wir sollten uns sehr um statistische Eigenschaften kümmern, da wir uns in einem großen Wandel hin zu einem stärkeren Einsatz von Resampling-Methoden in der Datenanalyse befinden

Einverstanden. Solange diese Eigenschaften für einige reale Anwendungsfälle relevant sind, ist dies sehr wichtig. Die Bedenken, die normalerweise angesprochen werden, sind immer äußerst akademisch.

Forensik und Diskussion finden Sie unter: https://arxiv.org/pdf/1810.10985.pdf

Sehr interessanter Artikel. Es wird der Schluss gezogen, dass NumPy im Gegensatz zu R, Python stdlib & co die einzige Bibliothek ist, die es richtig macht (oben auf Seite 9).

Es wäre sehr nützlich, noch konkretere Beispiele als in der Zeitung zu erhalten. Wenn unser aktueller Standardgenerator irgendwann auch ausfällt, wann ist das? Beispiele wie die sample -Funktion von R, die 40% gerade und 60% ungerade Zahlen erzeugt, wenn ~ 1,7 Milliarden Proben gezogen werden. Was ist hier das Bootstrapping / Resampling-Äquivalent?

Die neueste Version von R (3.6) behebt den Ansatz von Kürzung und Zufallsbits
um zufällige ganze Zahlen zu erzeugen. Der Mersenne Twister bleibt die Standardeinstellung
PRNG allerdings.

@Kellie Ottoboni [email protected] und ich denke die Standard-PRNG in
Pakete für wissenschaftliche Sprachen und Statistiken sollten kryptografisch sein
sicher (ein CS-PRNG, z. B. SHA256 im Zählermodus), mit der Option zu fallen
zurück zu etwas schnellerem, aber von geringerer Qualität (z. B. dem Mersenne Twister)
wenn Geschwindigkeit es erfordert.

Wir haben an einem CS-PRNG für Python gearbeitet:
https://github.com/statlab/cryptorandom

Die Leistung ist (noch) nicht großartig. Der Engpass scheint die Typkonvertierung zu sein
in Python, um binäre Zeichenfolgen (Hash-Ausgabe) als Ganzzahlen umzuwandeln. Wurden
Arbeiten an einer Implementierung, die einen größeren Teil der Arbeit nach C verlagert.

Prost,
Philip

Am Montag, den 27. Mai 2019 um 06:27 Uhr Ralf Gommers [email protected]
schrieb:

Ich denke, wir sollten uns sehr um statistische Eigenschaften kümmern, weil wir es sind
im Zuge einer großen Verlagerung hin zu einem stärkeren Einsatz von Resampling-Methoden in
Datenanalyse

Einverstanden. Solange diese Eigenschaften für eine reale Verwendung relevant sind
Fälle, das ist sehr wichtig. Die Bedenken, die normalerweise angesprochen werden, sind
immer sehr akademisch.

Für einige Forensik und Diskussion siehe:
https://arxiv.org/pdf/1810.10985.pdf

Sehr interessanter Artikel. Es kommt zu dem Schluss, dass NumPy das einzige ist
Bibliothek, die es richtig macht (oben auf Seite 9), im Gegensatz zu R, Python stdlib & co.

Es wäre sehr nützlich, noch konkretere Beispiele zu erhalten als in der
Papier. Wenn unser aktueller Standardgenerator irgendwann auch ausfällt,
wann ist das? Beispiele wie die Sample-Funktion von R erzeugen sogar 40%
Zahlen und 60% ungerade Zahlen beim Ziehen von ~ 1,7 Milliarden Proben. Was ist das?
Bootstrapping / Resampling hier gleichwertig?

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWJW445QDPGZDGXMPA3PXPOUFANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Die Leistung ist für einen erheblichen Teil der Benutzerbasis von Bedeutung. Die statistischen Eigenschaften von Generatoren sind nur für einen sehr kleinen Teil der Benutzer von Bedeutung. Alle enthaltenen Generatoren sind für normale Anwendungsfälle geeignet. Also +1 für die Auswahl des schnellsten als Standard

Erstens gibt es einfach keine Möglichkeit, „den Schnellsten“ auszuwählen. @bashtage führte einige Benchmarks für den aktuellen Code in # 13163 durch und es war überall auf der Karte, wobei dSFMT unter Windows gewann und von PCG-64 und Xoroshiro 256 unter Linux geschlagen wurde. Und das alles auf derselben Maschine mit demselben Benchmark. Unterschiedliche Hardwarearchitekturen (sogar Revisionen innerhalb von X86) sowie unterschiedliche Benchmarks machen einen Unterschied. (Wie bereits in diesem Thread erläutert, schneidet PCG in den Windows-Benchmarks aufgrund von Problemen mit MSVC schlecht ab, was wahrscheinlich auch vorübergehend ist, da sich MSVC möglicherweise verbessert oder die Probleme möglicherweise behoben werden. Wahrscheinlich ähnliche MSVC-Probleme erklären, warum Xoshiro wurde geschlagen.)

Ich frage mich auch, wie groß der „signifikante Anteil“ der Benutzer ist, denen Geschwindigkeit wichtig ist. Python selbst ist durchschnittlich etwa 50-mal langsamer als C. Welcher Teil der NumPy-Benutzerbasis führt sie auf PyPy aus (was einen 4-fachen Geschwindigkeitsschub bewirken würde)? Einige sicherlich, aber ich vermute keine sehr hohe Zahl.

Und für diesen „signifikanten Bruchteil“, der sich angesichts der oben beschriebenen Variabilität um Geschwindigkeit kümmert, wer wird dann einfach Ihr Wort dafür nehmen, dass das Standard-PRNG für seine Anwendung am schnellsten läuft? Eine sinnvolle Sache (die auch Spaß macht und für die meisten Benutzer erreichbar ist) ist es, die verschiedenen verfügbaren PRNGs zu vergleichen und herauszufinden, welche für sie am schnellsten ist.

Im Gegensatz dazu ist das Herausfinden der statistischen Qualität bestimmter PRNGs, wie Sie bemerken, für die meisten Benutzer nicht auf dem Radar (und selbst für Experten eine Herausforderung), obwohl sie möglicherweise Hinweise in der Dokumentation finden. Die meisten wissen nicht einmal, wann / ob sie sich darum kümmern sollen oder nicht. Ich würde argumentieren, dass dies ein Ort für Paternalismus ist - die Tatsache, dass sich die meisten Benutzer nicht um etwas kümmern, bedeutet nicht, dass sich die Betreuer nicht darum kümmern sollten.

Es ist wahr, dass alle enthaltenen PRNGs für die meisten Anwendungsfälle in Ordnung sind, aber das ist ein ziemlich niedriger Balken. Unix-Systeme wurden mit einer Vielzahl von PRNGs der C-Bibliothek ausgeliefert, die alle statistisch schrecklich sind und dennoch seit Jahren weit verbreitet sind, ohne dass sich die Welt von ihrer Achse dreht.

Neben den statistischen Eigenschaften gibt es noch andere Eigenschaften, die der Benutzer möglicherweise nicht für sich selbst haben möchte, aber ich möchte sie möglicherweise. Persönlich möchte ich als Anbieter von PRNGs eine triviale Vorhersehbarkeit vermeiden. Ich möchte nicht, dass jemand ein paar Ausgaben des PRNG betrachtet und dann sagen kann, wie alle zukünftigen Ausgaben aussehen werden. In den meisten Kontexten, in denen NumPy verwendet wird, ist die Vorhersagbarkeit kein Problem - es gibt keinen Gegner, der davon profitiert, dass die Sequenz leicht vorhergesagt werden kann. Aber irgendwo wird jemand die PRNGs von NumPy verwenden, nicht weil er NumPy benötigt, um Statistiken zu erstellen, sondern weil er dort zuvor PRNGs gefunden hat. Dieser Code kann einem tatsächlichen Gegner gegenüberstehen, der von der Vorhersage des PRNG profitiert. Es lohnt sich nicht, viel zu zahlen (z. B. einen erheblichen Geschwindigkeitsverlust), um sich gegen diese Ausreißersituation zu versichern, aber eine bescheidene Versicherung könnte sich lohnen.

Forensik und Diskussion finden Sie unter: https://arxiv.org/pdf/1810.10985.pdf

Lehrbücher enthalten Methoden, die implizit oder explizit davon ausgehen, dass echte IIDU [0,1) -Variablen durch PRNGs ersetzt werden können, ohne dass ein materieller Fehler auftritt [20, 7, 2, 16, 15]. Wir zeigen hier, dass diese Annahme für Algorithmen in vielen häufig verwendeten Statistikpaketen falsch ist, einschließlich MATLAB, Pythons Zufallsmodul, R, SPSS und Stata.

FWIW, es gibt ein schönes Papier von @lemire über die effiziente Erzeugung einer Zahl in einem Bereich ohne Verzerrung. Ich habe das als Ausgangspunkt verwendet, um einige Benchmarks in meinem eigenen Artikel zu untersuchen und auszuführen. (Beim Generieren von 64-Bit verwendet die Lemire-Methode eine 128-Bit-Multiplikation, um eine langsame 64-Bit-Division mit all den bekannten Problemen zu vermeiden, die für MSVC-Benutzer auftreten können.)

@pbstark @kellieotto Ich habe Ihr Papier mit Interesse gelesen, als es auf arXiv auftauchte. Ich habe einige Freunde bei BIDS besucht und sie hatten Ihre Arbeit erwähnt. Der Diskussionsteil stellt fest, dass "bisher keine Statistik mit konsistenter Verzerrung gefunden wurde, die groß genug ist, um in O (10 ^ 5) -Replikationen erkannt zu werden" für MT19937. Hast du schon einen gefunden? Haben Sie ein konkretes Beispiel für ein PRNG mit 128-Bit-Status wie PCG64 gefunden? Dies scheint mir eine vernünftige Schwelle von praktischer Relevanz zu sein, bei der diese Überlegung andere überwiegen könnte (IMO), zumindest um einen allgemeinen Standard auszuwählen.

Das nette Feature unseres neuen PRNG-Frameworks # 13163 ist, dass es jedem ermöglicht, sein eigenes BitGenerator bereitzustellen, das einfach angeschlossen werden kann. Es muss nicht einmal numpy sein, damit die Leute es in ihrem numpy verwenden können Code. Ich möchte Sie ermutigen, die Implementierung von cryptorandom als BitGenerator in C zu betrachten, damit wir sie direkt mit den anderen Optionen vergleichen können.

Persönlich erwarte ich, dass diejenigen, die sich wirklich für Geschwindigkeit interessieren, bei Bedarf die Extrameile gehen (es ist hier nicht viel). Wir sollten sichere Standardeinstellungen bereitstellen, und ich gehe derzeit davon aus, dass dies sichere Standardeinstellungen für alle Zwecke mit Ausnahme der Kryptografie bedeutet (wir sollten wahrscheinlich eine Warnung dazu in den Dokumenten haben). Viele Benutzer legen Wert auf Geschwindigkeit, aber ehrlich gesagt scheue ich mich genau davor, ihr zu hohe Priorität einzuräumen.
Dieser Artikel, in dem Numpy gut abgeschnitten hat, scheint interessant zu sein (ein großes Lob an Robert, wahrscheinlich dafür, dass er es richtig gemacht hat!), Aber eigentlich ist der Sampler nicht der Bitgenerator.

@pbstark Vielleicht möchten Sie dies als BitGenerator implementieren, der mit numpy / randomgen kompatibel ist? Dies ist wahrscheinlich der einfachste Weg, um es zu beschleunigen und einem breiten Publikum in einer viel nützlicheren Form zugänglich zu machen. Da Sie und Kellie Ottoboni anscheinend in Berkeley sind, könnten wir uns irgendwann treffen, um das in Gang zu bringen? (Nur ein Angebot, ich sollte mir den Code zuerst selbst genauer ansehen).

In Bezug auf dieses _Random Sampling: Practice Makes Imperfect_-Papier ist es eine gute Lektüre, aber es ist erwähnenswert, dass wir, wenn wir 1 Billion Kerne hätten, die 100 Jahre lang eine Zahl pro Nanosekunde produzieren, weniger als 2 ^ 102 Zahlen erzeugen würden.

Für trivial vorhersehbare PRNGs (auch solche mit großen Zustandsräumen wie dem Mersenne Twister) können wir tatsächlich wissen, ob jemals eine bestimmte Ausgabesequenz erzeugt werden kann (und den Keim finden, der sie erzeugen würde, wenn sie existiert, oder uns wehmütig fühlen, wenn dies nicht der Fall ist ), aber für andere nicht trivial vorhersehbare PRNGs können wir nicht (leicht) wissen, welche Ausgabesequenzen niemals produziert werden können und welche vorhanden sind, aber so selten, dass es unwahrscheinlich ist, dass wir sie jemals in einem Zeitalter der Suche finden. (Wie Sie vielleicht wissen, habe ich ein PRNG , von dem ich weiß, dass es eine Zip-Datei mit _Twelth Night_ innerhalb von 2 ^ 80 Ausgaben ausspuckt, aber viel Glück, es jemals zu finden.)

Wenn Sie wirklich eine Kryptografie benötigen, ist die einzige Wahl für moderne Hardware
AES, da es eine spezielle Anweisung hat. @lemire hat eine Implementierung
hier https://github.com/lemire/testingRNG/blob/master/source/aesctr.h das
ist so schnell wie Nicht-Krypto-Generatoren. Es gibt auch ChaCha20, das gehen kann
schnell mit SIMD. Beide werden auf alter Hardware allerdings sehr langsam sein. ThreeFry und
Philox sind bereits enthalten und sind Cryptolite Counter Prngs.

IMO-Krypto wird in Bezug auf Kostenvorteile überbewertet. Mir sind keine bekannt
wichtiger Rückzug aufgrund von PRNG-Problemen mit Mt, was ich für richtig halte
verwendet in der Reihenfolge zu 10e6 veröffentlichten Papieren. Die einzigen Anwendungen, die ich gesehen habe
Wo die PRNG wirklich problematisch war, waren Fälle, in denen der Zeitraum so war
klein, dass der Generator den gesamten Zyklus abgeschlossen hat. Auch hier der einzige
Effekt war die Reduzierung der Stichprobengröße der Studie, die die Hauptstichprobe replizierte
Ergebnisse einmal auf einem System mit einem größeren Zeitraum erneut ausgeführt.

Am Montag, 27. Mai 2019, 19:50 Uhr schrieb Robert Kern

@pbstark https://github.com/pbstark @kellieotto
https://github.com/kellieotto Ich habe Ihr Papier mit Interesse gelesen, wenn es
tauchte auf arXiv auf. Ich habe einige Freunde bei BIDS besucht, und sie hatten
erwähnte deine Arbeit. Der Diskussionsabschnitt stellt fest, dass "wir dies bisher nicht getan haben
fanden eine Statistik mit konsistenter Verzerrung, die groß genug ist, um in erkannt zu werden
O (10 ^ 5) Replikationen "für MT19937. Haben Sie schon eine gefunden? Haben Sie eine gefunden
Ein konkretes Beispiel für ein PRNG mit 128-Bit-Status wie PCG64? Das scheint mir zu sein
eine vernünftige Schwelle von praktischer Relevanz sein, wo diese Überlegung
könnte anfangen, andere (IMO) zu überwiegen, zumindest zum Zweck der Auswahl
eine allgemeine Standardeinstellung.

Das nette Feature unseres neuen PRNG-Frameworks # 13163
https://github.com/numpy/numpy/pull/13163 ist, dass es jedem erlaubt
Stellen Sie einen eigenen BitGenerator bereit, der einfach angeschlossen werden kann
Sie müssen sogar numpy sein, damit die Leute es in ihrem numpy-Code verwenden können. Ich würde
Wir empfehlen Ihnen, Cryptorandom als BitGenerator in C zu implementieren
so können wir es Kopf an Kopf mit den anderen Optionen vergleichen.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=ABKTSRO5PKW4MRFSBGUFUNTPXQUOLA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4DFVREXG43ZW
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/ABKTSRMRIHC4OYDR52HLTHDPXQUOLANCNFSM4HPX3CHA
.

Ich frage mich auch, wie groß der „signifikante Anteil“ der Benutzer ist, denen Geschwindigkeit wichtig ist. Python selbst ist im Durchschnitt etwa 50-mal langsamer als C. Welcher Teil der NumPy-Benutzerbasis führt es auf PyPy aus (was einen 4-fachen Geschwindigkeitsschub bewirken würde)? Einige sicherlich, aber ich vermute keine sehr hohe Zahl.

Ich vermute, Sie sind kein normaler Benutzer :) NumPy ist meistens C unter der Haube und so schnell wie Ihr eigenes Ding in C (na ja, meistens schneller). Außerdem ist PyPy nicht produktionsbereit für wissenschaftliche Anwendungen und auf jeden Fall langsamer (da es auf die Verwendung der von NumPy verwendeten CPython-API beschränkt ist und daher die Vorteile seiner JIT nicht nutzen kann).

In jedem Fall ist dies kein Thema. Die Behauptung, dass Geschwindigkeit eine Rolle spielt, ist nicht umstritten.

@imneme Wir verwenden die Lemires-Methode für begrenzte Ganzzahlen. Da dies ein
Neuanfang ohne Vermächtnis oder Wertminderung, mit dem wir uns zunächst sehr bemüht haben
gute Algorithmen.

Am Montag, 27. Mai 2019, 19:46 schrieb imneme [email protected] :

Für einige Forensik und Diskussion siehe:
https://arxiv.org/pdf/1810.10985.pdf

Lehrbücher enthalten Methoden, die implizit oder explizit davon ausgehen, dass PRNGs dies können
echte IIDU [0,1) -Variablen ersetzen, ohne Material einzuführen
Fehler [20, 7, 2, 16, 15]. Wir zeigen hier, dass diese Annahme für falsch ist
Algorithmen in vielen häufig verwendeten Statistikpaketen, einschließlich MATLAB,
Pythons Zufallsmodul, R, SPSS und Stata.

FWIW, es gibt ein schönes Papier https://arxiv.org/abs/1805.10941 von @lemire
https://github.com/lemire zum effizienten Generieren einer Zahl in einem Bereich
ohne Vorurteile. Ich nutzte das als Ausgangspunkt, um einige zu erkunden und zu betreiben
Benchmarks auch in meinem eigenen Artikel
http://www.pcg-random.org/posts/bounded-rands.html . (Beim Generieren
Die 64-Bit-Methode von Lemire verwendet eine 128-Bit-Multiplikation, um eine langsame Multiplikation zu vermeiden
64-Bit-Division mit allen bekannten Problemen, die bei MSVC auftreten können
Benutzer.)

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/ABKTSRKV3KYKRLNMBKNU4JLPXQUA3ANCNFSM4HPX3CHA
.

Wir sollten sichere Standardeinstellungen angeben, und ich gehe derzeit davon aus, dass dies sichere Standardeinstellungen für alle Zwecke mit Ausnahme der Kryptografie bedeutet

Das ist schwer zu streiten. Meine Frage ist - was ist sicher? Es gibt nur unterschiedliche Grade der Quasi-Zufälligkeit mit verschiedenen Eigenschaften. Bisher habe ich noch niemanden gesehen, der ein konkretes Beispiel gegeben hat, auch hier nicht in anderen Ausgaben, PRs oder Threads. Nur über abstrakte statistische Eigenschaften zu sprechen, ist nicht hilfreich.

Meiner Meinung nach wäre PCG64 ein guter Standard. Der Geschwindigkeitsnachteil unter Windows wird für Leute, die Anaconda et al. al. und wird wahrscheinlich irgendwann behoben. Da die parallele Ausführung in Python neu ist, halte ich es auch für wünschenswert, einstellbare Streams zu haben.

Ich bin sehr skeptisch, dass die Geschwindigkeitsstrafe für PCG64 unter Visual Studio nicht beseitigt werden kann.

Wurde dies irgendwo sorgfältig geprüft?

Die Behauptung, dass Geschwindigkeit eine Rolle spielt, ist nicht umstritten.

Meine Frage ist - was ist sicher?

Wenden Sie die Logik konsequent an: "Was ist schnell"? Ich habe keine gute Idee, welche Numpy-Programme tatsächlich die Leistung von BitGenerator als signifikanten Engpass haben. Wenn ich ein doppelt so schnelles BitGenerator , bekomme ich dann eine Beschleunigung von 5% in meiner vollständigen Berechnung? Wahrscheinlich nicht einmal das. Python-nicht-so-schnell-wie-C ist nicht das Problem; Es ist nur so, dass selbst PRNG-lastige Programme, die tatsächlich nützlich sind, nicht viel Zeit in den BitGenerator verbringen. Wahrscheinlich ist jede der verfügbaren Optionen ausreichend.

Ich bin sehr skeptisch, dass die Geschwindigkeitsstrafe für PCG64 unter Visual Studio nicht beseitigt werden kann.

Up-Thread Ich zeige, wie clang PCG64 in Assembly kompiliert, die wir für 64-Bit-MSVC stehlen können. Nein, ich glaube nicht, dass MSVC unter 64-Bit-Windows ein unüberwindbares Problem ist.

Was möglicherweise schwieriger ist, ist PCG64 auf 32-Bit-Systemen, von denen möglicherweise nur noch 32-Bit-Windows für uns praktisch wichtig ist. In diesem Fall geht es weniger um MSVC als darum, uns auf die 32-Bit-ISA zu beschränken.

Was @Kellie Ottoboni [email protected] und ich darauf hinweisen, ist das
Selbst bei Problemen mit bescheidener Größe ist der Zustandsraum von MT zu klein, um angenähert zu werden
einheitliche Permutationen (n <2100) oder einheitliche Zufallsstichproben (n = 4e8, k = 1000).

Dies betrifft alles vom Bootstrap über Permutationstests bis hin zu MCMC.
Der Unterschied zwischen der beabsichtigten Verteilung und der tatsächlichen
Die Verteilung kann beliebig groß sein (Gesamtvariationsentfernung nähert sich
2). Es ist groß und es ist ernst.

Wir haben uns nicht bemüht, MT für "statistische" Funktionen in a zu brechen
paar Jahren. Ich bin mir ziemlich sicher, dass es einen systematischen Weg gibt, es zu brechen
(da die Verteilungsabstände so groß sind).

Prost,
Philip

Am Montag, den 27. Mai 2019 um 12:26 Uhr Robert Kern [email protected]
schrieb:

Die Behauptung, dass Geschwindigkeit eine Rolle spielt, ist nicht umstritten.

Meine Frage ist - was ist sicher?

Wenden Sie die Logik konsequent an: "Was ist schnell"? Ich habe keine gute Idee
Welche Numpy-Programme haben tatsächlich die Leistung des BitGenerators als
ein erheblicher Engpass. Wenn ich einen BitGenerator verwende, der doppelt so schnell ist,
Bekomme ich bei meiner vollständigen Berechnung eine Beschleunigung von 5%? Wahrscheinlich nicht einmal das.
Python-nicht-so-schnell-wie-C ist nicht das Problem; es ist nur so eben
PRNG-schwere Programme, die tatsächlich nützlich sind, geben nicht viel Geld aus
Zeit im BitGenerator. Wahrscheinlich sind alle verfügbaren Optionen
ausreichend.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWIDCPAJJ6DJ3RO332LPXQYVRANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

@pbstark Was ich sehen möchte, ist eine konkrete Implementierung eines Problems (könnte künstlich sein, aber nicht zu erfunden), bei dem MT oder ein 128-Bit-PRNG fehlschlägt und an dem cryptorandom würde. Können Sie auf einen Datensatz hinweisen, bei dem die Resampling-Methode mit einem 128-Bit-PRNG falsche Schlussfolgerungen und mit cryptorandom korrekte Schlussfolgerungen liefert?

Durch die Umstellung auf PCG64 verschlechtert sich die Untergrenze für die Größe des Problems.
da sein Zustandsraum noch kleiner als der von MT ist. Natürlich könnte es
produzieren immer noch "bessere" Zufälligkeit, indem es eine Untergruppe der
Permutationsgruppe gleichmäßiger als MT. Aber es muss vorher zusammenbrechen
500 wählen Sie 10 und vor 21!.

Prost,
Philip

Am Montag, den 27. Mai 2019 um 12:30 Uhr Robert Kern [email protected]
schrieb:

Ich bin sehr skeptisch, dass die PCG64-Geschwindigkeitsstrafe unter Visual Studio ist
etwas, das nicht weggewischt werden kann.

Up-Thread Ich zeige, wie Clang PCG64 zu einer Assembly zusammensetzt, die wir stehlen können
für 64-Bit-MSVC, also nein, ich glaube nicht, dass MSVC unter 64-Bit-Windows eine ist
unüberwindbares Problem.

Was möglicherweise schwieriger ist, ist PCG64 auf 32-Bit-Systemen, von denen nur 32-Bit
Windows kann für uns immer noch praktisch wichtig sein. In diesem Fall ist es weniger
über MSVC als über die Beschränkung auf die 32-Bit-ISA.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWK6QTB65Z4TJU76XKTPXQZGLANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Ich weiß nicht genug über PRNGs, um auf jeden Fall wirklich zu wiegen. Ich möchte nur, dass der Fokus zuerst auf den statistischen Eigenschaften liegt (wenn die Antwort lautet, dass sie alle sehr, sehr gut sind, gut). Eine Sache, die ich mich jetzt frage, ist die k-dimensionale Gleichverteilung. Verwenden wir derzeit Varianten von beispielsweise PCG, die hier im Vergleich zu MT gut abschneiden? (Aufgrund der nichtlinearen Dynamik bin ich etwas nervös, aber ich habe nicht genug Überblick über PRNGs und werde ihn in den nächsten 2 Tagen nicht bekommen ...

Es ist unwahrscheinlich, dass es viele Windows 32-Bit-Benutzer gibt, denen die Leistung auf dem neuesten Stand ist. Der Wechsel zu 64-Bit ist nicht sehr aufwendig.

Ich würde es auch gerne sehen.

Wir wissen - auf der Grundlage der Mathematik -, dass es viele große Probleme geben muss,
aber wir können noch nicht auf ein Beispiel verweisen.

Das Vorsorgeprinzip würde sagen, dass, da wir wissen, dass es große gibt
Probleme und wir wissen, wie wir sie verhindern können (CS-PRNGs), könnten wir genauso gut tun
Dies ist standardmäßig der Fall, und lassen Sie die Benutzer weniger vorsichtig sein, wenn sie dies wünschen.

Am Montag, den 27. Mai 2019 um 12:39 Uhr Robert Kern [email protected]
schrieb:

@pbstark https://github.com/pbstark Was ich sehen möchte, ist ein konkreter
Umsetzung des Problems (könnte künstlich sein, aber nicht zu erfunden) auf
Welches MT oder ein 128-Bit-PRNG ausfällt und Cryptorandom funktioniert. Können Sie
Weisen Sie auf einen Datensatz hin, bei dem die Resampling-Methode falsch ist
Schlussfolgerungen mit einem 128-Bit-PRNG und korrekte Schlussfolgerungen mit Kryptozufall?

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWITAGQFZDQSIFNEHETPXQ2FPANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

k-Äquidistribution ist eine Ensemble-Eigenschaft von PRNG-Ausgaben über die gesamte
Zeitraum der PRNG. Es ist eine gute Sache, aber es sagt nichts über andere aus
Arten von Zufallsfehlern, wie z. B. serielle Korrelation von Ausgaben.
Es ist eine relativ niedrige Bar.

Am Montag, den 27. Mai 2019 um 12:48 Uhr Sebastian Berg [email protected]
schrieb:

Ich weiß nicht genug über PRNGs, um auf jeden Fall wirklich zu wiegen, ich möchte nur
Der Fokus muss zuerst auf den statistischen Eigenschaften liegen (wenn die Antwort das ist
sie sind alle sehr sehr gut, gut). Eine Sache, die ich mich jetzt frage, ist die
k-dimensionale Gleichverteilung. Verwenden wir derzeit Varianten von beispielsweise PCG?
das hier gut im vergleich zu MT? (Aus nichtlinearer Dynamik kommend
macht mich etwas nervös, aber ich habe nicht genug Überblick über PRNGs und mich
werde es in den nächsten 2 Tagen nicht bekommen ...

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWOEB7KR2YJZWHRRAHLPXQ3HZANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

@pbstark MT besteht eine Reihe statistischer Tests nicht, die PCG (und andere Generatoren) bestehen.

@rkern

Wenn MSVC den ror-Befehl generieren soll, muss man meiner Meinung nach den intrinsischen "_rotr64" verwenden.

Man könnte auch das '/ O2'-Flag für die Optimierung bevorzugen.

Wenn man es betrachtet, ist es in der Tat am besten, es in Assembly zu schreiben, wenn man PCG64 verwenden möchte.

Für @pbstark sind hier einige Ausgaben des PCG-64, die mit einem Ihnen unbekannten 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

Angenommen, Sie initialisieren einen anderen PCG-Generator mit einem zufällig ausgewählten Startwert. Lassen Sie uns aus Gründen der Argumentation 0xb124fedbf31ce435ff0151f8a07496d3 auswählen. Wie viele Ausgaben müssen wir generieren, bevor wir diese bekannte Ausgabe entdecken? Da ich den Samen kenne, den ich oben verwendet habe, kann ich darauf antworten (über die Distanzfunktion von PCG), ungefähr 2,5 × 10 ^ 38 (oder ungefähr 2 ^ 127,5) Ausgänge. Als Referenz sind 10 ^ 38 Nanosekunden 230 Milliarden Mal so alt wie das Universum.

Es gibt also eine Sequenz in PCG-64, die wirklich vorhanden ist, aber praktisch werden Sie sie nie finden, wenn ich Ihnen nicht sage, wo Sie suchen sollen. (Und es gäbe noch mehr Möglichkeiten, wenn wir den Stream variieren würden.)

Normales PCG hat tatsächlich keine Chance, ein Shakespeare-Stück auszugeben. Das erweiterte PCG-Generierungsschema kann tatsächlich ein Shakespeare-Spiel ausgeben, aber die Wahrscheinlichkeit, dass dies jemals in einem nicht erfundenen Szenario geschieht, ist so infinitesimal, dass es im Wesentlichen auch Null ist. In meinen Augen gibt es sehr wenig Wert in einer Immobilie, die keinerlei praktische Konsequenzen hat.

(Außerdem ist nicht garantiert, dass kryptografisch sichere PRNGs k-dimensional gleichverteilt sind, und sie sind auch kein Wundermittel für Leute, die PRNGs wollen, die jede mögliche Sequenz generieren können. In dem Moment, in dem Sie mehr Bits aus einem PRNG herausholen möchten, als es als solches aufnimmt Seed und speichert als Zustand, es gibt notwendigerweise einige Bitsequenzen, die nicht erzeugt werden können (Beweis: nach dem Pigeon-Hole-Prinzip). Und wenn Sie sich auf die gleiche Menge an Ausgabe beschränken, die Sie als Seed eingegeben haben, was Sie ' Wenn Sie wirklich nach einer Hash-Funktion suchen, oder vielleicht nur nach der Identitätsfunktion, wenn Ihre Seed-Eingabe wirklich zufällig ist, nicht nach einer PRNG.)

Aus Neugier habe ich einen AES-Zählerbitgenerator mit aesctr.h , Zeit in ns pro Zufallswert, eingepackt:

+---------------------+--------------+----------+--------------+
|                     |   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  |
+---------------------+--------------+----------+--------------+

Gute Arbeit, @bashtage.

Einige Dinge zu beachten ist, dass die spezifischen AES-Anweisungen je nach Architektur variieren können und nicht in allen aktiv verwendeten CPUs vorhanden sind. Daher muss ein (langsamer) Fallback-Pfad vorhanden sein.

Außerdem ist es ein bisschen ein Vergleich zwischen Äpfeln und Orangen. Zusätzlich zur Verwendung spezieller Anweisungen erhält der AES-Code einen Teil seiner Geschwindigkeit durch das Abrollen der Schleife - er generiert tatsächlich Zahlen in Blöcken und liest sie dann aus. Das Abrollen kann möglicherweise jede PRNG beschleunigen. FWIW, @lemire hat tatsächlich eine vektorisierte Version von PCG , die AVX-Anweisungen verwendet, um mehrere Ausgaben gleichzeitig zu generieren.

Lassen Sie mich sehen, ob ich mindestens einen Konsenspunkt zusammenfassen kann: Wir sind uns alle einig, dass Numpy eine Meinung darüber abgeben sollte, welcher BitGenerator -Algorithmus verwendet werden soll, und einen BitGenerator gegenüber anderen zu fördern.

Gestatten Sie mir noch einen Versuch, eine Option "Kein Standard" zu skizzieren, die diesem Konsens entspricht und einige der Probleme vermeidet, die einige der anderen Optionen möglicherweise haben. Wenn es keine Traktion bekommt, werde ich die Klappe halten.

Was ich wirklich mit der Option "Kein Standard" gemeint habe, war "Kein anonymer Standard". Es gibt immer noch Möglichkeiten, die API so zu gestalten, dass der bequemste Weg, ein gesetztes Generator besteht, das von uns nominierte PRNG zu benennen. Nehmen wir zum Beispiel an, wir schließen nicht alle Algorithmen von BitGenerator . Wir versuchen, die Anzahl ziemlich gering zu halten und den Vervollständigungismus scipy und anderen Bibliotheken von Drittanbietern im Allgemeinen zu überlassen, und es kann eine gute Idee sein, dies hier zu tun. Das Schöne an der aktuellen Architektur ist, dass wir diese BitGenerator in andere Bibliotheken verschieben können. Nehmen wir also an, wir stellen nur MT19937 zur Verfügung, um die alten RandomState und die BitGenerator , die wir bevorzugen. Nehmen wir zum Zwecke der Argumentation an, das sind Xoshiro256 . Lassen Sie uns dafür sorgen, dass der Konstruktor Generator.__init__() BitGenerator . Definieren wir aber auch eine Funktion np.random.xoshiro256_gen(seed) , die Generator(Xoshiro256(seed)) unter der Decke zurückgibt. Wir dokumentieren diese Komfortfunktion als die Art und Weise eine geimpften zu bekommen Generator .

Spulen Sie jetzt ein paar Releases vor. Nehmen wir an, wir haben PCG64 , ThreeFry usw. auf random-tng oder scipy oder ein anderes Paket verschoben, und eines davon wird wegen populär Die zusätzlichen Funktionen oder neuen statistischen Fehler finden Sie in Xoshiro256 . Wir beschließen, die Meinung von numpy darüber, welche BitGenerator Leute verwenden sollen, auf PCG64 zu aktualisieren. Dann fügen wir die Klasse PCG64 BitGenerator und fügen die Funktion np.random.pcg64_gen(seed) . Wir fügen np.random.xoshiro256_gen(seed) eine Verfallswarnung hinzu, um zu sagen, dass dies nicht mehr der bevorzugte Algorithmus ist: Wir empfehlen, dass neuer Code np.random.pcg64_gen(seed) , aber weiterhin den Algorithmus Xoshiro256 ohne verwendet Warnungen, sie sollten explizit Generator(Xoshiro256(seed)) .

Ich denke, dies vermeidet einige der Probleme, die ich mit einer "anonymen" API habe (dh Generator(seed) , Generator(DefaultBitGenerator(seed)) , np.random.default_gen(seed) ). Wir können die nicht mehr bevorzugten Algorithmen auf Dauer unterstützen. Wir müssen unseren "bevorzugten" Konstruktor niemals dazu bringen, etwas anderes zu tun, wenn wir unsere Meinung ändern. Da wir die echten Namen verwenden, um Dinge zu unterscheiden, anstatt Versionsnummern, wissen Sie immer, wie Sie den Code aktualisieren, um alte Ergebnisse zu reproduzieren (wenn Sie die harmlosen Warnungen aus irgendeinem Grund nicht ertragen können). Sie können sogar Code ohne Herkunft oder aufgezeichnete numpy-Versionsnummern abholen und dieses Update durchführen. Gleichzeitig können wir die Meinung von numpy effektiv zum Ausdruck bringen, indem wir die Anzahl der Algorithmen auf ein absolutes Minimum beschränken und die Best Practice zur einfachsten Art der Arbeit mit der Bibliothek machen.

Wie klingt das? Wir sollten uns noch viel Mühe geben, um eine solide Wahl für diese BitGenerator für die erste Veröffentlichung zu treffen. Es ist immer noch folgerichtig.

Es ist unwahrscheinlich, dass es viele Windows 32-Bit-Benutzer gibt, denen die Leistung auf dem neuesten Stand ist. Der Wechsel zu 64-Bit ist nicht sehr aufwendig.

Genau. Da das Problem mit PCG64 unter Win32 einfach die Leistung ist (und eines, das wir wahrscheinlich mit etwas Aufwand verbessern können), würden Sie zustimmen, dass es kein Blocker ist?

Wenn MSVC den ror-Befehl generieren soll, muss man meiner Meinung nach den intrinsischen "_rotr64" verwenden.

Man könnte auch das '/ O2'-Flag für die Optimierung bevorzugen.

Vielen Dank! @imneme wies mich auf all diese Fehler hin. :-)

@seberg Hast du irgendwelche Zitate aus deinem Bereich, die zu deiner Vorsicht geführt haben? ZB eine Arbeit, die zeigte, dass die Gleichverteilungseigenschaft k = 623 von MT19937 Probleme in nichtlinearen Dynamiksimulationen behebt, die ein kleineres PRNG verursacht? Ich könnte in der Lage sein, dies als Referenz genauer zu versichern. Im Allgemeinen ist meine Ansicht zur Gleichverteilung, dass Sie im Allgemeinen möchten, dass die Gleichverteilung des PRNG nahe an dem Maximum liegt, das durch die Zustandsgröße des PRNG zulässig ist. In _practice_ habe ich nie viel Grund gesehen, mir Sorgen zu machen, wenn Ihr PRNG in anderer Hinsicht groß genug für Ihre Zwecke ist (besteht PractRand, hat einen Zeitraum, der größer ist als das Quadrat der Anzahl der Proben, die Sie zeichnen möchten usw.) präzise k . Andere haben möglicherweise andere Meinungen, und vielleicht gibt es bestimmte Probleme in Ihrem Bereich, die mir nicht bekannt sind. Wenn dies der Fall ist, stehen spezielle Lösungen zur Verfügung!

Aus Neugier habe ich einen AES-Zählerbitgenerator mit aesctr.h eingepackt

Ich könnte mich irren, aber ich glaube nicht, dass dies bei @pbstarks Bedenken helfen würde. AES-CTR ist ein CS-RNG, aber nicht alle CS-RNGs haben die großen Zeiträume, die man (theoretisch) benötigen würde, um alle möglichen k! Permutationen für beträchtliche k . Der Zähler ist immer noch eine 128-Bit-Zahl, und sobald er überrollt ist, haben Sie das Ende des Zeitraums erreicht. @pbstark plädiert für PRNGs mit sehr großem Zeitraum, von denen die meisten zufällig CS-RNGs sind.

Im Allgemeinen ist meine Ansicht zur Gleichverteilung, dass Sie im Allgemeinen möchten, dass die Gleichverteilung des PRNG nahe an dem Maximum liegt, das die Staatsgröße des PRNG zulässt.

Obwohl einige die maximale Gleichverteilung als wünschenswerte Eigenschaft betrachten, kann sie auch als Fehler angesehen werden (und es gibt Papiere, die dies aussagen). Wenn wir ein _k_-Bit-PRNG haben und jede _k_-Bit-Sequenz genau einmal auftritt, verstößt dies gegen das Geburtstagsproblem, das besagt, dass wir erwarten würden, dass sich eine Ausgabe nach etwa 2 ^ (k / 2) Ausgaben wiederholt. ( Ich habe einen statistischen Test für Geburtstagsprobleme geschrieben, der auf diesen Ideen basiert . Er hat das statistisch unplausible Fehlen von Wiederholungen in SplitMix, einem 64-Bit-Ausgabe-64-Bit-Status-PRNG, und Xoroshiro64 +, 32-Bit-Ausgabe-64-Bit, korrekt erkannt -Status 2-dimensional gleichverteiltes PRNG, unter anderem.)

Interessanterweise ist es, obwohl es sehr praktisch ist, einen statistischen Test zu schreiben, bei dem ein PRNG mangels 64-Bit-Wiederholungen fehlschlägt (oder zu viele Wiederholungen - wir erwarten eine Poisson-Verteilung), umgekehrt nicht praktisch, einen Test zu schreiben, der dies tut Erkennen Sie das Auslassen von 36,8% aller 64-Bit-Werte, wenn wir nicht wissen, welche weggelassen werden.

Offensichtlich beginnt das Testen auf den Fehler, dass keine Wiederholungen erwartet werden, unpraktisch zu werden, wenn _k_ größer wird, aber wenn wir zu einer immer größeren Zustandsgröße (und Periode) kommen, bedeutet die hinzugefügte Größe, dass es beide unpraktisch ist, dies zu zeigen Ein maximal gleichverteiltes PRNG ist fehlerhaft, weil es nicht wiederholt werden kann, und ebenso unpraktisch, um zu zeigen, dass ein nicht maximal gleichverteiltes PRNG fehlerhaft ist, wenn es einige _k_-Bit-Sequenzen (auf statistisch plausible Weise) wiederholt und andere vollständig weglässt. In beiden Fällen ist der PRNG zu groß, als dass wir die beiden unterscheiden könnten.

Ich würde es auch gerne sehen. Wir wissen - auf der Grundlage der Mathematik -, dass es viele große Probleme geben muss, aber wir können noch nicht auf ein Beispiel verweisen. Das Vorsorgeprinzip besagt, dass wir, da wir wissen, dass es große Probleme gibt und wir wissen, wie wir sie verhindern können (CS-PRNGs), dies genauso gut standardmäßig tun und die Benutzer weniger vorsichtig sein lassen können, wenn sie dies wünschen.

Ich muss sagen, dass mich diese Argumentation und diese Beweise nicht überzeugen. Als solches würde ich mich nicht wohl fühlen, wenn ich vor numpy Benutzern stehe und ihnen sage, dass sie aus diesem Grund wechseln sollen. Ich bin nicht in der Lage, diese Aussage zu verteidigen, weshalb ich nach diesen Beispielen frage. Diese wären überzeugend und würden mich darauf vorbereiten, diese Position zu verteidigen, die Sie uns empfehlen.

Es gibt viele Eigenschaften, durch die endliche PRNGs nicht den tatsächlichen RNGs entsprechen. Und es gibt viele Berechnungen, die wir theoretisch durchführen möchten, die von den Eigenschaften echter RNGs abhängen (oder zumindest haben wir nicht genau bewiesen, wie sehr wir sie entspannen können). Viele dieser Mängel haben jedoch nur einen sehr geringen, praktisch nicht wahrnehmbaren Einfluss auf die Ergebnisse der tatsächlichen Berechnungen, die wir durchführen. Diese Verstöße sind nicht dispositiv, alles oder nichts, gehen / gehen nicht. Sie haben eine Effektgröße und wir können mehr oder weniger des Effekts tolerieren.

Sie zeigen natürlich überzeugend, dass PRNGs bestimmter Größe nicht alle k! Permutationen für einige k gleichmäßig erzeugen können. Der Schritt, den ich vermisse, ist, wie sich das Versagen, all diese Permutationen erzeugen zu können, auf eine konkrete Berechnung auswirkt, an deren Durchführung ich interessiert wäre. Ich vermisse den Test, den Sie empfehlen würden, zu PractRand oder TestU01 hinzuzufügen, um das Problem den Leuten zu demonstrieren.

Eine der Analyselinien , die ich aus dem PCG-Papier von @imneme als sehr informativ

Wenn andererseits mehr Daten aus dem PRNG benötigt werden, um einen Fehler basierend auf diesen Permutationen anzuzeigen, als der Fehlerpunkt der kleinen PRNGs für die aktuelle Testsuite in PractRand, würde ich zu dem Schluss kommen, dass dieses Problem nicht praktikabel ist Bedenken, und wir könnten "Pässe PractRand" als guten Proxy dafür verwenden, ob das Permutationsproblem einen praktischen Einfluss auf meine Berechnungen haben würde oder nicht.

Aber bis ich ein Programm in der Hand habe, das ich ausführen und den Leuten das Problem zeigen kann, ist es mir unangenehm, auf dieser Basis auf ein CS-PRNG zu drängen. Ich könnte die Wahl nicht überzeugend erklären.

Für diejenigen, die verlangen, dass beim Mischen von 32 Artikeln alle 32! Shuffles (dh alle 263130836933693530167218012160000000) sollten generierbar sein und kein PRNG, das lediglich eine Zufallsstichprobe aus diesen 32 liefert! shuffles, ich würde eigentlich sagen, wenn du große Zahlen fordern willst, denkst du einfach nicht groß genug .

Ich würde daher (scherzhaft) behaupten, dass die Reihenfolge, in der diese Mischvorgänge herauskommen, auch nicht vorbestimmt sein sollte! Natürlich sollten Sie verlangen, dass alle 32 ausgegeben werden! mischt in allen möglichen Reihenfolgen - (32!)! ist was du brauchst! Dies erfordert natürlich 3,8 × 10 ^ 18 Exabyte für den Zustand und eine ähnliche Menge an Entropie zum Initialisieren, aber es lohnt sich sicherlich zu wissen, dass alles da ist.

... Wir fügen np.random.xoshiro256_gen(seed) eine Verfallswarnung hinzu, um zu sagen, dass dies nicht mehr der bevorzugte Algorithmus ist: Wir empfehlen, dass neuer Code np.random.pcg64_gen(seed) , aber den Xoshiro256-Algorithmus weiterhin ohne Warnungen verwendet. Sie sollten explizit Generator(Xoshiro256(seed))

Das stört die Benutzer immer noch sowohl mit einer Abwertung als auch mit seltsam klingenden Namen, von denen sie wirklich nichts wissen wollen.

Der NEP sagt: Zweitens ist es mit Vorsicht zulässig, die Stream-Kompatibilität zu unterbrechen, um neue Funktionen einzuführen oder die Leistung zu verbessern.

Wenn es einen guten Grund gibt, unsere Standardeinstellungen zu aktualisieren, ist es imho die bessere Option (und eine, für die Sie zuvor argumentiert haben), dies nur zu tun und die Bit-für-Bit-Reproduzierbarkeit für Benutzer zu unterbrechen, die keinen Algorithmus explizit angegeben haben.

Was ich wirklich mit der Option "Kein Standard" gemeint habe, war "Kein anonymer Standard".

Wollen Sie also, dass Benutzer den Namen des von ihnen verwendeten PRNG kennen?

Betrachten Sie dies aus Anwendersicht. Es ist schwer genug, sie dazu zu bringen, von np.random.rand & co auf np.random.RandomState() und dann Methoden anzuwenden. Jetzt werden wir ein besseres System einführen, und was sie sehen können, ist np.random.xoshiro256_gen() ? Das wäre eine große Regression in Bezug auf die Benutzerfreundlichkeit.

Wollen Sie also, dass Benutzer den Namen des von ihnen verwendeten PRNG kennen?

Nein, es soll die Probleme abmildern, eine "Designated Moving Target" -API wie default_generator(seed) , an der die Leute gearbeitet haben (z. B. @shoyers Argument version ).

Die Aufrechterhaltung der Stream-Kompatibilität (die von NEP 19 abgelehnt wird) ist sekundär zum API-Bruch. Unterschiedliche BitGenerator haben unterschiedliche effektive APIs, abhängig von ihren Funktionssätzen (einstellbare Streams, hauptsächlich Jumpahead, obwohl es andere geben kann, abhängig davon, wie parametrierbar das PRNG ist). Einige Änderungen in unserer Standard-PRNG-Auswahl würden also tatsächlich den Code beschädigen (dh nicht mehr oder nicht mehr korrekt ausgeführt werden) und nicht nur die herauskommenden Werte ändern.

Nehmen wir zum Beispiel an, wir wählen zuerst PCG64 . Es hat einen 128-Bit-Status, 2 ^ 127 einstellbare Streams und implementiert Jumpahead. nett und voll ausgestattet. Also fangen die Leute an, default_generator(seed, stream=whatever) schreiben. Nehmen wir nun an, die zukünftige Arbeit weist einen großen statistischen Fehler auf, der uns dazu veranlasst, zu etwas anderem zu wechseln. Das nächste PRNG, das wir als Standard bewerben, muss einen> = 128-Bit-Status haben (einfach; ich würde nichts Kleineres als allgemeinen Standard empfehlen), Jumpahead (schwer!),> = 2 ^ 127 einstellbare Streams (whoo, Junge!), um die Verwendungen von default_generator() , die bereits im Code vorhanden sind, nicht zu unterbrechen. Jetzt können wir vielleicht mit dieser Ratsche leben.

@shoyer schlug vor, dass wir die Standardeinstellung BitGenerator möglicherweise immer absichtlich auf die Funktionen mit dem kleinsten gemeinsamen Nenner beschränken könnten. Das würde funktionieren! Es würde aber auch die Gelegenheit verpassen, einstellbare Streams zu fördern, um das Problem der parallelen Streams zu lösen, wie es @charris gerne tun würde.

Jetzt werden wir ein besseres System einführen, und was sie sehen können, ist np.random.xoshiro256_gen() ? Das wäre eine große Regression in Bezug auf die Benutzerfreundlichkeit.

Wenn der seltsam klingende Name das Problem ist, würde ich gerne einen freundlicheren, generischen Namen verwenden, solange die Richtlinie ansonsten dieselbe ist (wir fügen eine neue Funktion hinzu und beginnen mit der Warnung vor der alten). Ich würde das als gleichwertig betrachten. Wir sollten das nicht zu oft machen.

Mir geht es auch gut, wenn wir uns entscheiden, mit der Ratsche zu leben und einen version -Mechanismus zu vermeiden.

Ich gehe von einem "Standard" aus, dass wir ihn als Implementierungsdetail belassen könnten, damit Generator() immer funktioniert. Ich würde dies mit großer Vorsicht verhandeln, dass der einzige Weg, um immer reproduzierbare Ergebnisse zu erzielen (bis zu Änderungen im Generator), die Verwendung der Syntax Generator(BitGenerator(kwarg1=1,kwargs2=b,...))

Es ist nicht praktisch, Implementierungsdetails wirklich zu verbergen, da der Zugriff auf den Status erforderlich ist.

Die Alternative besteht darin, sie wie jede andere Funktion - und generell die Zufallsgenerierung - zu behandeln und einen Standard-Abschreibungszyklus zu durchlaufen, falls ein zwingender Änderungsbedarf besteht. Dies wird niemals Benutzer betreffen, die die Dinge richtig machen, und mit genügend Warnung in den Dokumenten könnte es möglich sein, zumindest bei großen Projekten eine angemessene Trefferquote zu erzielen. Was ich hier vorschlage, ist, dass man vergessen könnte, dass die Stream-Kompatibilitätsgarantie jemals gegeben wurde, wenn man über die neue API nachdachte.

@bashtage in # 13650 Ich habe den Zugriff auf Generator().bit_generator so verweigert, dass das Beizen ohne direkten Zugriff auf state . Es übergibt die leicht umgeschriebenen test_pickle auf eine Weise, die die Verwendung über Python Thread s hinweg ermöglicht

Meine Frage ist - was ist sicher? Es gibt nur unterschiedliche Grade der Quasi-Zufälligkeit mit verschiedenen Eigenschaften. Bisher habe ich noch niemanden gesehen, der ein konkretes Beispiel gegeben hat, auch hier nicht in anderen Ausgaben, PRs oder Threads.

" Besteht PractRand bei _N_ GiB" für einige _N_ (512, 1024) ist eine passable Definition, wenn Sie ein klares Pass / Fail-Kriterium für helle Linien wünschen. Wenn Sie ein konkretes Beispiel wünschen, werden MT und seine Varianten aufgrund dieses Kriteriums ausgeschlossen. Aus diesem Grund haben wir auch einige ältere Mitglieder der Xoroshiro-Familie aus der PR entfernt.

Wenn Sie eine differenziertere Ansicht der statistischen Qualität wünschen, die eine ordinale Rangfolge von Algorithmen ermöglicht, empfehle ich Ihnen Abschnitt 3 des PCG- Dokuments von BitGenerator PractRand besteht, seine 120-Bit-Version jedoch fehlschlägt.

@ Mattip Das scheint vernünftig. Obwohl jemand irgendwo wird

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

Wenn sie so tief eintauchen wollen, ist das in Ordnung. Ich möchte nur dem nicht erfahrenen Benutzer helfen

Es übergibt die leicht umgeschriebenen test_pickle auf eine Weise, die die Verwendung über Python Thread s hinweg ermöglicht

Es ist erwähnenswert, dass dies ein ausstehendes Problem ist (# 9650) - idealerweise würde Generator() in untergeordneten Threads neu gesetzt. IIRC ist dies nur in Python> = 3.7 praktikabel

Ich gehe von einem "Standard" aus, dass wir ihn als Implementierungsdetail belassen könnten, damit Generator() immer funktioniert. Ich würde dies mit großer Vorsicht verhandeln, dass der einzige Weg, um immer reproduzierbare Ergebnisse zu erzielen (bis zu Änderungen im Generator), die Verwendung der Syntax Generator(BitGenerator(kwarg1=1,kwargs2=b,...))

Es gibt zwei Arten der Reproduzierbarkeit, die wir unterscheiden müssen. Zum einen führe ich mein Programm zweimal mit demselben Startwert aus und erhalte dieselben Ergebnisse. Das müssen wir unterstützen. Das andere ist die Reproduzierbarkeit über Versionen von Numpy, die wir zumindest im strengsten Sinne abgelehnt haben.

Argumentloses Generator() , dh "gib mir ein willkürlich gesetztes PRNG, das numpy empfiehlt", ist nicht der primäre Anwendungsfall. Es erfordert nicht viel Unterstützung. "Gib mir das PRNG, das numpy mit _this_ seed empfiehlt" ist und ist das, wofür wir Optionen diskutieren. Wir brauchen eine Möglichkeit für Numpy, eine Meinung darüber zu äußern, wie man ein gesätes PRNG erhält, und diese Möglichkeit muss für Benutzer einfach und bequem sein (sonst werden sie es nicht verwenden). Ich mag es, den Algorithmus zu benennen (wenn auch durch eine bequemere Funktion), aber @rgommers hält das für einen Schritt zu weit, und ich bin damit

Argumentloser Generator (), dh "gib mir einen willkürlich gesetzten PRNG, den numpy empfiehlt", ist nicht der primäre Anwendungsfall. Es erfordert nicht viel Unterstützung. "Gib mir das PRNG, das Numpy mit diesem Samen empfiehlt" ist und ist das, worüber wir Optionen diskutieren. Wir brauchen eine Möglichkeit für Numpy, eine Meinung darüber zu äußern, wie man ein gesätes PRNG erhält, und diese Möglichkeit muss für Benutzer einfach und bequem sein (sonst werden sie es nicht verwenden). Ich mag es, den Algorithmus zu benennen (wenn auch durch eine bequemere Funktion), aber @rgommers hält das für einen Schritt zu weit, und ich bin damit

Ich würde argumentieren, dass Benutzer tatsächlich schlecht gerüstet sind, um gutes Saatgut bereitzustellen. Wie viele Benutzer kennen beispielsweise den richtigen Weg, um den Mersenne Twister zu säen? Es ist nicht so einfach, wie Sie denken - wenn Sie nicht 624 zufällige 32-Bit-Ganzzahlen eingeben (um 19937-Bit-Status bereitzustellen), machen Sie es falsch.

Eigentlich würde ich sagen, dass der richtige Weg für den Benutzer, um reproduzierbare Ergebnisse zu erzielen, darin besteht, das PRNG zu erstellen (ohne einen Samen bereitzustellen, damit dieser automatisch gut ausgesät wird) und ihn dann einzulegen.

Wenn es in der Diskussion nur um den richtigen Weg geht, bin ich dafür
Generator(BitGenerator(**kwargs)) da dies nur von verwendet wird
Semiaware-Benutzer, die sich für die Reproduktion interessieren.

Ich denke, die Standardeinstellung für Generator() wichtig, da dies der Fall sein wird
interpretierte so viele wie eine überlegte Wahl und nehme es so als
Empfehlung bei Verwendung der Samenform.

Nur um noch einen rauszuwerfen, eine Klassenmethode 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 seeded`.

Am Dienstag, 28. Mai 2019, 16:38 schrieb Robert Kern [email protected] :

Ich gehe von einem "Standard" aus, dass wir ihn als Implementierung belassen könnten
Detail, so dass Generator () immer funktionieren würde. Ich würde dies mit einem verhandeln
Vorsicht, der einzige Weg, um immer reproduzierbare Ergebnisse zu erzielen
(bis zu Änderungen im Generator) ist die Verwendung der Syntax
Generator (BitGenerator (kwarg1 = 1, kwargs2 = b, ...))

Es gibt zwei Arten der Reproduzierbarkeit, die wir unterscheiden müssen. Eins ist
dass ich mein Programm zweimal mit demselben Startwert ausführe und dieselben Ergebnisse erhalte.
Das müssen wir unterstützen. Das andere ist die Reproduzierbarkeit über
Versionen von Numpy, die wir zumindest im strengsten Sinne abgelehnt haben.

Argumentloser Generator (), dh "gib mir einen willkürlich gesetzten PRNG, der
numpy empfiehlt "ist nicht der primäre Anwendungsfall. Es erfordert nicht viel
Unterstützung. "Gib mir das PRNG, das Numpy mit diesem Samen empfiehlt" ist,
und dafür diskutieren wir Optionen. Wir brauchen einen Weg für Numpy
eine Meinung darüber äußern, wie man ein gesätes PRNG bekommt, und das muss auch so sein
einfach und bequem für Benutzer (oder sie werden es nicht verwenden). Ich mag
Benennung des Algorithmus (wenn auch durch eine bequemere Funktion), aber
@rgommers https://github.com/rgommers hält das für einen Schritt zu weit und
Ich bin damit einverstanden.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/ABKTSROCMLHG6E6BLWI6TWDPXVGW5ANCNFSM4HPX3CHA
.

Am Dienstag, 28. Mai 2019, 16:38 schrieb Robert Kern [email protected] :

Ich gehe von einem "Standard" aus, dass wir ihn als Implementierung belassen könnten
Detail, so dass Generator () immer funktionieren würde. Ich würde dies mit einem verhandeln
Vorsicht, der einzige Weg, um immer reproduzierbare Ergebnisse zu erzielen
(bis zu Änderungen im Generator) ist die Verwendung der Syntax
Generator (BitGenerator (kwarg1 = 1, kwargs2 = b, ...))

Es gibt zwei Arten der Reproduzierbarkeit, die wir unterscheiden müssen. Eins ist
dass ich mein Programm zweimal mit demselben Startwert ausführe und dieselben Ergebnisse erhalte.
Das müssen wir unterstützen. Das andere ist die Reproduzierbarkeit über
Versionen von Numpy, die wir zumindest im strengsten Sinne abgelehnt haben.

Argumentloser Generator (), dh "gib mir einen willkürlich gesetzten PRNG, der
numpy empfiehlt "ist nicht der primäre Anwendungsfall. Es erfordert nicht viel
Unterstützung. "Gib mir das PRNG, das Numpy mit diesem Samen empfiehlt" ist,
und dafür diskutieren wir Optionen. Wir brauchen einen Weg für Numpy
eine Meinung darüber äußern, wie man ein gesätes PRNG bekommt, und das muss auch so sein
einfach und bequem für Benutzer (oder sie werden es nicht verwenden). Ich mag
Benennung des Algorithmus (wenn auch durch eine bequemere Funktion), aber
@rgommers https://github.com/rgommers hält das für einen Schritt zu weit und
Ich bin damit einverstanden.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/ABKTSROCMLHG6E6BLWI6TWDPXVGW5ANCNFSM4HPX3CHA
.

Aus Sicht der Benutzerfreundlichkeit denke ich, dass wir wirklich Generator(seed) . Andernfalls bleiben Benutzer angesichts der Entscheidung, zu der sie nicht bereit sind, einfach bei RandomState .

Für die Versionierung des Standardbitgenerators in Generator könnten wir bit_version=1 anstelle von version=1 , obwohl ich auch damit einverstanden bin, die Idee version . Ich denke nicht, dass Benutzer Bitgeneratoren sehr oft explizit setzen müssen.

Meine Präferenz für die Lösung bestimmter Anwendungsfälle, die bestimmte Generatorfunktionen erfordern, wäre das Entwerfen neuer, generischer BitGenerator-APIs, die Implementierungsdetails verbergen. Diese können entweder zu DefaultBitGenerator hinzugefügt oder in neue Klassen eingeteilt werden, wenn ihre Verwendung Kompromisse beinhaltet, z. B. ParallelBitGenerator .

Ich möchte auf jeden Fall Warnungen vor zukünftigen Änderungen im RNG-Stream aufgrund der Änderung des Standardbitgenerators vermeiden. Diese Warnungen wären nur ein Rauschen für die große Mehrheit der Benutzer, die sich nicht auf solche Details verlassen, aber im Generator einen seed festlegen, um zu verhindern, dass sich ihre Zufallszahlen spontan ändern.

Benutzer bleiben einfach bei RandomState.

Das ist in Ordnung, sie sind nicht die Early Adopters. Ich dränge hart (vielleicht zu hart?) Auf die minimal mögliche realisierbare API, da wir die API immer erweitern können, es aber viel schwieriger ist, sie zu verkleinern. Die Nuancen zwischen Generator(Philox()) , Generator(seed(3)) und Generator(bit_version=1) sind etwas schwer zu erkennen, bis dies für Endbenutzer herauskommt.

Lassen Sie uns eine erste Version ohne Generator(seed) herausbringen und Feedback erhalten.

Lassen Sie uns eine erste Version ohne Generator(seed) herausbringen und Feedback erhalten.

OK, ich habe hier keine ernsthaften Einwände. In diesem Fall müssen Sie möglicherweise auch vorerst den vollständigen BitGenerator angeben.

Eigentlich würde ich sagen, dass der richtige Weg für den Benutzer, um reproduzierbare Ergebnisse zu erzielen, darin besteht, das PRNG zu erstellen (ohne einen Samen bereitzustellen, damit dieser automatisch gut ausgesät wird) und ihn dann einzulegen.

Ich sage das Gleiche, aber ich bekomme sehr wenig Traktion damit. Wie Sie sagen: "Nun, nur weil etwas eine schlechte Idee ist, heißt das nicht, dass die Leute es nicht wollen!"

Ein Teil des Problems wird durch den riesigen Zustand von MT verschärft, der wirklich eine Serialisierung in eine Datei erfordert. Es ist nur schwer, diesen dateibasierten Tanz zur einfachsten verfügbaren API zu machen, sodass Benutzer ihn verwenden möchten. Mit einem Standard-PRNG mit viel kleinerem Status wird es besser. 128 Bit ist die Größe einer UUID, die gerade so klein ist, dass sie hexadezimal gedruckt und kopiert und eingefügt werden kann. Ein gutes Muster könnte also darin bestehen, Ihr Programm so zu schreiben, dass es standardmäßig einen guten Entropiesamen enthält, und dann seinen Status so auszudrucken, dass Sie ihn beim nächsten Ausführen des Programms kopieren und einfügen können.

❯ 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

Ich bin mir nicht sicher, ob ein solches Muster sowohl die Einfachheit als auch die Zukunftssicherheit bietet.

NumPy 1.next

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

NumPy 1.20.x.

`` `Python
Klassengenerator:
def __init __ (bitgen_or_seed = Keine, *, bit_generator = Keine, inc = Keine):
wenn bit_generator nicht None ist oder inc nicht None ist:
warn ('Die Standardeinstellung ändert sich von PCG64 zu AESCtr. Das Schlüsselwort inc'
'Argument ist veraltet und wird in Zukunft auftauchen', FutureWarning)
`` ``

NumPy 1.22

`` `Python
Klassengenerator:
def __init __ (bitgen_or_seed = None, *, bit_generator = 'aesctr', inc = None, counter = 0):
wenn bit_generator == 'pcg64' oder inc nicht None ist:
Ausnahme auslösen ('PCG wird nicht mehr unterstützt und inc wurde entfernt')
`` ``

Ich bin mir nicht sicher, ob ein solches Muster sowohl die Einfachheit als auch die Zukunftssicherheit bietet.

Wie ich oben unter https://github.com/numpy/numpy/issues/13635#issuecomment -496589421 erwähnt habe, wäre dies für die meisten Benutzer überraschend und frustrierend. Ich würde lieber ein explizites BitGenerator-Objekt bereitstellen müssen, als Warnungen auszugeben, wenn Benutzer nicht alle optionalen Argumente festgelegt haben. Dies sollte wirklich ein letzter Ausweg sein, wenn wir feststellen, dass APIs auf eine Weise beschädigt sind, die wir nicht erwartet haben.

Das Problem ist die Übergangszeit. Jeder, der die Standardeinstellungen verwendet, erhält plötzlich Warnungen, ohne dass er während der Übergangszeit umschalten kann. Oder zumindest verschieben Sie sie von einem "Niedrigenergiezustand" von Generator(seed) in einen weniger bequemen "Hochenergiezustand" von Generator(seed, bit_generator='aesctr') . Da das Ziel dieser API darin bestand, einen bequemen "Niedrigenergiezustand" bereitzustellen, haben wir unseren Zweck während dieses Übergangs verfehlt. Wir haben dies einmal mit einer unserer Histogrammfunktionen, IIRC, gemacht, und es war ein Albtraum.

Dies gilt für alle Abwertungen, die versuchen, die Bedeutung der vorhandenen Argumente zu ändern. Abwertungen, die Sie von einer Funktion zur anderen bewegen, sind viel einfacher zu verwalten, und das habe ich befürwortet.

Lassen Sie uns eine erste Version ohne Generator(seed) herausbringen und Feedback erhalten.

Meinen Sie mit "erste Version" eine vollständige Numpy-Veröffentlichung? Oder einfach die PR zusammenführen (was seitdem passiert ist)?

Wenn es sich um eine vollständige Numpy-Veröffentlichung handelt, müssen wir noch einige Dinge bestimmen, z. B. wie viele BitGenerator wir enthalten. Wenn wir die vollständige aktuelle Ergänzung einbeziehen, haben wir einige der Optionen offengelegt.

Abwertungen, die Sie von einer Funktion zur anderen bewegen, sind viel einfacher zu verwalten, und das habe ich befürwortet.

+1 stimmte zu

Nein, es soll die Probleme abmildern, eine "Designated Moving Target" -API wie default_generator (Startwert) zu haben, an der die Leute gearbeitet haben (z. B. das Versionsargument von @shoyer ).

Die Aufrechterhaltung der Stream-Kompatibilität (die von NEP 19 abgelehnt wird) ist sekundär zum API-Bruch. Unterschiedliche BitGeneratoren haben unterschiedliche effektive APIs

Ah okay, jetzt macht es für mich mehr Sinn.

Wenn der seltsam klingende Name das Problem ist, würde ich gerne einen freundlicheren, generischen Namen verwenden, solange die Richtlinie ansonsten dieselbe ist (wir fügen eine neue Funktion hinzu und beginnen mit der Warnung vor der alten). Ich würde das als gleichwertig betrachten. Wir sollten das nicht zu oft machen.

Das klingt nach der bisher besten Lösung. Es sollte möglich sein, hier einen vernünftigen Namen zu wählen. Und es gibt eine große Anzahl anderer vernünftiger Namen - die wir wahrscheinlich nie brauchen werden.

So etwas wie np.random.generator oder np.random.default_generator .

Wie viele BitGeneratoren enthalten wir?

Könnten Sie ein separates Problem mit einem Vorschlag eröffnen, diejenigen zu löschen, von denen Sie glauben, dass wir sie aus dem derzeit enthaltenen entfernen sollten (MT19937, DSFMT, PCG32, PCG64, Philox, ThreeFry, Xoshiro256, Xoshiro512)?

Wir haben das hier vorliegende Problem immer noch nicht gelöst: Welcher BitGenerator sollte Standard sein (derzeit Xoshiro256 )

Nun, in dieser Ausgabe geht es mehr um "welche man als angesehenes BitGenerator numpy bewerben sollte", was in die Auswahl der Standardeinstellungen einfließt, aber auch welche eingeschlossen oder gelöscht werden sollen. Die Mechanik, mit der wir Standardeinstellungen bereitstellen (wenn wir Standardeinstellungen bereitstellen), fügt einige Einschränkungen hinzu. Dies sind also alles Dinge, die mehr oder weniger gemeinsam entschieden werden müssen. Es ist ein großes, haariges Durcheinander, und nach all der Arbeit, die Sie geleistet haben, um diese PR durchzuarbeiten, bin ich sicher, dass Sie erschöpft sind, einen weiteren Mega-Thread zu sehen, in dem niemand Code beisteuert, also haben Sie mein Mitgefühl. :-)

Was die Algorithmen an sich angeht, habe ich bereits meine Empfehlungen gegeben: Wir müssen MT19937 für RandomState und Vergleichszwecke behalten, und ich mag PCG64 für Empfehlungszwecke.

Ich habe ein bisschen im Compiler Explorer herumgespielt und ich glaube, ich habe PCG64 für 64-Bit-MSVC mithilfe von Intrinsics so implementiert, dass der Compiler gezwungen ist, Assemblys in der Nähe von Clangs uint128_t math: https: // godbolt zu generieren .org / z / ZnPd7Z

Ich habe momentan keine Windows-Entwicklungsumgebung eingerichtet, daher weiß ich nicht, ob es tatsächlich _korrekt _... @bashtage ist. Würde es Ihnen etwas ausmachen, dies zu

Ohne Patch:

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

Mit Patch:

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

Der Patch besteht die Tests, einschließlich der Generierung des gleichen Satzes von 1000 uint64-Werten für 2 verschiedene Samen.

Zum Vergleich gegen GCC native und im Vergleichsmodus:

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

Wow, das scheint wirklich schlimm zu sein. Vielleicht sollten wir die Informationen auf der Vergleichsseite erweitern , um die Leistung von msvc2017-on-win {64, 32} gegenüber gcc7.4-on-linux {64, 32} auf demselben Computer zu demonstrieren (ich gehe nur davon aus, dass Sie msvc2017 verwenden). Sie sollten diese Informationen wahrscheinlich irgendwo einfügen.

Win32 ist hier hoffnungslos. Ich vermute, dass Linux 32-Bit auch ziemlich schrecklich sein wird, aber es gibt kein 32-Bit-Linux-System, auf dem man einfach testen kann.

Ich sehe definitiv den Grund, eine Empfehlung für Leute abzugeben, die auf einem 32-Bit-Computer arbeiten (höchstwahrscheinlich Windows aufgrund von IT-Richtlinien des Unternehmens). Dieser Recco ist klar: DSFMT für 32 Bit (oder MT19937 ist auch gut). Benchmarks wären allerdings gut.

Für das, was es wert ist, bin ich ziemlich skeptisch gegenüber der oft wiederholten PCG-Behauptung mehrerer unabhängiger Zufallsströme. Hat jemand ernsthafte statistische Analysen durchgeführt, um den Anspruch auf Unabhängigkeit zu belegen? (Eigentlich denke ich, dass sich O'Neills Artikel nur auf "unterschiedliche" Ströme bezieht, ohne Anspruch auf Unabhängigkeit.)

Ich denke, es gibt gute Gründe, skeptisch zu sein: Für einen bestimmten LCG-Multiplikator werden all diese unterschiedlichen Streams einfach über die Skalierung [*] in Beziehung gesetzt. Bei zwei beliebigen LCG-Streams mit demselben Multiplikator ist einer davon einfach ein konstantes Vielfaches (Modulo 2**64 oder 2**32 nach Bedarf) des anderen, jedoch mit unterschiedlichen Startpunkten. Der Permutationsteil der PCG wird dazu beitragen, dies ein wenig zu verbergen, aber es wäre wirklich nicht überraschend, wenn es statistisch nachweisbare Korrelationen gäbe.

Also unterschiedliche Streams, unabhängiger Streams ohne ernsthafte Tests nicht zum Nennwert annehmen.

[*] Beispiel: Angenommen, x[0], x[1], x[2], ... ist ein Standard-64-Bit-LCG-Stream mit x[i+1] := (m*x[i] + a) % 2**64 . Stellen Sie y[i] := 3*x[i] % 2**64 für alle i . Dann ist y[i] ein LCG-Stream mit y[i+1] := (m*y[i] + 3*a) % 2**64 Wenn Sie also einfach den ursprünglichen Stream skalieren, haben Sie einen dieser unterschiedlichen LCG-Streams mit demselben Multiplikator, aber unterschiedlicher additiver Konstante erstellt. Wenn Sie andere ungerade Multiplikatoren anstelle von 3 und davon ausgehen, dass wir nur an LCGs für die gesamte Periode interessiert sind (und daher a ungerade ist), erhalten Sie alle möglichen vollen Multiplikatoren. Perioden-LCGs mit diesem Multiplikator.


BEARBEITEN: Falsche Aussage über die Anzahl der Konjugationsklassen behoben.

Ich denke, die gründlichste öffentliche Analyse von PCG-Streams finden Sie hier: http://www.pcg-random.org/posts/critiquing-pcg-streams.html

@imneme Kannst du deinen letzten Rat erweitern? "Die Korrespondenz mit David Blackman zeigt, dass es möglicherweise einfacher ist, als ich gedacht hatte," nahe gelegene "Streams mit korrelierten Initialisierungen wie einem konstanten Startwert und Streams von 1,2,3,4 zu erstellen. Wenn Sie mehrere Streams bei verwenden Gleichzeitig empfehle ich vorerst, dass die Stream-ID und der Startwert unterschiedlich sind und keine offensichtlichen Korrelationen miteinander aufweisen, was bedeutet, dass sie nicht beide 1,2,3,4 sind. "

Bedeutet dies, dass Sie denken, dass es in Ordnung ist, einen einzigen guten Samen (z. B. aus einer Entropiequelle) zu haben und dann die IDs 1,2,3,4 zu streamen? Oder sollten sowohl die Seed- als auch die Stream-ID zufällig aus einer guten Entropiequelle ausgewählt werden?

Einige Linux-32-Nummern (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

Es ist also doppelt so schnell wie Win-32, aber langsam. Alle 4 Timings wurden auf derselben Maschine durchgeführt

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 ist das NumPy 1.16.4. DSFMT ist der einzige Geneator mit guter Leistung auf 32-Bit (x86). Dies sollte für alle 32-Bit-Benutzer klar dokumentiert werden. MT19937 ist auch eine relativ gute Wahl für einen 32-Bit-Benutzer.

Wir brauchen also MT19937 für Legacy-Zwecke. Wenn wir nur minimal darüber sein möchten, welche PRNGs wir einschließen (dh MT19937 plus unsere einzige allgemeine Empfehlung), würde ich mich nicht gezwungen fühlen, die 32-Bit-Leistung zu verwenden, um unsere einzige allgemeine Empfehlung einzuschränken fühlen sich gezwungen, ein drittes "für 32-Bit empfohlenes" PRNG hinzuzufügen. MT19937 wird immer verfügbar sein und es ist nicht schlimmer als das, was sie derzeit haben. Und Pakete von Drittanbietern werden für die Nischenverwendung verfügbar sein.

Wenn wir aus anderen Gründen einen vollständigeren Satz von PRNGs aufnehmen möchten, können wir natürlich alle möglichen spezifischen Empfehlungen in die Dokumentation aufnehmen.

Ich war neugierig, wie sehr der "P" -Teil von PCG potenzielle Probleme durch korrelierte Streams abschwächt.

Hier ist (vielleicht) der schlechteste Fall für die LCGs: wobei die additive Konstante eines LCG-Stroms die exakte Negation der additiven Konstante für den anderen ist. Dann, mit entsprechend schrecklicher Auswahl an Samen, endet einer der LCG-Ströme als exakte Negation des anderen.

Wenn wir nun beide Streams verwenden, um eine Reihe von Floats zu generieren, sollten uns sowohl der Permutationsteil des PCG als auch die Konvertierung in float64 ein wenig helfen.

Hier ist ein Diagramm, das zeigt, wie viel die Permutation hilft:

streams

Das ist ein Streudiagramm von 10000 Schwimmern aus einem solchen Strom, gegenüber 10000 von seinem negierten Zwilling. Nicht schrecklich, aber auch nicht großartig: Es gibt klare Artefakte.

Ich bin mir nicht sicher, was ich daraus schließen soll: Es ist absolut ein erfundenes Beispiel, dass es unwahrscheinlich ist (ich hoffe), dass Sie versehentlich darauf stoßen. Auf der anderen Seite zeigt es, dass einige Überlegungen und Sorgfalt erforderlich sind, wenn Sie wirklich mehrere unkorrelierte Streams benötigen.

Für die Aufzeichnung ist hier die Quelle:

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 ist der Generator, den O'Neill PCG-XSL-RR nennt (Abschnitt 6.3.3 des PCG-Papiers). Das pcgrandom Paket ist von hier

Ich dachte, der Standardweg, um unabhängige Streams zu erhalten, ist die Verwendung von jumpahead ().
Das erneute Säen, um "unabhängige" Streams zu erhalten, ist im Allgemeinen gefährlich.

Zähler- / Hash-Generatoren haben einen trivialen Jumpahead (). Hat PCG?

Auch ein Plädoyer eines Benutzers: Bitte geben Sie mindestens einen Bitstream an
kryptografische Qualität mit unbegrenztem Zustandsraum.

Prost,
Philip

(EDIT von seberg: E-Mail-Zitat entfernt)

@pbstark : Dies ist nicht nur eine erneute Aussaat: Die beiden zugrunde liegenden LCG-Generatoren sind tatsächlich unterschiedlich: x ↦ mx + a (mod 2 ^ 128) und x ↦ mx + b (mod 2 ^ 128) für verschiedene Inkremente a und b. Das PCG-Papier von O'Neill verkauft die Idee, durch Ändern dieses LCG-Inkrements verschiedene Streams erstellen zu können (siehe Abschnitt 4.3.2 des Papiers).

Die Einfachheit von LCG bedeutet jedoch, dass das Ändern dieser additiven Konstante im ursprünglichen Generator nur einen Sprungkopf um einen unbekannten Betrag ergibt, kombiniert mit einer einfachen linearen Transformation (Multiplikation mit einer Konstante oder in einigen Fällen nur Addition einer Konstante).

Kein Grund, PCG nicht zu verwenden, und ich behaupte keinen Moment, dass es nicht für NumPys neues Haupt-PRNG geeignet ist. Ich möchte einfach nicht, dass die Leute vom Versprechen "unabhängiger" Zufallsströme erfasst werden. Bestenfalls bietet die Idee der einstellbaren Streams für PCG eine bequeme Möglichkeit, etwas zu tun, das einem schnellen Jumpahead entspricht, sowie eine zusätzliche multiplikative oder additive Bonus-Transformation.

Wir haben das kryptografische im Community-Aufruf ein wenig besprochen. Ich denke, wir waren etwas vorsichtig. Es scheint eine gute Idee zu sein, aber wenn wir ein kryptografisch einwandfreies RNG einbinden, müssen wir auch mit den auftretenden Sicherheitsproblemen Schritt halten, da wir nicht wissen, ob Benutzer sie für tatsächliche Kryptografiezwecke verwenden.

In Bezug auf die Anzahl der einzubeziehen: Der Konsens tendierte dazu, ein paar weitere Bitgeneratoren in der Nähe zu haben (einige kleine Dokumentationen wären natürlich gut). Der Wartungsaufwand scheint nicht zu groß. Am Ende denke ich, dass wir mit allem gehen würden, was Kevin und Robert vorschlagen.

In Bezug auf Namen: Es macht mir persönlich nichts aus, die RNG-Namen zu verwenden und Benutzer zu zwingen, sie zu verwenden. Der einzige Nachteil ist, dass wir den Namen möglicherweise beim Codieren nachschlagen müssen. Wir sollten nur versuchen, so wenig Abwertungswarnungen wie möglich zu haben. Ich mag die minimal exponierte API für das Standard-RNG ohne Seeding.

@mdickinson , ich habe versucht, Ihr Diagramm selbst zu reproduzieren und bin gescheitert. Ich habe mit diesem Programm die kanonische C ++ - Version des Codes verwendet, die das moralische Äquivalent von Ihnen sein sollte.

#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";
    }
}

Wenn ich dies ausführe, wird Folgendes ausgegeben (um die Reproduzierbarkeit zu ermöglichen):

RNG1: 47026247687942121848144207491837523525 203756742601991611962280963671468648533 41579532896305845786243518008404876432
RNG2: 47026247687942121848144207491837523525 136525624318946851501093643760299562925 52472962479578397910044896975270170620

und Datenpunkte, die das folgende Diagramm darstellen können:

corr1

Wenn Sie herausfinden können, was ich anders mache, wäre das hilfreich.

(Ich sage dies nicht, um die Idee zu widerlegen, dass Korrelationen möglich sind, weil sie möglich sind, und ich werde einen separaten Kommentar zum Thema schreiben, aber beim Schreiben dieses Kommentars wurde mir klar, dass ich Ihr Ergebnis mit nicht reproduzieren konnte meine üblichen Werkzeuge.)

@mdickinson stanzte im berechneten Zustand und inkrementierte direkt in die Interna, wobei die übliche Initialisierungsroutine umgangen wurde , die zwei schrittweise Fortschritte aufweist.

Ich habe ein schnelles kleines Treiberskript geschrieben , das mehrere PCG32-Streams verschachtelt, die auf verschiedene Arten erstellt wurden, um sie in PractRand einzuspeisen. In Python 3 wird master von numpy verwendet. Wenn ich die gegnerischen internen Zustände / Inkremente direkt eingebe, schlägt PractRand schnell fehl. Es ist mir unklar, ob wir vernünftige Wege finden können, um gegnerische Samen (die tatsächlich die Initialisierungsroutine durchlaufen) zu finden, um die gegnerischen Zustände zu erreichen.

Wie in meinem Blog-Beitrag erwähnt, haben die Streams von PCG viel mit denen von SplitMix gemeinsam.

In Bezug auf das Diagramm von @mdickinson können wir für _jedes_ PRNG, mit dem Sie den gesamten Status festlegen können, einschließlich kryptografischer Daten auf Gegenbasis, Seedings erfinden, bei denen PRNGs vorhanden wären, deren Ausgaben auf irgendeine Weise korreliert wurden (der einfachste Weg, dies zu tun) ist es, PRNG-Zustände zu erstellen, die nicht weit voneinander entfernt sind, aber oft können wir andere Dinge tun, basierend auf dem Verständnis, wie sie funktionieren. Und obwohl PRNGs, die kein Seeding in vollem Zustand zulassen, dieses Problem vermeiden können, führt dies lediglich ein neues ein und bietet nur praktischen Zugang zu einem winzigen Bruchteil ihrer möglichen Zustände.

Die richtige Art, an Streams zu denken, ist nur ein zufälliger Zustand, der gesetzt werden muss. Die Verwendung kleiner Werte wie 1,2,3 ist im Allgemeinen eine schlechte Idee für Seeding-Zwecke für _jedes_ PRNG (denn wenn jeder diese Samen bevorzugt, werden die entsprechenden Anfangssequenzen überrepräsentiert sein).

Wir können uns dafür entscheiden, es überhaupt nicht als Stream zu bezeichnen und es einfach als Status zu bezeichnen. Das hat Marsaglia in XorWow getan . Wenn Sie sich den Code ansehen, interagiert die Weyl-Sequenz counter überhaupt nicht mit dem Rest des Zustands und entspricht wie LCGs und Variationen des Anfangswertes nur einer zusätzlichen Konstante.

Die Streams von SplitMix, PCG und XorWow sind sogenannte „dumme“ Streams. Sie stellen eine triviale Neuparametrisierung des Generators dar. Dies hat jedoch einen Wert. Angenommen, ohne Streams hätte unser PRNG eine interessante enge Wiederholung von 42, wobei 42 mehrmals schnell hintereinander auftaucht und dies nur für 42 und keine andere Zahl tut. Mit dummen Streams "nur ein Inkrement" oder "nur ein xor" vermeiden wir tatsächlich, die seltsame Wiederholung auf 42 fest zu verdrahten. Alle Zahlen haben einen Stream, in dem sie sich seltsam wiederholen. (Aus diesem Grund besteht die Lösung, die ich zur Behebung der Probleme mit engen Wiederholungen in Xoshiro 256 anwenden würde, darin, eine Weyl-Sequenz zu mischen.)

Ich bin kein Experte, aber auf der kryptografischen Seite wird vorgeschlagen, was nicht verfügbar ist in:
https://cryptography.io/en/latest/ von der Python Cryptographic Authority ?

Auf ihrer Seite zur Zufallszahlengenerierung wird außerdem Folgendes erwähnt:

Ab Python 3.6 enthält die Standardbibliothek das Geheimnis- Modul, mit dem kryptografisch sichere Zufallszahlen generiert werden können, mit speziellen Hilfsprogrammen für textbasierte Formate.

Ich denke, vielleicht werden der Generation Arrays hinzugefügt. Ich muss mich fragen, ob sich die potenzielle Wartungslast, die mit der krypotgrafischen Robustheit verbunden ist, wirklich lohnt und in NumPy angemessen ist, anstatt beispielsweise mit der Pyca zu kommunizieren und möglicherweise über einen Generator / Plugin eines Drittanbieters nachzudenken. Ich denke, Nathaniel hat zuvor ein ähnliches Problem erwähnt.

In der Tat scheint es mir, dass Dinge wie der potenzielle dtype Refactor / Enhancement auch darauf ausgelegt sind, eine API-Infrastruktur bereitzustellen, ohne die Last der Wartung einer Vielzahl spezialisierter neuer Anwendungen zu übernehmen.

Übrigens geht es in meiner Antwort auf Vignas diesem Abschnitt ] auch mehr darum, korrelierte PRNG-Zustände zu erfinden. Etwas, das ich dort beobachtet habe, ist, dass Sie mit PCG, da es eine Distanzfunktion hat, tatsächlich mit der Distanzfunktion überprüfen können, um erfundene Samen zu erkennen. In PRNGs ohne Distanzfunktion können Menschen immer noch Seeding-Paare erfinden, die eine schlechte Wahl sind (insbesondere wenn sie die öffentliche API für Seeding umgehen), aber es gibt keinen Mechanismus, der selbst die offensichtlichsten Erfindungen erkennen kann.

Einerseits macht es Spaß darüber nachzudenken, ob es möglich ist, Ihr Lieblings- (oder am wenigsten Lieblings-) PRNG zu nehmen und Samen dafür zu erfinden, die es zu amüsanten oder schrecklichen Dingen machen (z. B. diese Pathologie )…

Wenn ich jedoch das Gesamtbild betrachte, halte ich es für sinnvoll, Probleme zu betrachten, mit denen Benutzer in der Praxis konfrontiert sind. Welchen Rat wir ihnen geben usw. Die meisten Benutzer wissen nicht, dass ein 32-Bit-Startwert für alle PRNGs (Vergangenheit und Zukunft) eine absolut schreckliche Idee ist und zu trivial erkennbaren Verzerrungen führt, unabhängig davon, welches PRNG im Spiel ist. Sicher, wir können das abwischen und stattdessen unsere Zeit damit verbringen, uns Gedanken darüber zu machen, ob es jemandem gelungen ist, den Mersenne Twister in einen Zustand mit größtenteils Nullen zu initialisieren (oder in den Zustand mit Nullen, in dem LFSRs überhaupt nicht funktionieren!) Oder ob jemand dies könnte Initialisieren Sie Xoshiro so nahe, dass es innerhalb von elf Ausgängen sieben Mal dieselbe Ausgabe wiederholt, oder erfinden Sie zwei ähnliche PCG-Streams oder was auch immer, aber alle diese Geräte haben im Grunde eine infinitesimale (in der Praxis null) Chance, dass der Generator auftritt wird mit zufälligen Daten ausgesät. So intellektuell engagiert und akademisch interessant diese Ablenkungen auch sind, wenn man über sie nachdenkt und dabei meistens die Tatsache ignoriert, dass Benutzer normalerweise keine Ahnung haben, was sie beim Seeding tun, spielt sie herum, während Rom brennt.

Wenn inc=1,2,3,4 eine schlechte Idee ist, würde das nicht bedeuten, dass sie entweder sehr klar dokumentiert werden sollte oder dass wir vielleicht eine etwas andere API haben sollten? Vielleicht sogar new_generator = (Bit)Generator().independent() , können wir eine Warnung darauf setzen, wenn der (zugrunde liegende) Bitgenerator keine großartige Möglichkeit bietet, dies zu erreichen.

Auch abhängig davon, wie schlecht 32bit Seeding ist. Können wir uns eine nette API zum Erstellen und Speichern eines Samens vorstellen, um ihn einzufrieren? Ich weiß es nicht. Vielleicht sogar eine "Erstelle eine eingefrorene Seed-Cache-Datei, wenn sie nicht existiert".

Denn PCG könnte nur -> uint64_t [2] -> splitmix64 (seed_by_array) -> uint128 aussäen, was sicherstellen würde, dass niedrige, aufeinanderfolgende Samen ausgebreitet werden.

Denn PCG könnte nur -> uint64_t [2] -> splitmix64 (seed_by_array) -> uint128 aussäen, was sicherstellen würde, dass niedrige, aufeinanderfolgende Samen ausgebreitet werden.

Oder verwenden Sie einfach einen guten ganzzahligen Hash. (Es sollte eine Bijektion sein.) Es gibt viele billige und kurze. Ein paar Runden Multiplizieren - XorShift sind in Ordnung.

Ich denke, @mdickinson möchte immer noch ein bisschen davon überzeugen, dass die Stream-Abhängigkeit auf eine kleine Menge von erfundenen / kontroversen Einstellungen beschränkt ist. Und wenn dies der Fall ist, können wir es mit geeigneten Methoden lösen, um solche Fälle zu verhindern. Mit dem aktuellen Code, den wir haben, gibt es einige schlechte Zustände, in die Benutzer mit den aktuellen APIs leicht verfallen können. Ich kann David Blackmans Feststellung bestätigen, dass das Setzen von seed=1 und inc=0,1,2,... Korrelationen erzeugt. Mein neuester PractRand-Treiber für verschachtelte PCG32-Streams kann verwendet werden, um dies zu demonstrieren.

❯ ./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

Ich bin noch nicht auf einen Fehler mit einem zufälligen Startwert gestoßen, aber mit den gleichen engen Schritten. Ich melde mich morgen bei dir.

Ich stelle fest, dass wir das empfohlene Standardinkrement nicht verwenden, wenn im Konstruktor keines angegeben ist. Wir sollten das wahrscheinlich beheben. Vielleicht wäre das eine gute Basiszahl, aus der wir das tatsächliche Inkrement aus der angegebenen Stream-ID anstelle von 2*inc + 1 ableiten.

Wir können versuchen, einige Werkzeuge zu entwickeln, die den Menschen helfen, die Standard-Entropiesamen zu verwenden und sie zu speichern. Eine Frage, die ich habe, ist, ob die Inkremente für mehrere Streams einfach generiert werden können oder ob wir sie auch entropie abtasten und auch speichern müssen. Es ist sehr praktisch, den "Anfangszustand" einer Simulation als einzelne Nummer zu codieren, die aus der E-Mail eines Kollegen und nicht aus einer undurchsichtigen Datei kopiert werden kann. Mit diesen kleineren PRNGs mit nur 128 oder 256 Bit Status kann ich das einfach hexadezimal in meine Protokolldatei ausdrucken und dann einfach kopieren und in meine Befehlszeile einfügen, wenn ich es reproduzieren möchte. Es ist größer als eine 32-Bit-Ganzzahl, aber überschaubar. Wenn ich auch alle meine Stream-IDs mit Entropie abtasten muss, muss ich darauf verzichten und sicherstellen, dass ich alles irgendwo in einer Statusdatei aufzeichne. Dies könnte einige Anwendungsfälle ausschließen, die wir besprochen haben und in denen wir neue Streams dynamisch erzeugen möchten. Wenn ich einen Zähler nur inkrementieren kann, um eine gute Stream-ID zu erhalten (möglicherweise durch einen Hash des Zählers oder was auch immer), muss ich nur den anfänglichen Startwert und nicht die Stream-IDs aufzeichnen.

IIRC, das Geheimmodul, ruft die Entropiequelle des Betriebssystems auf, was durchaus sein kann
In einigen Systemen schlecht und nicht reproduzierbar / reproduzierbar.

Am Mittwoch, den 29. Mai 2019 um 15:19 Uhr Tyler Reddy [email protected]
schrieb:

Ich bin kein Experte, aber auf der Seite der Kryptographie, was vorgeschlagen wird
nicht verfügbar in:
https://cryptography.io/en/latest/ von der Python Cryptographic Authority
https://github.com/pyca ?

Ihre Seite zur Zufallszahlengenerierung
https://cryptography.io/en/latest/random-numbers/ erwähnt auch:

Ab Python 3.6 enthält die Standardbibliothek die Geheimnisse
https://docs.python.org/3/library/secrets.html Modul, das sein kann
wird zur Erzeugung kryptografisch sicherer Zufallszahlen mit spezifischen verwendet
Helfer für textbasierte Formate.

Ich denke, vielleicht werden der Generation Arrays hinzugefügt. Ich muss mich fragen, ob die
potenzielle Wartungsbelastung durch Krypotgraphie
Robustheit ist es wirklich wert und angemessen in NumPy vs.
Kommunikation mit der Pyca und vielleicht über einen Dritten nachdenken
Generator / Plugin dafür. Ich denke, Nathaniel erwähnte ein ähnliches Anliegen
vorher.

In der Tat scheint es mir, dass Dinge wie der potenzielle dtype Refactor /
Verbesserungen wurden auch entwickelt, um API-Infrastruktur ohne bereitzustellen
notwendigerweise die Last der Aufrechterhaltung einer großen Vielfalt von übernehmen
spezialisierte neue Anwendungen.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWKGZMUB67VPCMFZYGTPX36O3ANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

@tylerjereddy Diese dienen dazu, eine kleine Menge zufälliger Bits von einer physischen Entropiequelle zu erhalten, die für einen Angreifer (und Sie!) nicht vorhersehbar sind. Sie werden in der Kryptographie für Dinge wie Initialisierungsvektoren, Nonces und Schlüssel verwendet, die alle kurz sind. Der springende Punkt dabei ist, dass es keine Möglichkeit gibt, sie zu reproduzieren, was im Widerspruch zu den numerischen Simulationszwecken von np.random . Auf dieser Seite geht es nicht um reproduzierbare kryptografisch sichere PRNGs, die ebenfalls vorhanden sind und aus den im Paket cryptography verfügbaren Grundelementen erstellt werden könnten. In _practice_ haben wir jedoch bessere Implementierungen dieser Algorithmen, die uns bereits in effizientem C-Code zur Verfügung stehen, zumindest diejenigen, die für Simulationszwecke formuliert und getestet wurden. @bashtage hat einige für dieses Framework

Ich möchte dem numpy-Team auch klar machen, dass @pbstark nicht irgendein

Die meisten kryptobasierten PRNGs, die üblicherweise für die Simulation in Betracht gezogen werden, haben nicht den unbegrenzten Zustand, den @pbstark wünscht. Sie basieren normalerweise auf der Verschlüsselung eines endlichen Zählers. Sobald dieser Zähler herumrollt, haben Sie die endliche Periode erreicht. Technisch gesehen ist sein cryptorandom aufgrund des 256-Bit-Digest-Status fester Größe und des 64-Bit-Längenzählers fester Größe auch an 2**(256+64) eindeutige Anfangsbedingungen gebunden. Das weist wahrscheinlich den Weg zur Implementierung eines wirklich unbegrenzten PRNG, indem der Längenzähler beliebig dimensioniert wird, aber ich habe noch nie einen solchen Algorithmus veröffentlicht oder getestet gesehen.

Wenn Sie jedoch nur einen PRNG-Algorithmus mit einem beliebig großen Status wünschen, der zu Beginn auf etwas festgelegt ist, das über dem liegt, was Sie benötigen, dann sind die erweiterten Generatoren von PCG für diese Aufgabe gut geeignet. Dies sind eindeutig keine CS-PRNGs, aber sie würden tatsächlich @pbstarks Wunsch erfüllen, auf

Es gibt andere Eigenschaften, die die standardmäßigen, begrenzten CS-PRNGs haben, die wir vielleicht wollen, aber sie sind keine einfache Standardeinstellung, IMO.

Der Zustandsraum von Cryptorandom ist nicht der 256-Bit-Hash, sondern unbegrenzt. Das
Der Startzustand ist eine Zeichenfolge beliebiger Länge, und bei jeder Aktualisierung wird eine Null angehängt
auf den aktuellen Stand. Das Inkrementieren eines unbegrenzten ganzzahligen Zählers würde
das Gleiche erreichen. Wir haben das zunächst implementiert, aber auf geändert
anhängen statt inkrementieren, da es ein effizienteres Update ermöglicht
der Digest als jeden Zustand von Grund auf neu zu hashen (spürbare Beschleunigung).

Am Mittwoch, 29. Mai 2019 um 19:26 Uhr Robert Kern [email protected]
schrieb:

@tylerjereddy https://github.com/tylerjereddy Diese sind für das Erhalten eines
kleine Menge zufälliger Bits von einer physikalischen Entropiequelle, die sind
unvorhersehbar für einen Angreifer (und Sie!). Sie werden in der Kryptographie für verwendet
Dinge wie Initialisierungsvektoren, Nonces, Schlüssel, die alle kurz sind. Das
Der springende Punkt dabei ist, dass es keine Möglichkeit gibt, sie zu reproduzieren
Quoten mit den numerischen Simulationszwecken von np.random. Diese Seite ist
nicht über reproduzierbare kryptografisch sichere PRNGs sprechen, die
sind auch Dinge, die existieren und aus den Primitiven aufgebaut werden könnten
im Kryptografiepaket verfügbar. In der Praxis haben wir jedoch
bessere Implementierungen dieser Algorithmen, die uns bereits in zur Verfügung stehen
effizienter C-Code, zumindest diejenigen, die formuliert und getestet wurden
für Simulationszwecke. @bashtage https://github.com/bashtage implementiert
ein paar https://github.com/numpy/numpy/issues/13635#issuecomment-496287650
für diesen Rahmen.

Ich möchte dem numpy Team auch klar machen, was @pbstark ist
https://github.com/pbstark schlägt vor, nicht nur auf Krypto zu basieren
PRNG. Vielmehr will er einen mit unbegrenztem Zustand , der dafür sorgen würde
die mathematische Eigenschaft, die er sucht.

Die meisten kryptobasierten PRNGs, die üblicherweise für die Simulation in Betracht gezogen werden
habe nicht den unbegrenzten Zustand, dass @pbstark
https://github.com/pbstark will. Sie basieren normalerweise auf
einen endlichen Zähler verschlüsseln. Sobald dieser Zähler herumrollt, haben Sie die getroffen
endliche Periode. Technisch gesehen sein Kryptozufall
https://statlab.github.io/cryptorandom/ ist ebenfalls auf 2 ** (256 + 64) begrenzt.
einzigartige Anfangsbedingungen aufgrund des 256-Bit-Digest-Zustands fester Größe und
64-Bit-Zähler mit fester Größe. Das weist wahrscheinlich den Weg zu
Implementierung eines wirklich unbegrenzten PRNG durch Erstellen des Längenzählers
beliebig groß, aber ich habe noch nie einen solchen Algorithmus veröffentlicht oder gesehen
geprüft.

Auf der anderen Seite, wenn Sie nur einen PRNG-Algorithmus mit einem wollen
beliebig großer Zustand, nur einer, der am Anfang festgelegt ist
etwas über alles, was Sie brauchen, dann die erweiterten Generatoren von PCG
http://www.pcg-random.org/party-tricks.html würde dafür gut funktionieren
Aufgabe. Dies sind eindeutig keine CS-PRNGs, aber sie würden tatsächlich befriedigen
@pbstark https://github.com/pbstarks Wunsch nach riesigen Staatsräumen
auf Nachfrage. Trotzdem empfehle ich nicht, sie in numpy aufzunehmen.

Es gibt andere Eigenschaften, die die standardmäßigen, begrenzten CS-PRNGs haben
Wir möchten vielleicht, aber sie sind kein Kinderspiel, IMO.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWLIJD3UCVY3NXCLPKDPX43ONANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Ich fürchte, so funktioniert das Online-Update von SHA-256 nicht. Der Status, den es beibehält, ist nur der 256-Bit-Digest und der 64-Bit-Längenzähler mit fester Größe, die bei der Berechnung des Updates angehängt werden. Es enthält nicht den gesamten Text. Hashes komprimieren. Auf diese Weise kann das effiziente Update für jedes Byte durchgeführt werden. Grundsätzlich gibt es viele Initialisierungen / vergangene Historien, die demselben internen SHA-256-Zustand zugeordnet sind, der endlich ist. Während die Zyklen sicherlich lang sind, vielleicht länger als 2**(256+64) , existieren sie sicherlich. Und in jedem Fall haben Sie nur weniger als 2**(256+64) mögliche Anfangsbedingungen (für jede Textlänge von 0 bis 2**64-1 können Sie höchstens 2**256 interne Hash-Zustände haben; Die Textlänge beträgt mehr als 32 Byte (es müssen Kollisionen a la Pigeonhole auftreten). Es gibt einfach keine Bits mehr in der Datenstruktur.

Vielen Dank; verstanden. Ich würde es anders ausdrücken: den Staat
Der Raum ist unbegrenzt, aber (durch die Schublade) müssen viele verschiedene Anfangszustände
erzeugen nicht unterscheidbare Ausgabesequenzen.

Am Mittwoch, 29. Mai 2019, um 20:21 Uhr Robert Kern [email protected]
schrieb:

Ich fürchte, so funktioniert das Online-Update von SHA-256 nicht. Der Staat
Es werden nur der 256-Bit-Digest und der 64-Bit-Digest mit fester Größe beibehalten
Längenzähler, den es beim Berechnen des Updates anfügt. Es hält nicht
der ganze Text. Hashes komprimieren. So kann es effizient arbeiten
Update für jedes Byte. Grundsätzlich gibt es viele Initialisierungen / Vergangenheit
Historien, die demselben internen SHA-256-Status zugeordnet sind, der endlich ist.
Während die Zyklen sicherlich lang sind, vielleicht länger als 2 (256 + 64), sind siesicherlich existieren.
mögliche Anfangsbedingungen (für jede Textlänge 0 bis 2

Bytes, es muss Kollisionen a la Pigeonhole geben). Es gibt einfach keine
mehr Bits in der Datenstruktur.

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail und sieh sie dir auf GitHub an
https://github.com/numpy/numpy/issues/13635?email_source=notifications&email_token=AANFDWO6HRJZHTVBF2TLK3LPX5B3TA5CNFSM4HPX3CHKYY3PNVWWK3TUL52HS4WVM2
oder schalten Sie den Thread stumm
https://github.com/notifications/unsubscribe-auth/AANFDWM56HCQRZXDO3BAQHDPX5B3TANCNFSM4HPX3CHA
.

- -
Philip B. Stark | Associate Dean, Mathematik und Physik |
Professor, Institut für Statistik |
Universität von Kalifornien
Berkeley, CA 94720-3860 | 510-394-5077 | statistics.berkeley.edu/~stark |
@philipbstark

Es ist auch der Fall, dass es nur 2**(256+64) Zustände gibt, die möglicherweise durchlaufen werden können. Da das Update jedes Mal dieselbe Form annimmt, erreichen Sie schließlich einen Zustand, den Sie zuvor gesehen haben, und treten in eine Schleife unbekannter (für mich), aber endlicher Zeiträume ein. Unabhängig davon, ob es sich um die endliche Anzahl von Anfangszuständen oder um eine endliche Periode handelt, hat cryptorandom beides, und ich denke, sie sind sogar kleiner als MT19937 .

Nicht, dass ich denke, dass das per se ein Problem mit cryptorandom ist. Ich war nicht davon überzeugt, dass ein unbegrenzter Satz von Anfangszuständen oder eine unbegrenzte Periode tatsächlich im praktischen Sinne benötigt wird.

Ich bin noch nicht auf einen Fehler mit einem zufälligen Startwert gestoßen, aber mit den gleichen engen Schritten. Ich melde mich morgen bei dir.

Immer noch stark bei 512GiB:

❯ ./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)

Ah, wenn wir mehr als 2 Streams mit sequentiellen Inkrementen ausführen, sehen wir schnell Fehler. Wir müssen versuchen, das tatsächliche Inkrement aus der Benutzereingabe mit einer Art Bijektion abzuleiten, um es im Raum zu verteilen.

❯ ./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

Wenn Sie herausfinden können, was ich anders mache, wäre das hilfreich.

Ich denke, Sie müssen die Zeile pcg64 rng2(-seed,-stream); in Ihrem Code durch pcg64 rng2(-seed,-1-stream); ersetzen, um die increment = 2 * stream + 1 -Transformation zu ermöglichen. Die Negation des Inkrements entspricht der bitweisen Negation des Stream-Index. Wenn ich diese Änderung vornehme und Ihren Code ausführe, sehe ich etwas, das meinem früheren Plot sehr ähnlich ist. (Und ich bestätige, dass alles gut aussieht, wenn ich diese Änderung nicht vornehme.)

@imneme

Die richtige Art, an Streams zu denken, ist nur ein zufälliger Zustand, der gesetzt werden muss.

Einverstanden. Ich denke, das ergibt ein sehr sauberes Bild für LCGs: Für eine 64-Bit-LCG mit festem, gut gewähltem Multiplikator a haben wir dann einen Zustandsraum der Größe 2^127 , der aus allen Paaren (x, c) von ganzen Zahlen mod 2 ^ 64, wobei c das ungerade Inkrement ist. Die Statusaktualisierungsfunktion ist next : (x, c) ↦ (ax+c, c) und unterteilt den Statusbereich in 2^63 disjunkte Zyklen mit einer Länge von jeweils 2^64 . Beim Seeding muss lediglich ein Startpunkt in diesem Zustandsraum ausgewählt werden.

Es gibt dann eine offensichtliche Gruppenaktion, die die Analyse vereinfacht und die Beziehungen zwischen den verschiedenen Streams klar macht: Die Gruppe der invertierbaren affinen 1-D-Transformationen auf Z / 2^64Z hat eine genaue Reihenfolge von 2^127 und handelt transitiv (und so auch treu) auf dem Zustandsraum: Die affine Transformation y ↦ ey + f ordnet das Paar (x, c) (ex + f, ec + (1-a)f) . Diese Gruppenaktion pendelt mit der Funktion next , sodass das eindeutige Gruppenelement, das einen Punkt (x, c) im Statusraum in einen anderen umwandelt, (x2, c2) , auch die von (x, c) generierte Sequenz abbildet (x2, c2) generierten Sequenz.

tl; dr: Für einen festen Multiplikator werden zwei 2 oder das Multiplizieren mit -1 . Im allgemeinen Fall hoffen wir, dass die affine Transformation so kompliziert ist, dass statistische Standardtests die Beziehung zwischen den beiden Strömen nicht erkennen können.

@ Mdickinson deckt die Situation gut ab. Die Permutationen von PCG werden die Dinge ein wenig vom LCG-Fall ändern, aber nicht viel. Der springende Punkt bei den PCG-Permutationen ist, dass wir wählen können, wie viel Scrambling ausgeführt werden soll. Da abgeschnittene 128-Bit-LCGs BigCrush bereits bestehen, habe ich bei der Auswahl einer Permutation für pcg64 eine bescheidene Menge an Scrambling für diese LCG-Größe (XSL RR) ausgewählt. Im Gegensatz dazu scheitern 64-Bit-LCGs schnell an mehreren statistischen Tests, sodass pcg32 etwas mehr Scrambling verwendet, aber es ist immer noch nicht die stärkste Permutation aus dem PCG-Papier. Wie ich im Thread-Pull-Request-Thread erwähnt habe, habe ich begonnen, mich für den Anwendungsfall pcg32 einer stärkeren PCG-Permutation (RXS M) zuzuwenden. Es ist noch nicht die Standardeinstellung, Sie müssen explizit nach dieser Version fragen, aber es besteht eine gute Chance, dass ich die Standardeinstellung umschalte, wenn ich einen größeren Versionsschub für PCG mache. (RXS M ist die hohe Hälfte von RXS M XS, die Vigna ausgiebig bei dieser Größe und auch der Permutation getestet hat, die David Blackman mag).

Wir können den Unterschied visualisieren, den eine aktualisierte Version des Near-Streams-Testprogramms bei Verwendung beider Schemata für pcg32 (XSH RR und RCS M [und auch das zugrunde liegende LCG) verwendet:

#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";
    }
}

Bevor wir beginnen, schauen wir uns das Diagramm an, das @mdickinson gezeichnet hat, aber nur für ein LCG ohne Permutation, nur Kürzung:

corr-truncated-lcg

Beachten Sie, dass dies für pathologische LCG mit korrelierenden Zuständen gilt. Wenn wir stattdessen nur zwei LCGs mit zufällig ausgewählten additiven Konstanten (aber demselben Startwert) ausgewählt hätten, würde dies folgendermaßen aussehen:

corr-truncated-lcg-good

Wenn wir zu den Ausgabefunktionen von PCG übergehen und XSH RR für den pathologischen Fall verwenden, sieht es so aus - es ist eine große Verbesserung gegenüber der obigen Grafik, aber es ist klar, dass es die Schrecklichkeit nicht vollständig verdeckt:

corr-pcg32-current

und dies ist RXS M mit demselben zugrunde liegenden (schlecht korrelierten) LCG-Paar:

corr-pcg32-future

Aber das ist nur etwas, worüber ich nachdenke, pcg32 . Die Leistungseinbußen sind winzig und pcg32 ist klein genug, dass ich mir vorstellen kann, dass ein schwerer Benutzer sich Sorgen macht, eine Tonne zufällig generierter pcg32 Generatoren zu erstellen und eine Tonne Zahlen von ihnen und zu verlangen eine nicht infinitesimal genug große Chance auf Korrelationen haben. Ehrlich gesagt bin ich jedoch in zwei Gedanken, denn würde dieser mythische Power-User überhaupt pcg32 .

Ein Grund, warum ich mich nicht allzu sehr darum kümmere, die Streams von pcg64 unabhängiger zu machen, ist, dass ich nicht sicher bin, ob ich einen Anwendungsfall sehe, bei dem es sinnvoll wäre, alle anderen Zustände gleich zu halten und den Stream auf zu schalten ein anderer (z. B. zu einem zufälligen Wert, geschweige denn zu einem in der Nähe). Für so ziemlich alle PRNGs besteht der richtige Weg, ein zweites zu erstellen, darin, es mit frischer Entropie zu initialisieren.

Zusammenfassend kann ich sagen, dass es für NumPy am sinnvollsten ist, nur zu berücksichtigen, dass PCG64 zwei 256-Bit-Status will (technisch gesehen ist es 255, da das hohe Bit des Streams ignoriert wird) und es als erledigt zu bezeichnen. Dadurch werden auch Probleme im Zusammenhang mit der API vermieden, da in einem BitGenerator weniger Funktionen vorhanden sind als in einem anderen.

(Möglicherweise möchten Sie jedoch die 32-Bit-PCG-Variante auf die RXS M-Variante umstellen. Für die C-Quelle benötigen Sie eine neuere Version, da ich RXS M nicht explizit im C-Code bereitgestellt habe, sondern nur in C ++ verfügbar gemacht habe Menschwerdung.)

[Entschuldigung, wenn dies mehr ist, als Sie jemals wissen wollten! Nun, nicht das tut mir leid. ;-)]

Ein Grund, warum ich mich nicht allzu sehr darum kümmere, die Streams von pcg64 unabhängiger zu machen, ist, dass ich nicht sicher bin, ob ich einen Anwendungsfall sehe, bei dem es sinnvoll wäre, alle anderen Zustände gleich zu halten und den Stream auf zu schalten ein anderer (z. B. zu einem zufälligen Wert, geschweige denn zu einem in der Nähe). Für so ziemlich alle PRNGs besteht der richtige Weg, ein zweites zu erstellen, darin, es mit frischer Entropie zu initialisieren.

Ich habe den Anwendungsfall früher beschrieben. Es gibt starke UX-Gründe, ein stochastisches Programm zu schreiben, das eine einzelne kurze "Start" -Eingabe akzeptiert (dh etwas über die Größe, die sie kopieren und aus einer E-Mail in eine Befehlszeile einfügen können), wodurch die Ausgabe des Programms deterministisch wird. @stevenjkern stellte mir in einem Offline-Gespräch fest, dass diese Art der Interaktion für die Zusammenarbeit mit Aufsichtsbehörden, die seine Software validieren mussten, unerlässlich ist. Wenn Sie die Datei _output_ eines Programmlaufs verwenden mussten, um das Ergebnis zu replizieren, sieht dies unter solchen Umständen etwas verdächtig aus. Die Aufsichtsbehörde müsste sich eingehend mit dem Code befassen (der ihnen möglicherweise nicht zur Verfügung steht), um sicherzustellen, dass die Informationen in der Datei wirklich koscher sind.

In Python gibt es jetzt

Die Notwendigkeit, N-Streams reproduzierbar abzuleiten, ist stark genug, dass die Leute seltsame Dinge tun, um sie mit unserem aktuellen MT-Algorithmus zu erhalten. Ich musste eine Reihe riskanter Pläne abschießen und hoffte, dass die Streams von PCG uns dabei helfen würden, dorthin zu gelangen.

Was halten Sie von der Verwendung eines guten bijektiven Hash über 2**63 / 2**127 , um Inkremente aus einer Zählersequenz 0,1,2,3, ... abzuleiten, während der Status gleich bleibt? Sehen Sie Probleme damit? Was halten Sie von der Kombination des Hash-Inkrements, gefolgt von einem großen Jumpahead, um den Status in einen weiten Teil des neuen Zyklus zu verschieben? Vielleicht können wir diese Unterdiskussion in eine E-Mail oder ein anderes Problem verschieben und zurückmelden.

@rkern , es mag etwas sein, das sich gut

Das Problem verschärft sich, wenn beispielsweise jeder das gleiche PRNG (z. B. den Mersenne Twister) verwendet und Samen aus demselben kleinen Satz (z. B. Zahlen unter 10000) auswählt, weil dies nicht eine willkürliche bestimmte Verzerrung pro Programm ist eine für alle, die diese Tendenz tun. Nehmen wir zum Beispiel an, Sie wählen einen vierstelligen Startwert und nehmen dann eine angemessene Anzahl von Zahlen aus dem Mersenne Twister (z. B. weniger als eine Million). In dieser Situation kann ich Ihnen versichern, dass die unglückliche Zahl 13 niemals als eine der 10 Milliarden Ausgaben angezeigt wird (tatsächlich fehlen etwa 10% der 32-Bit-Ganzzahlen), und die Zahl 123580738 ist um einen Faktor von überrepräsentiert 16. Dies ist genau das, was wir für eine Zufallsstichprobe von zehn Milliarden 32-Bit-Ganzzahlen erwarten würden, aber es ist ein echtes Problem, wenn alle dieselbe Stichprobe verwenden. Wir hätten ein genau analoges Problem, wenn jeder neunstellige Samen auswählt und nur 10000 Zahlen zieht.

Die Tatsache, dass viele Leute etwas tun wollen, macht es nicht zu einer guten Idee. (Das bedeutet nicht, dass es in Ordnung ist, nur Leuten zu sagen, dass sie es falsch machen oder das Falsche wollen. Sie müssen herausfinden, was sie tatsächlich brauchen (z. B. reproduzierbare Ergebnisse aus einem kurzen Befehlszeilenargument - möglicherweise das Das Richtige ist, das Seeding von einer UUID und einer kleinen Ganzzahl zuzulassen. Einige Ideen, wie man diese Dinge gut verschlüsselt, um Seed-Daten zu erstellen, finden Sie in diesem Blog-Beitrag und finden ihren Weg in Randutils .)

(Hier ist der Code zum Spielen, da er ziemlich kurz ist ...)

// 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";
}

Wie ich bereits sagte, denke ich, dass 128-Bit-Seeds für diesen Zweck kurz genug sind, und ich kann das Tool erstellen, mit dem Leute Programme schreiben können, die das Richtige tun. Entropie-Abtastung nämlich standardmäßig, Ausdruck oder anderweitige Protokollierung, um sie später weitergeben zu können. Ihre Empfehlung, für jedes Programm eine UUID zu generieren und einen möglicherweise kleineren vom Benutzer bereitgestellten Startwert pro Lauf einzumischen, ist ebenfalls gut.

Nehmen wir an, ich kann Leute dazu bringen, gute 128-Bit-Seeds für den State-Teil von PCG64 , auf die eine oder andere Weise. Haben Sie Kommentare zum Ableiten von Streams aus demselben Status? Insgesamt möchte ich nicht mehr Zahlen ziehen als aus einem einzelnen PCG64 Stream. Ich möchte nur in der Lage sein, diese Zahlen in verschiedenen Prozessen ohne Koordination bei jeder Ziehung zu zeichnen. Die Verwendung eines Ad-hoc-63-Bit- Multiplikations-Xorshift-Hashs scheint bisher ziemlich gut zu funktionieren (ich bin gerade bei 32 GiB), für 8192 verschachtelte Streams.

@imneme

Es kann hilfreich sein zu definieren, was wir unter kurz verstehen.

Ich denke, dass @rkern "etwas über die Größe geschrieben hat, die sie kopieren und aus einer E-Mail in eine Befehlszeile einfügen können". Ich kann ziemlich große Zahlen in einigen Zeichen wie 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff darstellen.

Ich möchte nur in der Lage sein, diese Zahlen in verschiedenen Prozessen ohne Koordination bei jeder Ziehung zu zeichnen. Die Verwendung eines Ad-hoc-63-Bit- Multiplikations-Xorshift-Hashs scheint bisher ziemlich gut zu funktionieren (ich bin gerade bei 32 GiB), für 8192 verschachtelte Streams.

Haben Sie versucht, n Streams mit einem einzigen Qualitäts-Seed zu verschachteln, indem Sie den Status um eine ausreichend große Zahl erhöht haben (z. B. 2 ** 64, was von PCG64.jumped )? Dies scheint der einfachste Weg zu sein, große n -Streams in einem Cluster mithilfe von PCG64(seed).jumped(node_id) lose zu koordinieren

wobei node_id 0,1,2 ist, ...

Gibt es ein PRNG, das wirklich gut darin ist, einfache unabhängige Streams mit so etwas wie einem Index zu erstellen? Ich glaube, dass ein MLFG das kann, aber das hat mir nicht gefallen, da es ein 63-Bit-Generator war.

@bashtage , das ist wirklich nicht der richtige Weg. Der richtige Weg ist, einen Startwert zu nehmen. Wenn Sie eine kleine Ganzzahl hinzufügen möchten, verwenden Sie eine Hash-Funktion, um ihn zu hashen. Wie bereits erwähnt, habe ich zuvor (unabhängig von PCG) eine ernsthafte Mischfunktion geschrieben [Bearbeiten: Link korrigieren] zum richtigen Beitrag], um verschiedene Arten von großen und kleinen Entropien zu mischen. Sie müssen meine nicht verwenden, aber ich würde empfehlen, dass Sie etwas in diese Richtung tun.

Idealerweise möchten Sie einen Mechanismus, der nicht spezifisch für PCG ist. PCG ist möglicherweise nicht Ihre Standardauswahl, und selbst wenn dies der Fall wäre, möchten Sie, dass die Benutzer mit allen Generatoren ähnliche Aktionen ausführen. Ich denke nicht, dass Sie ein Schema für die Erstellung mehrerer unabhängiger PRNGs wünschen sollten, das von Streams oder Jump-Ahead abhängig ist.

(Hoppla, ich habe auf den falschen Blog-Beitrag verlinkt. Ich habe die vorherige Nachricht bearbeitet, aber falls Sie per E-Mail lesen, wollte ich auf diesen Blog-Beitrag verlinken.)

@imneme Im Moment unterstützen alle Generatoren, die wir haben, einen Sprung (von denen einige wirklich PRNG.jumped() . Ist das etwas, das davon abgeraten werden sollte?

Was das Seeding betrifft, verwenden alle MT-Generatoren die Init-Routinen des Autors, PCG verwendet Ihre und der Rest so etwas wie

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)

Ich kann mir vorstellen, dass dies verbessert werden könnte.

Ist das etwas, das davon abgeraten werden sollte?

Ich hatte jumped nicht gesehen. Es ist ziemlich schrecklich für die zugrunde liegende LCG.

Angenommen, wir haben einen Multiplikator M von 0x96704a6bb5d2c4fb3aa645df0540268d . Wenn wir M ^ (2 ^ 64) berechnen, erhalten wir 0x6147671fb92252440000000000000001 was ein schrecklicher LCG-Multiplikator ist. Wenn Sie also jedes 2 ^ 64. Element aus einem 128-Bit-LCG nehmen würden, wäre dies schrecklich (die niederwertigen Bits sind nur ein Zähler). Die Standard-Permutationsfunktionen von PCG dienen zum Verwürfeln der normalen Ausgabe eines LCG und nicht zum Verwürfeln von Zählern.

PCG64 wird derzeit mit Practrand bis zu einem halben Petabyte getestet. Weitere Analysen zeigen, dass Sie viele Petabyte ohne Probleme im Zusammenhang mit Zweierpotenzen lesen können. Leider können die üblichen (etwas bescheidenen) Permutationen von PCG die pathologische Abfolge des Überspringens der zugrunde liegenden großen LCG-Entfernungen wie dieser nicht ausreichend kompensieren, wenn Sie vorwärts springen, um große exakte Zweierpotenzen vorwärts zu überspringen. Sie können den Permutationsstärke-Ante erhöhen, um dies zu beheben, und tatsächlich haben sowohl I als auch Vigna unabhängig voneinander die Standardpermutationen von PCG mit Standard-Hash-Funktionen von der Stange verglichen, die dies wahrscheinlich tun würden (schließlich sind sie die Grundlage von SplitMix, das _is_ ist). nur ein Zähler). Als ich mich 2014 mit Fast Hash damit beschäftigte, schien die Geschwindigkeit nicht so groß zu sein, aber als Vigna es in jüngerer Zeit mit Murmeln tat, behauptete er, die Leistung sei Standard-PCG (!).

Wenn Sie wirklich einen Vorsprung von 2 ^ 64 haben möchten, müssen Sie meiner Meinung nach zu einer stärkeren Permutation der Ausgabefunktion wechseln (was, wie wir gesehen haben, zu geringen Kosten möglich ist). Aber wenn Sie das Gefühl haben, dass es nicht mehr wirklich "Standard" -PCG ist und die übliche Ausgabepermutation beibehalten möchten, muss jumped() wahrscheinlich gehen.

(Übrigens gilt der pathologische Sprung auch für andere PRNGs. SplitMix weist bekanntermaßen einige schlechte Inkremente auf, und es ist anzunehmen, dass das übliche Inkrement (a.ka. „Gamma“) von 0xbd24b73a95fb84d9 in Ordnung ist und um 2 ^ voranschreitet 32 gibt Ihnen ein Inkrement von 0x95fb84d900000000, was nicht so gut ist. Für LFSRs ist der schlechte Sprung wahrscheinlich keine Zweierpotenz, aber ich bin mir ziemlich sicher, dass es Sprünge geben wird, bei denen die zugrunde liegende Matrix pathologisch endet spärlich.)

Ich kann bestätigen, dass zumindest mit PCG32 4 verschachtelte Streams mit .jumped() sehr schnell fehlschlagen.

❯ ./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

Idealerweise möchten Sie einen Mechanismus, der nicht spezifisch für PCG ist. PCG ist möglicherweise nicht Ihre Standardauswahl, und selbst wenn dies der Fall wäre, möchten Sie, dass die Benutzer mit allen Generatoren ähnliche Aktionen ausführen.

Nun, das versuchen wir hier zu entscheiden. :-) Wir waren ziemlich zufrieden damit, einfach alle Funktionen, die jedes PRNG bereitstellte, aufzudecken, unter der Annahme, dass die von jedem Algorithmus belichteten Eigenschaften gut untersucht sind. Wir sind auch einigermaßen zufrieden damit, zu sagen: "Hier ist das von uns empfohlene Standard-PRNG. Es enthält eine Reihe nützlicher Funktionen. Die anderen haben sie möglicherweise nicht."

Die Idee, einen Hash zu verwenden, um einen neuen Zustand aus einem gegebenen Zustand und einer Stream-ID für jeden Algorithmus abzuleiten, ist interessant. Wissen Sie, wie gut das studiert ist? Es klingt nach einem Forschungsproblem, zu überprüfen, ob es für alle Algorithmen gut funktioniert. Ich würde zögern zu behaupten, "hier ist das allgemeine Verfahren zum Ableiten unabhängiger Streams für alle unsere PRNGs". Ich bin zufriedener mit "Hier ist eine gemeinsame API zum Ableiten unabhängiger Streams; jeder PRNG implementiert sie auf die für den Algorithmus geeignete Weise und implementiert sie möglicherweise nicht, wenn der Algorithmus sie nicht gut unterstützt".

Wenn es jedoch nur darum geht, genügend CPU-Zyklen zuzuweisen, um verschachtelte Streams für jeden BitGenerator an N GiB auf PractRand zu testen, ist das nicht allzu lästig.

Die Idee, einen Hash zu verwenden, um einen neuen Zustand aus einem gegebenen Zustand und einer Stream-ID für jeden Algorithmus abzuleiten, ist interessant. Wissen Sie, wie gut das studiert ist? Es klingt nach einem Forschungsproblem, zu überprüfen, ob es für alle Algorithmen gut funktioniert. Ich würde zögern zu behaupten, "hier ist das allgemeine Verfahren zum Ableiten unabhängiger Streams für alle unsere PRNGs". Ich bin zufriedener mit "Hier ist eine gemeinsame API zum Ableiten unabhängiger Streams; jeder PRNG implementiert sie auf die für den Algorithmus geeignete Weise und implementiert sie möglicherweise nicht, wenn der Algorithmus sie nicht gut unterstützt".

Ich weiß nicht, dass man es genau "ein Forschungsproblem" (und damit "gut untersucht") nennen kann, aber C ++ 11 (bei dem es hauptsächlich um die Verwendung bewährter, seit langem etablierter Techniken ging) bietet das _SeedSequence_-Konzept (und die eine bestimmte std::seed_seq -Implementierung), deren Aufgabe es ist, Seeding-Daten für völlig beliebige PRNGs bereitzustellen.

Im Allgemeinen erwarten fast alle PRNGs, dass sie mit zufälligen Bits initialisiert / geimpft werden. Es ist nichts besonders Magisches an zufälligen Bits, die aus (sagen wir) random.org und zufälligen Bits, die aus etwas algorithmischerem (CS PRNG, Hash-Funktion usw.) stammen.

Es ist ziemlich einfach, sich eine Sammlung von PRNGs aus demselben Schema vorzustellen, die alle mit ihren eigenen zufälligen Bits versehen sind. Sie können sich vorstellen, was wir tun, indem wir Punkte (oder tatsächlich Intervalle bis zu einer bestimmten maximalen Länge, die der Anzahl der Zufallszahlen entspricht, nach denen wir jemals plausibel fragen, z. B. 2 ^ 56) auf einer Linie (z. B. a Linie mit 2 ^ 255 Punkten). Wir können die Wahrscheinlichkeit berechnen, dass sich eines mit einem anderen überschneidet, wenn wir nach _n_ Intervallen fragen. Es ist eine ziemlich einfache Wahrscheinlichkeit - ich bin mir nicht sicher, ob Sie eine Veröffentlichung darüber veröffentlichen könnten, da (so wie ich es verstehe) niemand jemals von Veröffentlichungen begeistert ist, die elementare Mathematik enthalten. ( @lemire könnte anderer Meinung sein!)

[Ich würde argumentieren, dass Sie im Allgemeinen _nicht_ ein PRNG mit zufälligen Bits setzen sollten, die aus sich herauskommen. Das fühlt sich für mich viel zu inzestuös an.]

Richtig, mir ist klar, dass die Verwendung von so etwas wie einer gut gestalteten _SeedSequence_ eine Möglichkeit wäre, einen beliebigen Anfangssamen zu nehmen und mehrere Startpunkte im Zyklus unseres Algorithmus zu zeichnen, die sich nicht überlappen sollten. Und wenn dies der einzig wahre Weg ist, um unabhängige Streams zu erhalten, dann sei es so. Es wird eine Frage des API-Designs sein, um dies bequem zu machen.

Was mir weniger klar ist, ist, wie sicher es ist, den aktuellen Status eines initialisierten PRNG-Hash-Mix in der Stream-ID zu verwenden, um zu einem neuen Status im Zyklus zu springen. Ich dachte, Sie haben dies vorgeschlagen (und das auch) kam mir später der Gedanke, dass ich mich darin geirrt haben könnte). Eine gute Trennung im Zyklus ist nicht der einzige Faktor, wie das Scheitern von jumped() zeigt. jumped() außerdem sicher, dass Sie an einen weit entfernten Teil der Sequenz gesendet werden, der sich nicht überschneidet. Es ist nur ein Teil, der sehr stark mit dem Anfangsteil korrelieren kann, wenn der Sprung nicht gut gewählt ist. Es kann einige Kenntnisse der Interna jedes Algorithmus erfordern, um zu wissen, was ein guter Sprung ist und was nicht. Wir haben offensichtlich nicht für den Fall von PCG.

Wenn wir PRNGs als Übergangsfunktionen und Ausgabefunktionen betrachten, ist diese new_state = seed_seq(old_state||streamID) grundsätzlich nur eine weitere Übergangsfunktion, die wir für einen Schritt betrachten. Wir müssen sicher sein, dass die Operationen, die an diesem seed_seq sind, sich genug genug von den Übergangsfunktionen in jedem PRNG-Algorithmus (oder ihren Inversen) unterscheiden und möglicherweise andere Dinge sicher sein müssen. Ich möchte nicht etwas verwenden, das beispielsweise aus wyhash , um wyrand zu initialisieren . Wie Sie sagen, möchten Sie das PRNG selbst nicht verwenden, um die Bits für sich selbst bereitzustellen. Aus diesem Grund denke ich, dass einige Studien erforderlich sind, um dies für alle unsere PRNGs sicherzustellen (Studie, von der ich gehofft hatte, dass ich sie nicht selbst durchführen muss).

Auf der anderen Seite ist new_state = seed_seq(old_state||streamID) in dieser Hinsicht wahrscheinlich nicht schlechter als die beabsichtigte Verwendung von _SeedSequence_ für mehrere Streams: Ziehen Sie zwei Zustände nacheinander heraus. Wenn ja, dann kann ich mich auf die Erfahrung von C ++ stützen, vielleicht auf Ihre Implementierung, und nur einige empirische Tests mit PractRand für alle unsere Algorithmen durchführen, um zu zeigen, dass sie nicht schlechter dran sind als ihre Single-Stream-Gegenstücke.

Es wäre wirklich schön, den Hash-Sprung zum Laufen zu bringen, da dies einige Anwendungsfälle für das koordinierungsfreie Ablaichen von PRNGs eröffnet. Die Verwendung von Stream-IDs erfordert eine gewisse Kommunikation oder Vorabzuweisung. dask hat in der Vergangenheit nach so etwas gefragt.

Wenn es gute Alternativen gibt, die nur auf einem guten Seeding beruhen, das wir bequem machen können, um das Richtige zu tun, sollten wir wahrscheinlich einstellbare Streams als Kriterium für die Auswahl des Standards entfernen. Wir wollen nur einen Standardalgorithmus, der einen ausreichend großen Zustandsraum hat.

Alles in allem sieht es so aus, als würde die Verwendung eines 63-Bit-Hashs zum Ableiten des PCG32 -Inkrements aus sequentiellen Stream-IDs ( range(N) ) funktionieren. 8192 verschachtelte Ströme geben PractRand an 2 TiB weiter. Wenn wir Stream-IDs für die PCG-Generatoren verfügbar machen, möchten wir möglicherweise diese Technik verwenden, um die Inkremente abzuleiten, selbst wenn wir vorschlagen, dass Benutzer andere Mittel verwenden, um reproduzierbar unabhängige Streams zu erhalten.

Was mir weniger klar ist, ist, wie sicher es ist, den aktuellen Status eines initialisierten PRNG-Hash-Mix in der Stream-ID zu verwenden, um zu einem neuen Status im Zyklus zu springen. Ich dachte, Sie haben dies vorgeschlagen (und das auch) kam mir später der Gedanke, dass ich mich darin geirrt haben könnte).

Ich habe mich wahrscheinlich mehrdeutig ausgedrückt, aber nein , ich hatte nie die Absicht vorzuschlagen, dass der aktuelle Status des PRNG für die Selbstsaat verwendet werden sollte.

Aber, FWIW, SplitMix macht das, es ist das, was die Operation split() macht. Und ich mag es nicht, dass es das tut.

Dies sind vielleicht zu viele Informationen, aber ich werde ein wenig darüber erzählen, warum ich von SplitMix ' split() -Funktion entsetzt war (vielleicht mehr entsetzt als ich sollte). Aus historischer Sicht wurden SplitMix und PCG ungefähr zur gleichen Zeit unabhängig voneinander entwickelt (SplitMix wurde am 20. Oktober 2014 veröffentlicht, während die pcg-random.org im August 2014 online gingen und am 5. September 2014 mit dem PCG-Papier verknüpft wurden ). Es gibt einige Parallelen zwischen PCG und SplitMix (und verschiedenen anderen PRNGs, einschließlich Vignas xor shift * und xorshift + - ebenfalls 2014 veröffentlicht). Alle haben ziemlich einfache nicht ganz gut genug Zustandsübergangsfunktionen, die durch eine Verschlüsselungsausgangsfunktion festgelegt werden. Als ich das PCG-Papier schrieb, wusste ich, dass einige Leute eine split() -Funktion mögen würden, konnte aber keinen guten Weg finden, dies zu tun. Stattdessen habe ich einen schnellen Beweis dafür entwickelt, dass Sie, wenn Sie ein _k_-Bit-PRNG hätten, bei dem Sie bei jedem Schritt nach links oder rechts gehen könnten, innerhalb von _k_ Schritten in der Lage sein müssen, zu einem Zustand zu gelangen, in dem Sie sich zuvor befunden haben, und so das gesamte Konzept zu beweisen war schlecht konzipiert. Diese Beobachtung schaffte es nicht in die Zeitung. Aber als Ergebnis meiner Überlegungen vor diesem Beweis schlug ich in einer Fußnote in einem fast endgültigen Entwurf des Papiers etwas skurril vor, weil die Ausgabe dieser PCG ein Hash / Scramble / Permutation ihres Zustands war, wenn Sie es waren Wenn Sie sich ungezogen fühlen, können Sie den Generator mit seiner eigenen Leistung neu besäen und damit davonkommen. Ich habe es aus der endgültigen Version herausgenommen, weil ich dachte, dass solch eine Laune für Rezensenten eine zu große rote Fahne sein würde, da die Neuaussaat eines PRNG mit seinem eigenen Zustand allgemein als die Art des Missbrauchs eines PRNG und der Art angesehen wurde von Dingen, die wir von Menschen sehen, die unerfahren darin sind, sie zu benutzen.

Beim Lesen des SplitMix-Papiers fand ich viel zu mögen, aber ich war sehr überrascht, als ich split() . Es hat etwas getan, das ich im Grunde nur als Witz angesehen habe, und es zu einem Tentpole-Feature gemacht. Erst ein paar Jahre später schrieb ich ausführlicher darüber, was passiert, wenn Sie diese Art von Operation durchführen.

Der allgemeine Vorteil besteht darin, dass Sie, wenn Sie über einen ausreichend großen Statusbereich verfügen (und der von SplitMix kaum ausreicht), möglicherweise über eine Hash-Funktion mit der Selbstsaat davonkommen können. Ich halte das immer noch für keine gute Idee. Da zufällige Zuordnungen (mit denen wir uns in dieser Situation befassen) Eigenschaften wie "Mit einer asymptotischen Wahrscheinlichkeit ungleich Null hat der höchste Baum in einem Funktionsgraphen nicht im längsten Zyklus verwurzelt" haben, ist es meines Erachtens schwierig, volles Vertrauen zu haben es sei denn, der Designer hat die erforderlichen Arbeiten durchgeführt, um zu zeigen, dass solche Pathologien in seinem Entwurf nicht vorhanden sind.

Zum Spaß hier ein Dump des Zustandsraums einer winzigen Version von SplitMix, in dem nur drei verschiedene (und streng festgelegte) Möglichkeiten zum Kombinieren von next() und 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

usw.

Ah gut. Ich habe vor einigen Jahren selbst ein paar Vorschläge in diese Richtung abgeschossen, deshalb hatte ich Angst, dass wir etwas Gutes verpasst haben. :-)

Irgendwelche abschließenden Gedanken zum Hash-Ansatz zum Ableiten von Inkrementen für PCG-Streams? Abgesehen davon, ob dies der Hauptmechanismus ist, um unabhängige Streams zu erhalten. Ohne den Zugriff auf diese Funktion vollständig zu entfernen, möchten wir dies tun, um die leicht zu missbrauchenden sequentiellen Stream-IDs zu verhindern.

Gibt es aus Neugier eine (einfache) Möglichkeit zu erkennen, wie weit zwei Zustände in PCG64 voneinander entfernt sind?

Ja, obwohl wir es nicht offenlegen: http://www.pcg-random.org/useful-features.html#distance

Gibt es aus Neugier eine (einfache) Möglichkeit zu erkennen, wie weit zwei Zustände in PCG64 voneinander entfernt sind?

Ja, obwohl wir es nicht offenlegen: http://www.pcg-random.org/useful-features.html#distance

In der C ++ - Quelle gibt die Entfernungsfunktion sogar die Entfernung zwischen Streams an und gibt den Punkt an, an dem sie sich am nächsten nähern (wobei der einzige Unterschied zwischen den Streams eine hinzugefügte Konstante ist).

Im Übrigen können wir für die zugrunde liegende LCG die Entfernung verwenden, um herauszufinden, wie korreliert die erwarteten Positionen sind. Eine kurze Distanz ist offensichtlich schlecht (und für jedes PRNG überhaupt schlecht), aber eine Distanz mit nur einem Bit ist auch nicht großartig, weshalb man mit 2 ^ 64 ( 0x10000000000000000 ) mit vorspringt .jumped ist eine schlechte Idee. Auf meiner PCG-To-Do-Liste steht eine Funktion " independence_score ", die den Abstand zwischen zwei Zuständen untersucht und Ihnen sagt, wie zufällig der Abstand aussieht (über das Hamming-Gewicht usw. - idealerweise möchten wir ungefähr die Hälfte der Entfernung Bits sollen Nullen und halbe Einsen sein und sie sollen großzügig gestreut sein).

Eine Möglichkeit, mit PCG64 jumped zu halten, besteht darin, nicht um n * 0x10000000000000000 zu springen, sondern um n * 0x9e3779b97f4a7c150000000000000000 (auf 128 Bit abgeschnitten). Dies gibt Ihnen alle üblichen Eigenschaften, die Sie möchten ( .jumped(3).jumped(5) == .jumped(8) ), ohne für die zugrunde liegende LCG pathologisch zu sein.

(Ich bin mir auch bewusst, dass das Sprichwort "Nicht um 0x10000000000000000 vorrücken" so etwas wie eine " gut, halte es nicht so " independence_score existieren kann, aber diese ganze Sache (und das Problem mit ähnlichen Streams) kann für eine stärkere Standardausgabefunktion sprechen, so dass ich selbst dann, wenn Leute (seltene) Dinge tun, die für den Basiswert absolut pathologisch sind LCG, es wird kein Schaden angerichtet. PCG wird zu diesem Zeitpunkt fünf Jahre alt, und ich denke über einen Versions-Bump und Optimierungen in diesem Sommer nach, sodass dieses Problem möglicherweise in die Liste aufgenommen wird. Natürlich kann es Sie nerven, wenn Gerade als Sie PCG einsetzen, mache ich eine größere Version und verbessere sie.)

Irgendwelche abschließenden Gedanken zum Hash-Ansatz zum Ableiten von Inkrementen für PCG-Streams? Abgesehen davon, ob dies der Hauptmechanismus ist, um unabhängige Streams zu erhalten. Ohne den Zugriff auf diese Funktion vollständig zu entfernen, möchten wir dies tun, um die leicht zu missbrauchenden sequentiellen Stream-IDs zu verhindern.

Ich würde empfehlen, dass Sie es durch den Murmur3-Mixer setzen . Es ist wahrscheinlich, dass niemand ohne ähnliche Anstrengung versehentlich ähnliche Streams damit erstellt. (Bearbeiten: Ich denke, Sie benötigen eine 128-Bit-Version, aber Sie können auch die obere und untere Hälfte mischen. Ich würde auch eine Konstante hinzufügen. Jeder liebt 0x9e3779b97f4a7c15f39cc0605cedc835 (Bruchteil von ϕ), aber 0xb7e151628aed2a6abf7158809cf4f3c7 (Bruchteil von e) wäre auch in Ordnung, oder _jede_ zufällig aussehende Zahl.)

Ich empfehle wyhash (https://github.com/wangyi-fudan/wyhash), da es das schnellste und einfachste ist, das BigCrush und PractRand bestanden hat. Der c-Code ist so einfach wie

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

@ Wangyi-Fudan, ich kann mich nicht davon überzeugen, dass dies eine Bijektion ist.

Entschuldigung für mein begrenztes Wissen: Warum ist eine Bijektion für eine PRNG notwendig / bevorzugt?
wird für einige Erklärungen geschätzt :-)

@ wangyi-fudan, wenn eine Hash-Funktion von 64-Bit-Ints bis 64-Bit-Ints keine Bijektion ist (dh eine 1-zu-1-Funktion), werden einige Ergebnisse mehr als einmal und einige überhaupt nicht generiert. Das ist eine Art Voreingenommenheit.

Ich verstehe was du meinst. Für einen 64-Bit-Zufallszahlengenerator R erwarten wir jedoch eine Kollision nach 1,2 * 2 ^ 32 Zufallszahlen (http://mathworld.wolfram.com/BirthdayAttack.html). Bei 2 ^ 64 Zufallszahlen kommt es natürlich zu vielen Kollisionen. Kollisionen sind natürlich, während Bijektion nicht von Natur aus zufällig ist. Wenn ich weiß, dass der Spieltisch (z. B. ein 3-Bit-PRNG) innerhalb von 8 Trails einen Wert von 0 hat, werde ich es wagen, einen großen Einsatz auf Null zu machen, nachdem ich 5 Nicht-Null-Werte beobachtet habe.

@ wangyi-fudan, In diesem Zusammenhang haben wir über Möglichkeiten gesprochen, die Stream-ID so zu permutieren, dass Streams wie 1,2,3 etwas zufälliger aussehen (auch bekannt als normaler). In diesem Prozess gibt es keine Tugend bei Kollisionen.

Bei PRNGs im Allgemeinen sollten Sie den Unterschied zwischen PRNGs, die auf zufälligen Zuordnungen basieren, und solchen, die auf zufälligen invertierbaren Zuordnungen basieren (1: 1-Funktionen), nachlesen. Ich habe darüber geschrieben, aber andere auch. Bei kleinen PRNGs, die auf zufälligen Zuordnungen basieren, zeigen sie Verzerrungen und versagen schneller als PRNGs, die auf anderen Techniken basieren. Bei großen Größen können Fehler aller Art schwerer zu erkennen sein.

Wir können die Wahrscheinlichkeit berechnen, dass sich eines mit einem anderen überschneidet, wenn wir nach _n_ Intervallen fragen. Es ist eine ziemlich einfache Wahrscheinlichkeit - ich bin mir nicht sicher, ob Sie eine Veröffentlichung darüber veröffentlichen könnten, da (so wie ich es verstehe) niemand jemals von Veröffentlichungen begeistert ist, die elementare Mathematik enthalten.

Ich denke, du musst nur Pierre L'Ecuyer sein. ;-) Seite 15

Ja, wenn er die Grundlagen erklärt, gilt das als in Ordnung!

@rkern @imneme Einfachheit ist eine Funktion, sowohl in der Software als auch in der Mathematik. Dass einige von einfacher Arbeit unbeeindruckt sind, sollte nicht als widersprüchlicher Beweis angesehen werden.

@lemire : Es gibt ein _Wie man Informatiker kritisiert_ . Die Grundidee hinter dem Stück ist, dass Theoretiker Raffinesse und Experimentatoren Einfachheit bevorzugen. Wenn Ihr Publikum also ein Experimentator ist, werden sie von der Einfachheit begeistert sein, aber wenn Ihr Publikum einer der Theoretiker ist, nicht so sehr.

Der Standardwert für BitGenerator ist PCG64 . Vielen Dank für Ihre nachdenklichen Beiträge. Und Ausdauer!

Sehr inspiriert von diesem Thread, habe ich einige Neuigkeiten zu berichten…

Hintergrund

In vielerlei Hinsicht ist pcg64 ziemlich gut; Zum Beispiel erhält es unter den üblichen statistischen Qualitätsmaßstäben ein sauberes Gesundheitszeugnis. Es wurde auf verschiedene Arten getestet; Zuletzt habe ich es mit PractRand auf ein halbes Petabyte gebracht. Es funktioniert gut in normalen Anwendungsfällen.

ABER die Pathologien, die in diesem Thread auftauchten, passten nicht gut zu mir. Sicher, ich könnte sagen " gut, halte es nicht so ", aber der springende Punkt eines PRNG für allgemeine Zwecke ist, dass es robust sein sollte. Ich wollte es besser machen ...

Vor ungefähr 25 Tagen begann ich darüber nachzudenken, ein neues Mitglied der PCG-Familie zu entwerfen…

Tor

Mein Ziel war es, ein neues Mitglied der PCG-Familie zu entwerfen, das die derzeitige Variante pcg64 ersetzen könnte. So wie:

  • Die Ausgabefunktion sollte die Bits mehr als XSL RR verschlüsseln (da dadurch die Probleme vermieden werden, die in diesem Thread aufgetreten sind).
  • Die Leistung sollte ungefähr so ​​schnell (oder schneller) sein als die aktuellen pcg64 .
  • Das Design muss PCG-ish sein (dh nicht trivial vorhersehbar sein und daher nicht zulassen, dass _jedes_ der Arbeit der Ausgabefunktion leicht rückgängig gemacht werden kann).

Wie immer gibt es einen Kompromiss, da wir versuchen, so schnell wie möglich die bestmögliche Qualität zu erzielen. Wenn uns die Geschwindigkeit überhaupt nicht wichtig wäre, könnten wir mehr Schritte in der Ausgabefunktion haben, um eine stärker verschlüsselte Ausgabe zu erzeugen, aber der Punkt von PCG war, dass die zugrunde liegende LCG „fast gut genug“ war und wir sie nicht brauchten sich so viel Mühe geben, wie wir es mit einem Zähler tun würden, der um 1 erhöht wird.

Spoiler

Ich freue mich über Erfolge! Vor ungefähr 25 Tagen, als ich zum ersten Mal darüber nachdachte, war ich tatsächlich im Urlaub. Als ich vor ungefähr zehn Tagen zurückkam, probierte ich meine Ideen aus und stellte erfreut fest, dass sie gut funktionierten. Die folgende Zeit wurde hauptsächlich für verschiedene Arten von Tests aufgewendet. Gestern war zufrieden genug , dass ich schob den Code in das C ++ Version von PCG. Tests bei kleinen Größen zeigen, dass es viel besser als XSL RR ist und mit RXS M konkurriert, aber es glänzt tatsächlich bei größeren Größen. Es erfüllt auch alle anderen Ziele.

Einzelheiten

FWIW ist die neue Ausgabefunktion (für den 64-Bit-Ausgabefall):

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;
}

Diese Ausgabefunktion ist von der weit verbreiteten Xorshift-Multiplikation inspiriert. Die Wahl der Multiplikatoren besteht darin, (a) die Anzahl der magischen Konstanten niedrig zu halten und (b) zu verhindern, dass die Permutation rückgängig gemacht wird (wenn Sie keinen Zugriff auf Bits niedriger Ordnung haben) und auch das gesamte "randomisierte" an sich “Qualität, die PCG-Ausgabefunktionen normalerweise haben.

Andere Änderungen

Es ist auch der Fall, dass 0xda942042e4dd58b5 der LCG-Multiplikator für dieses PRNG ist (und alle PCG-Generatoren cm_ Präfix von 0x2360ed051fc65da44385df649fccf645 das von pcg64 , ist diese Konstante in Bezug auf die Spektraltest-Eigenschaften eigentlich immer noch ziemlich gut, aber billiger zu multiplizieren, da 128-Bit × 64-Bit einfacher ist als 128-Bit × 128-Bit. Ich habe diese LCG-Konstante mehrere Jahre ohne Probleme verwendet. Bei Verwendung der Billig-Multiplikator-Variante führe ich die Ausgabefunktion für eine größere Parallelität auf Befehlsebene im voriterierten Zustand und nicht im nachiterierten Zustand aus.

Testen

Ich habe es gründlich getestet (PractRand und TestU01) und bin damit zufrieden. Die Tests umfassten Szenarien, die in diesem Thread beschrieben wurden (z. B. einen Ganggenerator entweder auf sequentiellen Dämpfen oder um 2 ^ 64 vorgerückt und ihre Ausgabe verschachtelt - ich habe auch eine Vierergruppe und eine 8192-Gruppe auf 8 TB ohne Probleme getestet als Strom und sein Gegenstück zum Gegenland).

Geschwindigkeit

Ich könnte ausführlich über Geschwindigkeitstests und Benchmarks sprechen. Es gibt alle möglichen Faktoren, die beeinflussen, ob ein PRNG in einem bestimmten Benchmark schneller als ein anderes läuft. Insgesamt scheint diese Variante jedoch oft etwas schneller, manchmal viel schneller und gelegentlich etwas langsamer zu sein. Faktoren wie der Compiler und die Anwendung haben einen viel größeren Einfluss auf die Variabilität der Benchmarks.

Verfügbarkeit

Benutzer des C ++ - Headers können auf dieses neue Familienmitglied _now_ als pcg_engines::cm_setseq_dxsm_128_64 zugreifen. Irgendwann in der Zukunft werde ich pcg64 von pcg_engines::setseq_xsl_rr_128_64 auf dieses neue Schema umstellen. Mein aktueller Plan ist es, dies in diesem Sommer im Rahmen einer PCG 2.0-Version zu tun.

Formelle Ankündigungen

Insgesamt bin ich sehr zufrieden mit diesem neuen Familienmitglied und irgendwann später im Sommer wird es Blog-Posts mit mehr Details geben, die wahrscheinlich auf diesen Thread verweisen.

Deine Entscheidungen...

Natürlich muss man herausfinden, was man damit machen soll. Unabhängig davon, ob Sie es verwenden oder nicht, wäre ich ziemlich gespannt, ob es in Ihren Geschwindigkeits-Benchmarks besser oder schlechter abschneidet.

@imneme Umgeht es die Notwendigkeit eines schnellen vollständigen 64-Bit-Multiplikators? (Was auf x64 super schnell ist, auf einigen schwächeren Architekturen aber etwas langsamer.)

@lemire : 128-Bit × 64-Bit-Multiplikation, die intern mit zwei Multiplikationsbefehlen auf x86 durchgeführt wird (ein 64-Bit × 64-Bit → 128-Bit-Ergebnis auf den niederwertigen Bits und 64-Bit × 64) -bit auf den höherwertigen Bits. Beide Multiplikationen können parallel stattfinden, und dann müssen die beiden Ergebnisse addiert werden.)

Es ist immer noch potenziell besser als 128-Bit × 128-Bit. Wie viel besser davon abhängt, wie gut die Befehlsplanung in diesem Moment verläuft.

Sie haben Recht, dass bei ARM das 64-Bit × 64-Bit → 128-Bit-Ergebnis tatsächlich zwei Anweisungen sind.

(Es ist natürlich durchaus möglich, zwei 64-Bit-LCGs zusammenzufassen und zu mischen. Der Platz aller PRNGs, die existieren könnten und gut funktionieren würden, ist ziemlich groß.)

Eine schnelle und schmutzige Implementierung in unserem Framework deutet auf eine leichte Leistungsverbesserung hin, zumindest unter 64-Bit-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

Ich denke, es wird aus dem voriterierten Zustand ausgegeben, der ihm zumindest in diesem Zusammenhang die Beule gibt. Ich habe das zuerst weggelassen und im Wesentlichen die gleiche Leistung wie PCG64 1.0 erzielt. Ich vermute, der eigentliche Gewinn wäre eine 128-Bit-Emulation, aber ich bin nicht dazu gekommen, das aufzuschreiben, und ich habe keine gute Möglichkeit, es auf den wichtigen Plattformen zu testen.

Ich denke, die eigentliche Frage für Sie, @imneme , ist, wie verärgert Sie über den Namen numpy.random.PCG64 , der den 1.0-Algorithmus implementiert. Die Veröffentlichung steht kurz bevor und ist bereits verzögert, daher denke ich nicht, dass wir den Algorithmus zu diesem Zeitpunkt ändern werden. Wenn die Leistung auf 32-Bit-Plattformen besonders gut ist, werden wir in einer folgenden Version möglicherweise PCG64DXSM hinzufügen und möglicherweise die Standardeinstellungen einige Versionen später überdenken.

Sie haben die Wahl!

Ich habe kein Problem mit dem Versand der 1.0-Version von PCG64. Viele andere Leute haben diese Variante benutzt.

Ich denke, die DXSM-Variante hat den Vorteil, dass die in diesem Thread aufgetretenen Probleme bei der Verwendung von Randfällen vermieden werden (schließlich gibt es so ziemlich den Grund, warum es sie gibt), aber andererseits hat sie den Nachteil, dass es zu spät ist die Party. Es mag sogar ziemlich rücksichtslos erscheinen, ein PRNG, das weniger als einen Monat alt ist, an Benutzer zu versenden (obwohl es auf denselben Ideen basiert wie die bewährten PCG-Varianten).

(Das heißt, wenn es meine Wahl wäre, würde ich trotz möglicher Vorwürfe der Rücksichtslosigkeit wahrscheinlich die neue ausliefern; ich denke, die Verzögerung, um sie in Numpy zum Laufen zu bringen, ist ziemlich gering. Und das Risiko ist sehr gering - es ist bereits Mit BigCrush gründlich getestet und mit PractRand auf 16 TB getestet (einschließlich cm_mcg_dxsm_64_32 was einem Viertel der Größe entspricht [keine Streams, 32-Bit-Ausgabe]), und wird wahrscheinlich in weniger als einer Woche 32 TB erreichen .)

[Ich bin froh, dass die Leistung etwas besser geworden ist. Vor fünf Jahren war die Verwendung des voriterierten Zustands eine Pessimierung für 128-Bit-Größen mit 128-Bit-Multiplikatoren. Aber das war dann auf den Maschinen, auf denen ich getestet habe, mit den Benchmarks, die ich verwendet habe.]

Ich meinte mehr über die Verwendung des Namens PCG64 für die 1.0-Variante, wenn Sie diesen Namen verwenden, um auf die 2.0-Variante zu verweisen.

@rkern Wenn es sich nur um ein Namensproblem handelt, unterscheiden sich PCG64DXSM und PCG64 gut voneinander, oder?

Für Numpy sicher. Ich frage mich nur, ob @imneme es vorziehen würde, dass wir unsere 1.0-Implementierung nicht PCG64 wenn sie die 2.0-Variante unter diesem Namen in der C ++ - Version bewerben wird. Ich bin mir der Tatsache bewusst, dass das Loswerden mit den Namen dazu führt, dass einige Leute Numpys PCG64 testen und dies mit den Behauptungen vergleichen, die auf pcg-random.org über die 2.0-Version gemacht werden. Vgl. Fast jedes Gespräch über Bob Jenkins PRNGs.

In Abschnitt 6.3 des PCG-Papiers heißt es:

Beachten Sie auch, dass Benutzer der PCG-Bibliothek, obwohl den Generatoren basierend auf den von ihnen durchgeführten Permutationen mnemonische Namen angezeigt werden, Familienmitglieder selten anhand dieser Mnemonik auswählen sollten. Die Bibliothek bietet benannte Generatoren basierend auf ihren Eigenschaften und nicht auf den zugrunde liegenden Implementierungen (z. B. pcg32_unique für einen universellen 32-Bit-Generator mit einem eindeutigen Stream). Auf diese Weise können Benutzer nahtlos zu ihnen wechseln, wenn zukünftige Familienmitglieder entdeckt und hinzugefügt werden (hoffentlich aufgrund der Entdeckungen anderer).

Und die C- und C ++ - Bibliotheken sind so strukturiert. Die Bibliotheken bieten

  • Eine Low-Level-Schnittstelle, mit der Sie ein bestimmtes Familienmitglied über den Namen seiner Permutation, die Bitgrößen, mit denen es arbeitet, und die Eigenschaften der zugrunde liegenden LCG auswählen können
  • Eine Benutzeroberfläche auf hoher Ebene, die praktische Aliase wie pcg64 bereitstellt, die eine Verbindung zu einem vorgewählten Familienmitglied auf niedriger Ebene herstellen.

Auf diese Weise können die Aliase aktualisiert werden, um auf neuere Familienmitglieder zu verweisen. Benutzer, die ältere Ergebnisse exakt reproduzieren möchten, können jedoch weiterhin über die Benutzeroberfläche auf niedriger Ebene das Familienmitglied auswählen, das zuvor über ein bequemes Hoch erreichbar war -Level-Alias.

Wenn Sie ein PRNG mit dem Namen PCG64 versenden möchten, würde ich sagen, dass es ausreicht, in Ihrer Dokumentation anzugeben, um welche spezifische PCG-Variante es sich handelt - mit anderen Worten, um welches Familienmitglied es sich im unteren Bereich handelt -level C oder C ++ Bibliotheksschnittstelle.

Der implementierte Standardgenerator ist in https://github.com/numpy/numpy/pull/13840 als np.random.default_gen() implementiert . ( @rkern als zukünftige Referenz, es ist wahrscheinlich gut, PRs explizit aufzurufen - sie sind in GitHub leicht zu übersehen, wenn Sie nur einen

Ein kleiner Fehler: Wie wäre es stattdessen mit dem Nennen von np.random.default_generator() ? gen fühlt sich für mich zu kurz / nicht offensichtlich an. Ich wäre gespannt, was andere denken.

Wie wäre es stattdessen mit dem Aufruf von np.random.default_generator ()?

Ich hatte den gleichen Gedanken, aber dann ist np.random.default_generator() ein Haar auf der langen Seite, also habe ich mit default_rng .

👍 Ich mag default_rng besser als default_gen . Ich würde mit beiden zufrieden sein, obwohl ich mich immer noch zu default_generator .

: +1: für default_rng() .

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen