diff --git a/PyRandLib/__init__.py b/PyRandLib/__init__.py index 6f4aac8..bd75909 100644 --- a/PyRandLib/__init__.py +++ b/PyRandLib/__init__.py @@ -9,6 +9,7 @@ from .basecwg import BaseCWG from .baselcg import BaseLCG from .baselfib64 import BaseLFib64 +from .basemelg import BaseMELG from .basemrg import BaseMRG from .baserandom import BaseRandom from .basesquares import BaseSquares @@ -23,9 +24,12 @@ from .lfib116 import LFib116 from .lfib668 import LFib668 from .lfib1340 import LFib1340 -from .mrgrand287 import MRGRand287 -from .mrgrand1457 import MRGRand1457 -from .mrgrand49507 import MRGRand49507 +from .melg607 import Melg607 +from .melg19937 import Melg19937 +from .melg44497 import Melg44497 +from .mrg287 import Mrg287 +from .mrg1457 import Mrg1457 +from .mrg49507 import Mrg49507 from .pcg64_32 import Pcg64_32 from .pcg128_64 import Pcg128_64 from .pcg1024_32 import Pcg1024_32 diff --git a/PyRandLib/annotation_types.py b/PyRandLib/annotation_types.py index 06f3b88..7b83352 100644 --- a/PyRandLib/annotation_types.py +++ b/PyRandLib/annotation_types.py @@ -23,11 +23,11 @@ #============================================================================= from typing import List, Tuple, Union -Numerical = Union[ int, float ] -StatesList = Union[ Tuple[int], List[int] ] -StatesListAndState = Tuple[ StatesList, int ] -StateType = Union[ StatesList, StatesListAndState ] -SeedStateType = Union[ Numerical, StateType ] +Numerical = Union[ int, float ] +StatesList = Union[ Tuple[int], List[int] ] +StatesListAndExt = Union[ Tuple[ StatesList, int ], Tuple[ StatesList, int, int ] ] +StateType = Union[ StatesList, StatesListAndExt ] +SeedStateType = Union[ Numerical, StateType ] #===== end of PyRandLib.annotation_types =============================== diff --git a/PyRandLib/basecwg.py b/PyRandLib/basecwg.py index bbcaeef..41142af 100644 --- a/PyRandLib/basecwg.py +++ b/PyRandLib/basecwg.py @@ -22,7 +22,7 @@ #============================================================================= from .baserandom import BaseRandom -from .annotation_types import SeedStateType, StatesListAndState +from .annotation_types import SeedStateType, StatesListAndExt #============================================================================= @@ -96,7 +96,7 @@ def __init__(self, _seedState: SeedStateType = None) -> None: #------------------------------------------------------------------------- - def getstate(self) -> StatesListAndState: + def getstate(self) -> StatesListAndExt: """Returns an object capturing the current internal state of the generator. This object can be passed to setstate() to restore the state. diff --git a/PyRandLib/basemelg.py b/PyRandLib/basemelg.py new file mode 100644 index 0000000..e69665b --- /dev/null +++ b/PyRandLib/basemelg.py @@ -0,0 +1,199 @@ +""" +Copyright (c) 2025 Philippe Schmouker, schmouk (at) gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +#============================================================================= +from .baserandom import BaseRandom +from .annotation_types import SeedStateType, StateType +from .splitmix import SplitMix64 + + +#============================================================================= +class BaseMELG( BaseRandom ): + """Definition of the base class for all MELG pseudo-random generators. + + This module is part of library PyRandLib. + + Copyright (c) 2025 Philippe Schmouker + + Maximally Equidistributed Long-period Linear Generators (MELG) use linear + recurrence based on state transitions with double feedbacks and linear output + transformations with several memory references. See reference [11] in README.md. + + MELGs offer large to very large periods with best known results in the evaluation + of their randomness. They ensure a maximally equidistributed generation of pseudo + random numbers. They pass all TestU01 tests and newer ones but are the slowest to + compute ones in the base of PRNGs that have been implemented in PyRandLib. + + Notice: while the WELL algorithm use 32-bits integers as their internal state and + output pseudo-random 32-bits integers also, the MELG algorithm is full 64-bits. + + See Melg607 for a large period MELG-Generator (2^607, i.e. 5.31e+182) with medium + computation time and the equivalent of 21 32-bits integers memory little + consumption. This is the shortest period version proposed in paper [11]. + See Melg19937 for an even larger period MELG-Generator (2^19,937, i.e. 4.32e+6001), + same computation time and equivalent of 626 integers memory consumption. + See Melg44497 for a very large period (2^44,497, i.e. 15.1e+13,466) with similar + computation time but use of even more memory space (equivalent of 1,393 32-bits + integers). This is the longest period version proposed in paper [11]. + + Please notice that this class and all its inheriting sub-classes are callable. + Example: + + rand = BaseMELG() # Caution: this is just used as illustrative. This base class cannot be instantiated + print( rand() ) # prints a pseudo-random value within [0.0, 1.0) + print( rand(a) ) # prints a pseudo-random value within [0, a) or [0.0, a) depending on the type of a + print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) + + Inheriting classes have to define class attributes '_STATE_SIZE'. See Melg607 for + an example. + + Reminder: + We give you here below a copy of the table of tests for the MELG algorithms that + have been implemented in PyRandLib, as provided in paper [11] and when available. + + | PyRandLib class | [11] generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | + | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | + | Melg607 | melg607-64 | 21 x 4-bytes | 2^607 | n.a. | n.a. | n.a. | n.a. | n.a. | + | Melg19937 | melg19937-64 | 625 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | Melg44497 | melg44497-64 | 1,393 x 4-bytes | 2^44497 | n.a. | n.a. | n.a. | n.a. | n.a. | + + * _small crush_ is a small set of simple tests that quickly tests some of + the expected characteristics for a pretty good PRNG; + * _crush_ is a bigger set of tests that test more deeply expected random + characteristics; + * _big crush_ is the ultimate set of difficult tests that any GOOD PRNG + should definitively pass. + """ + + + #------------------------------------------------------------------------- + _NORMALIZE: float = 5.421_010_862_427_522_170_037_3e-20 # i.e. 1.0 / (1 << 64) + """The value of this class attribute MUST BE OVERRIDDEN in inheriting + classes if returned random integer values are coded on anything else + than 32 bits. It is THE multiplier constant value to be applied to + pseudo-random number for them to be normalized in interval [0.0, 1.0). + """ + + _OUT_BITS: int = 64 + """The value of this class attribute MUST BE OVERRIDDEN in inheriting + classes if returned random integer values are coded on anything else + than 32 bits. + """ + + #------------------------------------------------------------------------- + def __init__(self, _seedState: SeedStateType = None) -> None: + """Constructor. + + _seedState is either a valid state, an integer, a float or None. + About valid state: this is a tuple containing a list of + self._STATE_SIZE 64-bits integers, an index in this list and an + additional 64-bits integer as a state extension. Should _seedState + be a sole integer or float then it is used as initial seed for the + random filling of the internal state of the PRNG. Should _seedState + be anything else (e.g. None) then the shuffling of the local + current time value is used as such an initial seed. + + """ + super().__init__( _seedState ) + # this call creates the two attributes + # self._state and self._index, and sets them + # since it internally calls self.setstate(). + + + #------------------------------------------------------------------------- + def getstate(self) -> StateType: + """Returns an object capturing the current internal state of the generator. + + This object can be passed to setstate() to restore the state. It is a + tuple containing a list of self._STATE_SIZE 64-bits integers, an index + in this list and an additional 64-bits integer as a state extension. + """ + return (self._state[:], self._index) + + + #------------------------------------------------------------------------- + def setstate(self, _seedState: StateType) -> None: + """Restores the internal state of the generator. + + _seedState should have been obtained from a previous call to + getstate(), and setstate() restores the internal state of the + generator to what it was at the time setstate() was called. + Should _seedstate not contain a list of self._STATE_SIZE 64- + bits integers, a value for attribute self._index and a value + for attribute self._extState, this method tries its best to + initialize all these values. + """ + try: + count = len( _seedState ) + + if count == 0: + self._index = 0 + self._initstate() + + elif count == 1: + self._index = 0 + self._initstate( _seedState[0] ) + + elif count == 2: + self._initindex( _seedState[1] ) + if (len(_seedState[0]) == self._STATE_SIZE): + self._state = _seedState[0][:] # each entry in _seedState MUST be integer + else: + self._initstate( _seedState[0] ) + + else: + self._initindex( _seedState[1] ) + if (len(_seedState[0]) == self._STATE_SIZE): + self._state = _seedState[0][:] # each entry in _seedState MUST be integer + else: + self._initstate( _seedState[0] ) + + except: + self._index = 0 + self._initstate( _seedState ) + + + #------------------------------------------------------------------------- + def _initindex(self, _index: int) -> None: + """Inits the internal index pointing to the internal list. + """ + try: + self._index = int( _index ) % self._STATE_SIZE + except: + self._index = 0 + + + #------------------------------------------------------------------------- + def _initstate(self, _initialSeed: StateType = None) -> None: + """Inits the internal list of values. + + Inits the internal list of values according to some initial + seed that has to be an integer or a float ranging within + [0.0, 1.0). Should it be None or anything else then the + current local time value is used as initial seed value. + """ + # feeds the list according to an initial seed and the value+1 of the modulo. + initRand = SplitMix64( _initialSeed ) + self._state = [ initRand() for _ in range(self._STATE_SIZE) ] + + +#===== end of module basemelg.py ======================================= diff --git a/PyRandLib/basemrg.py b/PyRandLib/basemrg.py index dfa2db2..8059c8e 100644 --- a/PyRandLib/basemrg.py +++ b/PyRandLib/basemrg.py @@ -48,12 +48,12 @@ class BaseMRG( BaseRandom ): vol.33 n.4, pp.22-40, August 2007". It is recommended to use such pseudo-random numbers generators rather than LCG ones for serious simulation applications. - See MRGRand287 for a short period MR-Generator (2^287, i.e. 2.49e+86) with low + See Mrg287 for a shor t period MR-Generator (2^287, i.e. 2.49e+86) with low computation time but 256 integers memory consumption. - See MRGRand1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and - longer computation time (2^31-1 modulus calculations) but less memory space - consumption (47 integers). - See MRGRand49507 for a far longer period (2^49507, i.e. 1.2e+14903) with lower + See Mrg1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and longer + computation time (2^31-1 modulus calculations) but less memory space consumption + (47 integers). + See Mrg49507 for a far longer period (2^49507, i.e. 1.2e+14903) with lower computation time too (32-bits modulus) but use of more memory space (1597 integers). @@ -66,7 +66,7 @@ class BaseMRG( BaseRandom ): print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) Inheriting classes have to define class attributes '_STATE_SIZE' and '_MODULO'. - See MRGRand287 for an example. + See Mrg287 for an example. Reminder: We give you here below a copy of the table of tests for the MRGs that have @@ -75,9 +75,9 @@ class BaseMRG( BaseRandom ): | PyRandLib class | TU01 generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | - | MRGRand287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | - | MRGRand1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | - | MRGRand49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | + | Mrg1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | * _small crush_ is a small set of simple tests that quickly tests some of the expected characteristics for a pretty good PRNG; diff --git a/PyRandLib/baserandom.py b/PyRandLib/baserandom.py index e72e4e6..f1f9ddb 100644 --- a/PyRandLib/baserandom.py +++ b/PyRandLib/baserandom.py @@ -45,12 +45,12 @@ class BaseRandom( Random ): precision (64-bits calculations) but memory consumption (resp. 17, 55, 607 and 1279 32-bits integers). - See MRGRand287 for a short period MR-Generator (2^287, i.e. 2.49e+86) with low + See Mrg287 fo r a short period MR-Generator (2^287, i.e. 2.49e+86) with low computation time but 256 32-bits integers memory consumption. - See MRGRand1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and - longer computation time (2^31-1 modulus calculations) but less memory space - consumption (32-bits 47 integers). - See MRGRand49507 for a far larger period (2^49507, i.e. 1.2e+14903) with low + See Mrg1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and longer + computation time (2^31-1 modulus calculations) but less memory space consumption + (32-bits 47 integers). + See Mrg49507 for a far larger period (2^49507, i.e. 1.2e+14903) with low computation time too (31-bits modulus) but use of more memory space (1597 32-bits integers). @@ -365,15 +365,15 @@ def __call__(self, _max : Union[Numerical, #------------------------------------------------------------------------- @classmethod - def _rotleft(cls, _value: int, _rotCount: int) -> int: + def _rotleft(cls, _value: int, _rotCount: int, _bitsCount: int = 64) -> int: """Returns the value of a left rotating by _rotCount bits Useful for some inheriting classes. """ - assert( 0 <=_rotCount <= 64 ) - loMask = (1 << (64 - _rotCount)) - 1 - hiMask = ((1 << 64) - 1) ^ loMask - hiBits = (_value & hiMask) >> (64 - _rotCount) + #assert 1 <=_rotCount <= _bitsCount + loMask = (1 << (_bitsCount - _rotCount)) - 1 + hiMask = ((1 << _bitsCount) - 1) ^ loMask + hiBits = (_value & hiMask) >> (_bitsCount - _rotCount) return ((_value & loMask) << _rotCount) | hiBits diff --git a/PyRandLib/basewell.py b/PyRandLib/basewell.py index 8520f8b..0e623c3 100644 --- a/PyRandLib/basewell.py +++ b/PyRandLib/basewell.py @@ -34,9 +34,9 @@ class BaseWELL( BaseRandom ): Copyright (c) 2025 Philippe Schmouker - Well-Equilibrated Long-period Linear Generators (WELL) use linear recurrence based - on primitive characteristic polynomials associated with left- and right- shifts - and xor operations to fastly evaluate pseudo-random numbers suites. + Well-Equidistributed Long-period Linear Generators (WELL) use linear recurrence + based on primitive characteristic polynomials associated with left- and right- + shifts and xor operations to fastly evaluate pseudo-random numbers suites. WELLs offer large to very large periods with best known results in the evaluation of their randomness, as stated in the evaluation done by Pierre L'Ecuyer and diff --git a/PyRandLib/melg19937.py b/PyRandLib/melg19937.py new file mode 100644 index 0000000..cb34b3e --- /dev/null +++ b/PyRandLib/melg19937.py @@ -0,0 +1,121 @@ +""" +Copyright (c) 2025 Philippe Schmouker, schmouk (at) gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +#============================================================================= +from .basemelg import BaseMELG + + +#============================================================================= +class Melg19937( BaseMELG ): + """Pseudo-random numbers generator. Definition of a 64-bits Maximally Equidistrib- + uted Long-period Linear generator with a large period (2^19,937, i.e. 4.32e+6001). + + This module is part of library PyRandLib. + + Copyright (c) 2025 Philippe Schmouker + + Maximally Equidistributed Long-period Linear Generators (MELG) use linear + recurrence based on state transitions with double feedbacks and linear output + transformations with several memory references. See reference [11] in README.md. + + MELGs offer large to very large periods with best known results in the evaluation + of their randomness. They ensure a maximally equidistributed generation of pseudo + random numbers. They pass all TestU01 tests and newer ones but are the slowest to + compute ones in the base of PRNGs that have been implemented in PyRandLib. + + Notice: the implementation of this version of the MELG algorithm in PyRandLib is + not as optimized as it is in C code provided by MELG authors. It is rather derived + from the formal description and related tables provided in paper referenced [11] + in file README.md, to be able to easier validate the Python code here. + + Notice also: in the original paper [11], in the description of Algorithm 1, an + error (typo) appears at the initialization of 'x'. An bit-xor operation appears + in the text while it should be a bit-or operation as explaind in plain text. We + correct in in the code here. + + See Melg607 for a large period MELG-Generator (2^607, i.e. 5.31e+182) with medium + computation time and the equivalent of 21 32-bits integers memory little + consumption. + See Melg44497 for a very large period (2^44,497, i.e. 15.1e+13,466) with similar + computation time but use of even more memory space (equivalent of 1,393 32-bits + integers). This is the longest period version proposed in paper [11]. + + Furthermore, this class is callable: + rand = Melg19937() + print( rand() ) # prints a pseudo-random value within [0.0, 1.0) + print( rand(a) ) # prints a pseudo-random value within [0, a) or [0.0, a) depending on the type of a + print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) + + Notice that for simulating the roll of a dice you should program: + diceRoll = Melg19937() + print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} + + Such a programming is an accelerated while still robust emulation of the inherited + methods: + - random.Random.randint(self,1,6) and + - random.Random.randrange(self,1,7,1) + + Reminder: + We give you here below a copy of the table of tests for the MELG algorithms that + have been implemented in PyRandLib, as provided in paper [11] and when available. + + | PyRandLib class | [11] generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | + | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | + | Melg607 | melg607-64 | 21 x 4-bytes | 2^607 | n.a. | n.a. | n.a. | n.a. | n.a. | + | Melg19937 | melg19937-64 | 625 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | Melg44497 | melg44497-64 | 1,393 x 4-bytes | 2^44497 | n.a. | n.a. | n.a. | n.a. | n.a. | + + * _small crush_ is a small set of simple tests that quickly tests some of + the expected characteristics for a pretty good PRNG; + * _crush_ is a bigger set of tests that test more deeply expected random + characteristics; + * _big crush_ is the ultimate set of difficult tests that any GOOD PRNG + should definitively pass. + """ + + #------------------------------------------------------------------------- + # 'protected' constants + _STATE_SIZE = 312 + _A_COND = (0, 0x5c32_e06d_f730_fc42) # this tuple will avoid an 'if' in method 'next()' + + + #------------------------------------------------------------------------- + def next(self) -> int: + """This is the core of the pseudo-random generator. + + Notice: the output value is coded on 64-bits. + """ + i = self._index + i_1 = (i + 1) % 311 + self._index = i_1 + + s311 = self._state[311] + + x = (self._state[i] & 0xffff_fffe_0000_0000) | (self._state[i_1] & 0x0000_0001_ffff_ffff) # notice: | instead of ^ as erroneously printed in [11] + s311 = ((x >> 1) ^ self._A_COND[x & 0x01]) ^ self._state[(i+81) % 311] ^ (s311 ^ ((s311 << 23) & 0xffff_ffff_ffff_ffff)) + self._state[311] = s311 + + si = self._state[i] = x ^ (s311 ^ (s311 >> 33)) + return (si ^ ((si << 16) & 0xffff_ffff_ffff_ffff)) ^ ((self._state[(i + 19) % 311]) & 0x6aed_e6fd_97b3_38ec) + + +#===== end of module melg607.py ======================================== diff --git a/PyRandLib/melg44497.py b/PyRandLib/melg44497.py new file mode 100644 index 0000000..2b3875a --- /dev/null +++ b/PyRandLib/melg44497.py @@ -0,0 +1,120 @@ +""" +Copyright (c) 2025 Philippe Schmouker, schmouk (at) gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +#============================================================================= +from .basemelg import BaseMELG + + +#============================================================================= +class Melg44497( BaseMELG ): + """Pseudo-random numbers generator. Definition of a 64-bits Maximally Equidistrib- + uted Long-period Linear generator with a large period (2^607, i.e. 5.31e+182). + + This module is part of library PyRandLib. + + Copyright (c) 2025 Philippe Schmouker + + Maximally Equidistributed Long-period Linear Generators (MELG) use linear + recurrence based on state transitions with double feedbacks and linear output + transformations with several memory references. See reference [11] in README.md. + + MELGs offer large to very large periods with best known results in the evaluation + of their randomness. They ensure a maximally equidistributed generation of pseudo + random numbers. They pass all TestU01 tests and newer ones but are the slowest to + compute ones in the base of PRNGs that have been implemented in PyRandLib. + + Notice: the implementation of this version of the MELG algorithm in PyRandLib is + not as optimized as it is in C code provided by MELG authors. It is rather derived + from the formal description and related tables provided in paper referenced [11] + in file README.md, to be able to easier validate the Python code here. + + Notice also: in the original paper [11], in the description of Algorithm 1, an + error (typo) appears at the initialization of 'x'. An bit-xor operation appears + in the text while it should be a bit-or operation as explaind in plain text. We + correct in in the code here. + + See Melg607 for a large period MELG-Generator (2^607, i.e. 5.31e+182) with medium + computation time and the equivalent of 21 32-bits integers memory little + consumption. + See Melg19937 for an even larger period MELG-Generator (2^19937, i.e. 4.32e+6001), + same computation time and equivalent of 626 integers memory consumption. + + Furthermore, this class is callable: + rand = Melg444907() + print( rand() ) # prints a pseudo-random value within [0.0, 1.0) + print( rand(a) ) # prints a pseudo-random value within [0, a) or [0.0, a) depending on the type of a + print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) + + Notice that for simulating the roll of a dice you should program: + diceRoll = Melg444907() + print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} + + Such a programming is an accelerated while still robust emulation of the inherited + methods: + - random.Random.randint(self,1,6) and + - random.Random.randrange(self,1,7,1) + + Reminder: + We give you here below a copy of the table of tests for the MELG algorithms that + have been implemented in PyRandLib, as provided in paper [11] and when available. + + | PyRandLib class | [11] generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | + | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | + | Melg607 | melg607-64 | 21 x 4-bytes | 2^607 | n.a. | n.a. | n.a. | n.a. | n.a. | + | Melg19937 | melg19937-64 | 625 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | Melg44497 | melg44497-64 | 1,393 x 4-bytes | 2^44497 | n.a. | n.a. | n.a. | n.a. | n.a. | + + * _small crush_ is a small set of simple tests that quickly tests some of + the expected characteristics for a pretty good PRNG; + * _crush_ is a bigger set of tests that test more deeply expected random + characteristics; + * _big crush_ is the ultimate set of difficult tests that any GOOD PRNG + should definitively pass. + """ + + #------------------------------------------------------------------------- + # 'protected' constants + _STATE_SIZE = 696 + _A_COND = (0, 0x4fa9_ca36_f293_c9a9) + + + #------------------------------------------------------------------------- + def next(self) -> int: + """This is the core of the pseudo-random generator. + + Notice: the output value is coded on 64-bits. + """ + i = self._index + i_1 = (i + 1) % 695 + self._index = i_1 + + s695 = self._state[695] + + x = (self._state[i] & 0xffff_8000_0000_0000) | (self._state[i_1] & 0x0000_7fff_ffff_ffff) # notice: | instead of ^ as erroneously printed in [11] + s695 = ((x >> 1) ^ self._A_COND[x & 0x01]) ^ self._state[(i+373) % 695] ^ (s695 ^ ((s695 << 37) & 0xffff_ffff_ffff_ffff)) + self._state[695] = s695 + + si = self._state[i] = x ^ (s695 ^ (s695 >> 14)) + return (si ^ ((si << 6) & 0xffff_ffff_ffff_ffff)) ^ ((self._state[(i + 95) % 695]) & 0x06fb_bee2_9aae_fd91) + + +#===== end of module melg607.py ======================================== diff --git a/PyRandLib/melg607.py b/PyRandLib/melg607.py new file mode 100644 index 0000000..01691b8 --- /dev/null +++ b/PyRandLib/melg607.py @@ -0,0 +1,120 @@ +""" +Copyright (c) 2025 Philippe Schmouker, schmouk (at) gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +#============================================================================= +from .basemelg import BaseMELG + + +#============================================================================= +class Melg607( BaseMELG ): + """Pseudo-random numbers generator. Definition of a 64-bits Maximally Equidistrib- + uted Long-period Linear generator with a large period (2^607, i.e. 5.31e+182). + + This module is part of library PyRandLib. + + Copyright (c) 2025 Philippe Schmouker + + Maximally Equidistributed Long-period Linear Generators (MELG) use linear + recurrence based on state transitions with double feedbacks and linear output + transformations with several memory references. See reference [11] in README.md. + + MELGs offer large to very large periods with best known results in the evaluation + of their randomness. They ensure a maximally equidistributed generation of pseudo + random numbers. They pass all TestU01 tests and newer ones but are the slowest to + compute ones in the base of PRNGs that have been implemented in PyRandLib. + + Notice: the implementation of this version of the MELG algorithm in PyRandLib is + not as optimized as it is in C code provided by MELG authors. It is rather derived + from the formal description and related tables provided in paper referenced [11] + in file README.md, to be able to easier validate the Python code here. + + Notice also: in the original paper [11], in the description of Algorithm 1, an + error (typo) appears at the initialization of 'x'. An bit-xor operation appears + in the text while it should be a bit-or operation as explaind in plain text. We + correct in in the code here. + + See Melg19937 for an even larger period MELG-Generator (2^19937, i.e. 4.32e+6001), + same computation time and equivalent of 626 integers memory consumption. + See Melg44497 for a very large period (2^44,497, i.e. 15.1e+13,466) with similar + computation time but use of even more memory space (equivalent of 1,393 32-bits + integers). This is the longest period version proposed in paper [11]. + + Furthermore, this class is callable: + rand = Melg607() + print( rand() ) # prints a pseudo-random value within [0.0, 1.0) + print( rand(a) ) # prints a pseudo-random value within [0, a) or [0.0, a) depending on the type of a + print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) + + Notice that for simulating the roll of a dice you should program: + diceRoll = Melg607() + print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} + + Such a programming is an accelerated while still robust emulation of the inherited + methods: + - random.Random.randint(self,1,6) and + - random.Random.randrange(self,1,7,1) + + Reminder: + We give you here below a copy of the table of tests for the MELG algorithms that + have been implemented in PyRandLib, as provided in paper [11] and when available. + + | PyRandLib class | [11] generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | + | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | + | Melg607 | melg607-64 | 21 x 4-bytes | 2^607 | n.a. | n.a. | n.a. | n.a. | n.a. | + | Melg19937 | melg19937-64 | 625 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | Melg44497 | melg44497-64 | 1,393 x 4-bytes | 2^44497 | n.a. | n.a. | n.a. | n.a. | n.a. | + + * _small crush_ is a small set of simple tests that quickly tests some of + the expected characteristics for a pretty good PRNG; + * _crush_ is a bigger set of tests that test more deeply expected random + characteristics; + * _big crush_ is the ultimate set of difficult tests that any GOOD PRNG + should definitively pass. + """ + + #------------------------------------------------------------------------- + # 'protected' constants + _STATE_SIZE = 10 # the internal state of this PRNG is set on ten 64-bits integers N=10 + _A_COND = (0, 0x81f1_fd68_0123_48bc) # this tuple will avoid an 'if' in method 'next()', a=0x81f1... + + + #------------------------------------------------------------------------- + def next(self) -> int: + """This is the core of the pseudo-random generator. + + Notice: the output value is coded on 64-bits. + """ + i = self._index + i_1 = (i + 1) % 9 + self._index = i_1 + + s9 = self._state[9] + + x = (self._state[i] & 0xffff_ffff_8000_0000) | (self._state[i_1] & 0x0000_0000_7fff_ffff) # notice: | instead of ^ as erroneously printed in [11] + s9 = ((x >> 1) ^ self._A_COND[x & 0x01]) ^ self._state[(i+5) % 9] ^ (s9 ^ ((s9 << 13) & 0xffff_ffff_ffff_ffff)) # M=5, s1=13 + self._state[9] = s9 + + si = self._state[i] = x ^ (s9 ^ (s9 >> 35)) # s2=35 + return (si ^ ((si << 30) & 0xffff_ffff_ffff_ffff)) ^ ((self._state[(i + 3) % 9]) & 0x66ed_c62a_6bf8_c826) # s3=30, L=3, b = 0x66ed... + + +#===== end of module melg607.py ======================================== diff --git a/PyRandLib/mrgrand1457.py b/PyRandLib/mrg1457.py similarity index 92% rename from PyRandLib/mrgrand1457.py rename to PyRandLib/mrg1457.py index e1024c7..4480324 100644 --- a/PyRandLib/mrgrand1457.py +++ b/PyRandLib/mrg1457.py @@ -25,7 +25,7 @@ #============================================================================= -class MRGRand1457( BaseMRG ): +class Mrg1457( BaseMRG ): """ Pseudo-random numbers generator - Definition of a fast 31-bits Multiple Recursive Generator with long period (3.98e+438). @@ -54,9 +54,9 @@ class MRGRand1457( BaseMRG ): and offers a period of about 2^1457 - i.e. nearly 4.0e+438 - with low computation time. - See MRGRand287 for a short period MR-Generator (2^287, i.e. 2.49e+86) with low + See Mrg287 for a short period MR-Generator (2^287, i.e. 2.49e+86) with low computation time but 256 integers memory consumption. - See MRGRand49507 for a far longer period (2^49_507, i.e. 1.2e+14_903) with low + See Mrg49507 for a far longer period (2^49507, i.e. 1.2e+14903) with lower computation time too (31-bits modulus) but use of more memory space (1597 integers). @@ -70,7 +70,7 @@ class MRGRand1457( BaseMRG ): print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) Notice that for simulating the roll of a dice you should program: - diceRoll = MRGRand1457() + diceRoll = Mrg1457() print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} @@ -85,9 +85,9 @@ class MRGRand1457( BaseMRG ): | PyRandLib class | TU01 generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | - | MRGRand287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | - | MRGRand1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | - | MRGRand49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | + | Mrg1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | * _small crush_ is a small set of simple tests that quickly tests some of the expected characteristics for a pretty good PRNG; @@ -129,18 +129,18 @@ def next(self) -> int: # evaluates indexes in suite for the i-1, i-24 (and i-47) -th values k1 = self._index-1 if k1 < 0: - k1 = MRGRand1457._STATE_SIZE - 1 + k1 = Mrg1457._STATE_SIZE - 1 k24 = self._index-24 if k24 < 0: - k24 += MRGRand1457._STATE_SIZE + k24 += Mrg1457._STATE_SIZE # then evaluates current value myValue = (0x0408_0000 * (self._state[k1] + self._state[k24] + self._state[self._index]) ) % 2_147_483_647 self._state[self._index] = myValue # next index - self._index = (self._index + 1) % MRGRand1457._STATE_SIZE + self._index = (self._index + 1) % Mrg1457._STATE_SIZE # then returns the integer generated value return myValue diff --git a/PyRandLib/mrgrand287.py b/PyRandLib/mrg287.py similarity index 90% rename from PyRandLib/mrgrand287.py rename to PyRandLib/mrg287.py index 93cefe5..d2eccf7 100644 --- a/PyRandLib/mrgrand287.py +++ b/PyRandLib/mrg287.py @@ -25,7 +25,7 @@ #============================================================================= -class MRGRand287( BaseMRG ): +class Mrg287( BaseMRG ): """ Pseudo-random numbers generator - Definition of a fast 32-bits Multiple Recursive Generator with a long period (2.49e+86). @@ -72,21 +72,21 @@ class MRGRand287( BaseMRG ): and offers a period of about 2^287 - i.e. 2.49e+86 - with low computation time due to the use of a 2^32 modulo. - See MRGRand1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and - longer computation time (2^31-1 modulus calculations) but less memory space - consumption (47 integers). - See MRGRand49507 for a far longer period (2^49_507, i.e. 1.2e+14_903) with low + See Mrg1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and longer + computation time (2^31-1 modulus calculations) but less memory space consumption + (47 integers). + See Mrg49507 for a far longer period (2^49507, i.e. 1.2e+14903) with lower computation time too (31-bits modulus) but use of more memory space (1_597 integers). Furthermore, this class is callable: - rand = MRGRand287() + rand = Mrg287() print( rand() ) # prints a pseudo-random value within [0.0, 1.0) print( rand(a) ) # prints a pseudo-random value within [0, a) or [0.0, a) depending on the type of a print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) Notice that for simulating the roll of a dice you should program: - diceRoll = MRGRand287() + diceRoll = Mrg287() print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} Such a programming is an accelerated while still robust emulation of the inherited @@ -100,9 +100,9 @@ class MRGRand287( BaseMRG ): | PyRandLib class | TU01 generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | - | MRGRand287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | - | MRGRand1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | - | MRGRand49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | + | Mrg1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | * _small crush_ is a small set of simple tests that quickly tests some of the expected characteristics for a pretty good PRNG; @@ -128,15 +128,15 @@ def next(self) -> int: # evaluates indexes in suite for the i-55, i-119, i-179 (and i-256) -th values k55 = self._index-55 if k55 < 0: - k55 += MRGRand287._STATE_SIZE + k55 += Mrg287._STATE_SIZE k119 = self._index-119 if k119 < 0: - k119 += MRGRand287._STATE_SIZE + k119 += Mrg287._STATE_SIZE k179 = self._index-179 if k179 < 0: - k179 += MRGRand287._STATE_SIZE + k179 += Mrg287._STATE_SIZE # then evaluates current value myValue = (self._state[k55] + self._state[k119] + self._state[k179] + self._state[self._index]) & 0xffff_ffff diff --git a/PyRandLib/mrgrand49507.py b/PyRandLib/mrg49507.py similarity index 90% rename from PyRandLib/mrgrand49507.py rename to PyRandLib/mrg49507.py index 69eb3b8..bfbe142 100644 --- a/PyRandLib/mrgrand49507.py +++ b/PyRandLib/mrg49507.py @@ -25,7 +25,7 @@ #============================================================================= -class MRGRand49507( BaseMRG ): +class Mrg49507( BaseMRG ): """ Pseudo-random numbers generator - Definition of a fast 31-bits Multiple Recursive Generator with a very long period (1.17e+14_903). @@ -54,24 +54,24 @@ class MRGRand49507( BaseMRG ): and offers a period of about 2^49_507 - i.e. nearly 1.2e+14_903 - with low computation time. - See MRGRand287 for a short period MR-Generator (2^287, i.e. 2.49e+86) with low + See Mrg287 fo r a short period MR-Generator (2^287, i.e. 2.49e+86) with low computation time but 256 integers memory consumption. - See MRGRand1457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and - longer computation time (2^31-1 modulus calculations) but less memory space - consumption (47 integers). + See Mrg457 for a longer period MR-Generator (2^1457, i.e. 4.0e+438) and longer + computation time (2^31-1 modulus calculations) but less memory space consumption + (47 integers). Class random.Random is sub-subclassed here to use a different basic generator of our own devising: in that case, overriden methods are: random(), seed(), getstate(), and setstate(). Furthermore this class is callable: - rand = MRGRand49507() + rand = Mrg49507() print( rand() ) # prints a pseudo-random value within [0.0, 1.0) print( rand(a) ) # prints a pseudo-random value within [0, a) or [0.0, a) depending on the type of a print( rand(a, n) ) # prints a list of n pseudo-random values each within [0, a) Notice that for simulating the roll of a dice you should program: - diceRoll = MRGRand49507() + diceRoll = Mrg49507() print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} @@ -86,9 +86,9 @@ class MRGRand49507( BaseMRG ): | PyRandLib class | TU01 generator name | Memory Usage | Period | time-32bits | time-64 bits | SmallCrush fails | Crush fails | BigCrush fails | | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | - | MRGRand287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | - | MRGRand1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | - | MRGRand49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | + | Mrg1457 | DX-47-3 | 47 x 4-bytes | 2^1457 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49507 | n.a. | 1.4 | 0 | 0 | 0 | * _small crush_ is a small set of simple tests that quickly tests some of the expected characteristics for a pretty good PRNG; @@ -126,14 +126,14 @@ def next(self) -> int: # evaluates indexes in suite for the i-7, i-1597 -th values k7 = self._index-7 if k7 < 0: - k7 += MRGRand49507._STATE_SIZE + k7 += Mrg49507._STATE_SIZE # then evaluates current value myValue = (-67_108_992 * (self._state[k7] + self._state[self._index])) % 2_147_483_647 self._state[self._index] = myValue # next index - self._index = (self._index+1) % MRGRand49507._STATE_SIZE + self._index = (self._index+1) % Mrg49507._STATE_SIZE # then returns the integer generated value return myValue diff --git a/PyRandLib/splitmix.py b/PyRandLib/splitmix.py index 5970e87..2d674e8 100644 --- a/PyRandLib/splitmix.py +++ b/PyRandLib/splitmix.py @@ -71,7 +71,7 @@ def __init__(self, _seed: Numerical = None) -> None: #------------------------------------------------------------------------- def __call__(self, _seed: int = None) -> int: - """The shuffle algorithm. + """The split-mix algorithm. """ if _seed is not None: self._state = _seed & 0xffff_ffff_ffff_ffff @@ -80,8 +80,8 @@ def __call__(self, _seed: int = None) -> int: self._state &= 0xffff_ffff_ffff_ffff z = self._state - z = ((z ^ (z >> 30)) * 0xbf58476d1ce4e5b9) & 0xffff_ffff_ffff_ffff - z = ((z ^ (z >> 27)) * 0x94d049bb133111eb) & 0xffff_ffff_ffff_ffff + z = ((z ^ (z >> 30)) * 0xbf5_8476_d1ce_4e5b9) & 0xffff_ffff_ffff_ffff + z = ((z ^ (z >> 27)) * 0x94d_049b_b133_111eb) & 0xffff_ffff_ffff_ffff return z ^ (z >> 31) @@ -106,7 +106,7 @@ def __init__(self, _seed: Numerical = None) -> None: #------------------------------------------------------------------------- def __call__(self, _seed: int = None) -> int: - """The shuffle algorithm. + """The split-mix algorithm. """ # returns the 63 higher bits generated by base class operator () return super().__call__( _seed ) >> 1 @@ -132,7 +132,7 @@ def __init__(self, _seed: Numerical = None) -> None: #------------------------------------------------------------------------- def __call__(self, _seed: int = None) -> int: - """The shuffle algorithm. + """The split-mix algorithm. """ # returns the 32 higher bits generated by base class operator () return super().__call__( _seed ) >> 32 diff --git a/PyRandLib/well1024a.py b/PyRandLib/well1024a.py index ee62eb9..01b8ef4 100644 --- a/PyRandLib/well1024a.py +++ b/PyRandLib/well1024a.py @@ -26,16 +26,16 @@ #============================================================================= class Well1024a( BaseWELL ): """ - Pseudo-random numbers generator - Definition of a fast 32-bits Well-Equilibrated + Pseudo-random numbers generator. Definition of a fast 32-bits Well-Equidistributed Long-period Linear generator with a large period (2^1024, i.e. 2.68e+308). This module is part of library PyRandLib. Copyright (c) 2025 Philippe Schmouker - Well-Equilibrated Long-period Linear Generators (WELL) use linear recurrence based - on primitive characteristic polynomials associated with left- and right- shifts - and xor operations to fastly evaluate pseudo-random numbers suites. + Well-Equidistributed Long-period Linear Generators (WELL) use linear recurrence + based on primitive characteristic polynomials associated with left- and right- + shifts and xor operations to fastly evaluate pseudo-random numbers suites. WELLs offer large to very large periods with best known results in the evaluation of their randomness, as stated in the evaluation done by Pierre L'Ecuyer and @@ -47,7 +47,7 @@ class Well1024a( BaseWELL ): zeroland. Notice: the algorithm in its Well1024a version has been coded here as a direct - implementation of its descriptions in the initial paper: "Improved Long-Period + implementation of its description in the initial paper: "Improved Long-Period Generators Based on Linear Recurrences Modulo 2", François PANNETON and Pierre L'ECUYER (Université de Montréal) and Makoto MATSUMOTO (Hiroshima University), in ACM Transactions on Mathematical Software, Vol. 32, No. 1, March 2006, Pages 1-16. @@ -74,7 +74,6 @@ class Well1024a( BaseWELL ): diceRoll = Well1024a() print( int(diceRoll.randint(1, 6)) ) # prints a uniform roll within set {1, 2, 3, 4, 5, 6} - Such a programming is an accelerated while still robust emulation of the inherited methods: - random.Random.randint(self,1,6) and diff --git a/PyRandLib/well19937c.py b/PyRandLib/well19937c.py index d94c85b..0c18771 100644 --- a/PyRandLib/well19937c.py +++ b/PyRandLib/well19937c.py @@ -26,16 +26,16 @@ #============================================================================= class Well19937c( BaseWELL ): """ - Pseudo-random numbers generator - Definition of a fast 32-bits Well-Equilibrated + Pseudo-random numbers generator. Definition of a fast 32-bits Well-Equidistributed Long-period Linear generator with a large period (2^19937, i.e. 4.32e+6001). This module is part of library PyRandLib. Copyright (c) 2025 Philippe Schmouker - Well-Equilibrated Long-period Linear Generators (WELL) use linear recurrence based - on primitive characteristic polynomials associated with left- and right- shifts - and xor operations to fastly evaluate pseudo-random numbers suites. + Well-Equidistributed Long-period Linear Generators (WELL) use linear recurrence + based on primitive characteristic polynomials associated with left- and right- + shifts and xor operations to fastly evaluate pseudo-random numbers suites. WELLs offer large to very large periods with best known results in the evaluation of their randomness, as stated in the evaluation done by Pierre L'Ecuyer and @@ -47,7 +47,7 @@ class Well19937c( BaseWELL ): zeroland. Notice: the algorithm in its Well1024a version has been coded here as a direct - implementation of its descriptions in the initial paper: "Improved Long-Period + implementation of its description in the initial paper: "Improved Long-Period Generators Based on Linear Recurrences Modulo 2", François PANNETON and Pierre L'ECUYER (Université de Montréal) and Makoto MATSUMOTO (Hiroshima University), in ACM Transactions on Mathematical Software, Vol. 32, No. 1, March 2006, Pages 1-16. diff --git a/PyRandLib/well44497b.py b/PyRandLib/well44497b.py index b87e324..f022592 100644 --- a/PyRandLib/well44497b.py +++ b/PyRandLib/well44497b.py @@ -26,16 +26,16 @@ #============================================================================= class Well44497b( BaseWELL ): """ - Pseudo-random numbers generator - Definition of a fast 32-bits Well-Equilibrated + Pseudo-random numbers generator. Definition of a fast 32-bits Well-Equidistributed Long-period Linear generator with a large period (2^44497, i.e. 1.51e+13466). This module is part of library PyRandLib. Copyright (c) 2025 Philippe Schmouker - Well-Equilibrated Long-period Linear Generators (WELL) use linear recurrence based - on primitive characteristic polynomials associated with left- and right- shifts - and xor operations to fastly evaluate pseudo-random numbers suites. + Well-Equidistributed Long-period Linear Generators (WELL) use linear recurrence + based on primitive characteristic polynomials associated with left- and right- + shifts and xor operations to fastly evaluate pseudo-random numbers suites. WELLs offer large to very large periods with best known results in the evaluation of their randomness, as stated in the evaluation done by Pierre L'Ecuyer and @@ -47,7 +47,7 @@ class Well44497b( BaseWELL ): zeroland. Notice: the algorithm in its Well1024a version has been coded here as a direct - implementation of its descriptions in the initial paper: "Improved Long-Period + implementation of its description in the initial paper: "Improved Long-Period Generators Based on Linear Recurrences Modulo 2", François PANNETON and Pierre L'ECUYER (Université de Montréal) and Makoto MATSUMOTO (Hiroshima University), in ACM Transactions on Mathematical Software, Vol. 32, No. 1, March 2006, Pages 1-16. diff --git a/PyRandLib/well512a.py b/PyRandLib/well512a.py index 4b94336..1dd6043 100644 --- a/PyRandLib/well512a.py +++ b/PyRandLib/well512a.py @@ -26,16 +26,16 @@ #============================================================================= class Well512a( BaseWELL ): """ - Pseudo-random numbers generator - Definition of a fast 32-bits Well-Equilibrated + Pseudo-random numbers generator. Definition of a fast 32-bits Well-Equidistributed Long-period Linear generator with a large period (2^512, i.e. 1.34e+154). This module is part of library PyRandLib. Copyright (c) 2025 Philippe Schmouker - Well-Equilibrated Long-period Linear Generators (WELL) use linear recurrence based - on primitive characteristic polynomials associated with left- and right- shifts - and xor operations to fastly evaluate pseudo-random numbers suites. + Well-Equidistributed Long-period Linear Generators (WELL) use linear recurrence + based on primitive characteristic polynomials associated with left- and right- + shifts and xor operations to fastly evaluate pseudo-random numbers suites. WELLs offer large to very large periods with best known results in the evaluation of their randomness, as stated in the evaluation done by Pierre L'Ecuyer and @@ -47,7 +47,7 @@ class Well512a( BaseWELL ): zeroland. Notice: the algorithm in its Well512a version has been coded here as a direct - implementation of its descriptions in the initial paper: "Improved Long-Period + implementation of its description in the initial paper: "Improved Long-Period Generators Based on Linear Recurrences Modulo 2", François PANNETON and Pierre L'ECUYER (Université de Montréal) and Makoto MATSUMOTO (Hiroshima University), in ACM Transactions on Mathematical Software, Vol. 32, No. 1, March 2006, Pages 1-16. diff --git a/PyRandLib/xoroshiro1024.py b/PyRandLib/xoroshiro1024.py index d05466c..e0d8650 100644 --- a/PyRandLib/xoroshiro1024.py +++ b/PyRandLib/xoroshiro1024.py @@ -24,7 +24,7 @@ from typing import Tuple, Union from .basexoroshiro import BaseXoroshiro -from .annotation_types import Numerical, SeedStateType, StatesListAndState +from .annotation_types import Numerical, SeedStateType, StatesListAndExt from .splitmix import SplitMix64 diff --git a/README.md b/README.md index e2335bc..ab90036 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Many best in class pseudo random generators grouped into one simple library. ## License PyRandLib is distributed under the MIT license for its largest use. -If you decide to use this library, please add the copyright notice to your software as stated in the LICENSE file. +If you decide to use this library, please add the copyright notice to your software as stated in the LICENSE file. ``` Copyright (c) 2016-2025 Philippe Schmouker, @@ -74,9 +74,12 @@ We add in this table the evaluations provided by the authors of every new PRNGs | LFib116 | LFib(2^64, 55, 24, +) | 110 x 4-bytes | 2^116 | n.a. | 1.0 | 0 | 0 | 0 | | LFib668 | LFib(2^64, 607, 273, +) | 1,214 x 4-bytes | 2^668 | n.a. | 0.9 | 0 | 0 | 0 | | LFib1340 | LFib(2^64, 1279, 861, +) | 2,558 x 4-bytes | 2^1,340 | n.a. | 0.9 | 0 | 0 | 0 | - | MRGRand287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | - | MRGRand1457 | DX-47-3 | 47 x 4-bytes | 2^1,457 | n.a. | 1.4 | 0 | 0 | 0 | - | MRGRand49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49,507 | n.a. | 1.4 | 0 | 0 | 0 | + | Melg607 | *Melg607-64* | 21 x 4-bytes | 2^607 | n.a | n.a. | 0 | 0 | 0 | + | Melg19937 | *Melg19937-64* | 625 x 4-bytes | 2^19,937 | n.a | n.a. | 0 | 0 | 0 | + | Melg44497 | *Melg44497-64* | 1,392 x 4-bytes | 2^44,497 | n.a | n.a. | 0 | 0 | 0 | + | Mrg287 | Marsa-LFIB4 | 256 x 4-bytes | 2^287 | 3.40 | 0.8 | 0 | 0 | 0 | + | Mrg1457 | DX-47-3 | 47 x 4-bytes | 2^1,457 | n.a. | 1.4 | 0 | 0 | 0 | + | Mrg49507 | DX-1597-2-7 | 1,597 x 4-bytes | 2^49,507 | n.a. | 1.4 | 0 | 0 | 0 | | Pcg64_32 | *PCG XSH RS 64/32 (LCG)* | 2 x 4 bytes | 2^64 | n.a. | n.a. | 0 | 0 | 0 | | Pcg128_64 | *PCG XSL RR 128/64 (LCG)* | 4 x 4 bytes | 2^128 | n.a. | n.a. | 0 | 0 | 0 | | Pcg1024_32 | *PCG XSH RS 64/32 (EXT 1024)* | 1,026 x 4 bytes | 2^32,830 | n.a. | n.a. | 0 | 0 | 0 | @@ -118,9 +121,12 @@ Up to now, it has only been run with a Python 3.9.13 (64-bits) virtual environme | LFib116 | 0.35 | | | | | 0 | 0 | 0 | | LFib668 | 0.37 | | | | | 0 | 0 | 0 | | LFib1340 | 0.39 | | | | | 0 | 0 | 0 | - | MRGRand287 | 0.57 | | | | | 0 | 0 | 0 | - | MRGRand1457 | 0.58 | | | | | 0 | 0 | 0 | - | MRGRand49507 | 0.54 | | | | | 0 | 0 | 0 | + | Melg607 | 1.00 | | | | | 0 | 0 | 0 | + | Melg19937 | 1.07 | | | | | 0 | 0 | 0 | + | Melg44497 | 1.03 | | | | | 0 | 0 | 0 | + | Mrg287 | 0.57 | | | | | 0 | 0 | 0 | + | Mrg1457 | 0.58 | | | | | 0 | 0 | 0 | + | Mrg49507 | 0.54 | | | | | 0 | 0 | 0 | | Pcg64_32 | 0.39 | | | | | 0 | 0 | 0 | | Pcg128_64 | 0.57 | | | | | 0 | 0 | 0 | | Pcg1024_32 | 0.80 | | | | | 0 | 0 | 0 | @@ -130,9 +136,9 @@ Up to now, it has only been run with a Python 3.9.13 (64-bits) virtual environme | Well1024a | 1.80 | | | | | 0 | 4 | 4 | | Well19937b (1) | 2.43 | | | | | 0 | 2 | 2 | | Well44497c | 2.82 | | | | | n.a. | n.a. | n.a. | - | Xiroshiro256 | | | | | | 0 | 0 | 0 | - | Xiroshiro512 | | | | | | 0 | 0 | 0 | - | Xiroshiro1024 | | | | | | 0 | 0 | 0 | + | Xiroshiro256 | 1.65 | | | | | 0 | 0 | 0 | + | Xiroshiro512 | 2.01 | | | | | 0 | 0 | 0 | + | Xiroshiro1024 | 1.99 | | | | | 0 | 0 | 0 | (1)The Well19937b generator provided with library PyRandLib implements the Well19937a algorithm augmented with an associated *tempering* algorithm. (*missing values in empty columns are to come*) @@ -200,6 +206,9 @@ In **PyRandLib**, the Squares32 and Squares64 versions of the algorithm are impl 1. The xiroshiro algorithm ("Scrambled Linear Pseudorandom Number Generators", see [10], 2018) is now implemented in **PyRandLib**, in its *mult-mult* form for the output scrambler. This algorithm is fast, uses 64-bits integers as its internal state and outputs 64-bits values. It uses few memory space (4, 8 or 16 64-bits integers for resp. the 256-, 512- and 1024- versions that are implemented in **PyRandLib**. Notice: the 256 version of the algorithm is know to show close repeats flaws, with a bad Hamming weight near zero. *xoroshiro512* seems to best fit this property, according to the tables proposed by the authors in [10]. +1. The MELG algorithm ("Maximally Equidistributed Long-period Linear Generators", see [11], 2018) is now implemented in **PyRandLib**. It can be considered as an extension of the WELL algorithm, with a miximization of the equidistribution of generated values, making computations on 64-bits integers and outputing 64-bits values. +**PyRandLib** implements its versions numbered 627-64, 19937-64 and 44497-64 related to the power of 2 of their periods: Melg627, Melg19937 and Melg44497. + 1. The SplitMix algorithm is now implemented in **PyRandLib**. It is used to initialize the internal state of all other PRNGs. It SHOULD NOT be used as a PRNG due to its random poorness. 1. A short script `testED.py` is now avalibale at root directory. It checks the equi-distribution of every PRNG implemented in **PyRandLib** in a simple way and is used to test for their maybe bad implementation within the library. Since release 2.0 this test is run on all PRNGs. @@ -384,9 +393,33 @@ Please notice that the TestUO1 article states that the operator should be '*' wh -### MRGRand287 - 2^287 periodicity +### Melg627 -- 2^627 periodicity + +**Melg627** implements a fast 64-bits Maximally Equidistributed Long-period Linear Generator (MELG) with a large period (2^627, i.e. 5.31e+182) and low computation time. The internal state of this PRNG is equivalent to 21 integers 32-bits coded. + +The base MELG algorithm offers large to very large periods with the best known results in the evaluation of their randomness. It escapes the zeroland at a fast pace. Its specializations are set with parameters that ensures the maximized equidistribution. It might be valuable to use these rather than the WELL algorithm derivations + + + +### Melg19937 -- 2^19937 periodicity + +**Melg19937** implements a fast 64-bits Maximally Equidistributed Long-period Linear Generator (MELG) with a large period (2^19,937, i.e. 4.32e+6001) and low computation time. The internal state of this PRNG is equivalent to 625 integers 32-bits coded. + +The base MELG algorithm offers large to very large periods with the best known results in the evaluation of their randomness. It escapes the zeroland at a fast pace. Its specializations are set with parameters that ensures the maximized equidistribution. It might be valuable to use these rather than the WELL algorithm derivations + -**MRGRand287** implements a fast 32-bits Multiple Recursive Generator (MRG) with a long period (2^287, i.e. 2.49e+86) and low computation time (about twice the computation time of above LCGs) but 256 integers 32-bits coded memory consumption. + +### Melg44497 -- 2^44497 periodicity + +**Melg44497** implements a fast 64-bits Maximally Equidistributed Long-period Linear Generator (MELG) with a very large period (2^44,497, i.e. 15.1e+13,466) and low computation time. The internal state of this PRNG is equivalent to 1.393 integers 32-bits coded. + +The base MELG algorithm offers large to very large periods with the best known results in the evaluation of their randomness. It escapes the zeroland at a fast pace. Its specializations are set with parameters that ensures the maximized equidistribution. It might be valuable to use these rather than the WELL algorithm derivations + + + +### Mrg287 - 2^287 periodicity + +**Mrg287** implements a fast 32-bits Multiple Recursive Generator (MRG) with a long period (2^287, i.e. 2.49e+86) and low computation time (about twice the computation time of above LCGs) but 256 integers 32-bits coded memory consumption. Multiple Recursive Generators (MRGs) use recurrence to evaluate pseudo-random numbers suites. For 2 to more different values of *k*, recurrence is of the form: @@ -408,15 +441,15 @@ where op is an operation that can be With the + or - operation, such generators are true MRGs. They offer very large periods with the best known results in the evaluation of their randomness, as evaluated by Pierre L'Ecuyer and Richard Simard in their paper. -The Marsa-LIBF4 version, i.e. **MRGRand287** implementation, uses the recurrence: +The Marsa-LIBF4 version, i.e. **Mrg287** implementation, uses the recurrence: x(i) = ( x(i-55) + x(i-119) + x(i-179) + x(i-256) ) mod 2^32 -### MRGRand1457 - 2^1,457 periodicity +### Mrg1457 - 2^1,457 periodicity -**MRGRand1457** implements a fast 31-bits Multiple Recursive Generator with a longer period than MRGRan287 (2^1457 vs. 2^287, i.e. 4.0e+438 vs. 2.5e+86) and 80 % more computation time but with much less memory space consumption (47 vs. 256 integers 32-bits coded). +**Mrg1457** implements a fast 31-bits Multiple Recursive Generator with a longer period than MRGRan287 (2^1457 vs. 2^287, i.e. 4.0e+438 vs. 2.5e+86) and 80 % more computation time but with much less memory space consumption (47 vs. 256 integers 32-bits coded). The implementation of this MRG 31-bits model is based on DX-47-3 pseudo-random generator proposed by Deng and Lin, see [2]. The DX-47-3 version uses the recurrence: @@ -424,9 +457,9 @@ The implementation of this MRG 31-bits model is based on DX-47-3 pseudo-random -### MRGRand49507 - 2^49,507 periodicity +### Mrg49507 - 2^49,507 periodicity -**MRGRand49507** implements a fast 31-bits Multiple Recursive Generator with the longer period of all of the PRNGs that are implemented in **PyRandLib** (2^49,507, i.e. 1.2e+14,903) with low computation time also (same as for MRGRand287) but use of much more memory space (1,597 integers 32-bits coded). +**Mrg49507** implements a fast 31-bits Multiple Recursive Generator with the longer period of all of the PRNGs that are implemented in **PyRandLib** (2^49,507, i.e. 1.2e+14,903) with low computation time also (same as for Mrg287) but use of much more memory space (1,597 integers 32-bits coded). The implementation of this MRG 31-bits model is based on the 'DX-1597-2-7' MRG proposed by Deng, see [3]. It uses the recurrence: @@ -762,24 +795,24 @@ eprint = {http://amstat.tandfonline.com/doi/pdf/10.1080/00031305.2000.10474528} **[3]** Lih-Yuan Deng. 2005. *Efficient and portable multiple recursive generators of large order*. -ACM Transactions on Modeling and Computer. Simulation 15:1. +In ACM Transactions on Modeling and Computer Simulation, Jan. 2005, Vol. 15 Issue 1, pp. 1-13. +DOI: https://doi.org/10.1145/1044322.1044323 **[4]** Georges Marsaglia. 1985. *A current view of random number generators*. In Computer Science and Statistics, Sixteenth Symposium on the Interface. -Elsevier Science Publishers, North-Holland, Amsterdam, 1985, The Netherlands. pp. 3–10. +Elsevier Science Publishers, North-Holland, Amsterdam, 1985, The Netherlands, pp. 3–10. **[5]** Makoto Matsumoto and Takuji Nishimura. 1998. *Mersenne twister: A 623-dimensionally equidistributed uniform pseudo-random number generator.* -In ACM Transactions on Modeling and Computer Simulation (TOMACS) - Special issue on uniform random number generation. -Vol.8 N.1, Jan. 1998, pp. 3-30. +In ACM Transactions on Modeling and Computer Simulation (TOMACS) - Special issue on uniform random number generation. Vol.8 N.1, Jan. 1998, pp. 3-30. **[6]** François Panneton and Pierre L'Ecuyer (Université de Montréal) and Makoto Matsumoto (Hiroshima University). 2006. *Improved Long-Period Generators Based on Linear Recurrences Modulo 2*. -In ACM Transactions on Mathematical Software, Vol. 32, No. 1, March 2006, Pages 1–16. +In ACM Transactions on Mathematical Software, Vol. 32, No. 1, March 2006, pp. 1–16. see [https://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf](https://www.iro.umontreal.ca/~lecuyer/myftp/papers/wellrng.pdf). @@ -802,20 +835,28 @@ see also [https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf](https://www.pcg-r **[8]** Tomasz R. Dziala. 2023. *Collatz-Weyl Generators: High Quality and High Throughput Parameterized Pseudorandom Number Generators*. -Published at arXiv, December 2023 (11 pages) -Last reference: arXiv:2312.17043v4 [cs.CE], 2 Dec 2024, -see [https://arxiv.org/abs/2312.17043](https://arxiv.org/abs/2312.17043). +Published at arXiv, December 2023 (11 pages), +Last reference: arXiv:2312.17043v4 [cs.CE], 2 Dec 2024, see [https://arxiv.org/abs/2312.17043](https://arxiv.org/abs/2312.17043) +DOI: https://doi.org/10.48550/arXiv.2312.17043 **[9]** Bernard Widynski. March 2022. *Squares: A Fast Counter-Based RNG*. Published at arXiv, March 2022 (5 pages) -Last reference: arXiv:2004.06278v7 [cs.DS] 13 Mar 2022 -see [https://arxiv.org/pdf/2004.06278](https://arxiv.org/pdf/2004.06278). +Last reference: arXiv:2004.06278v7 [cs.DS] 13 Mar 2022, see [https://arxiv.org/pdf/2004.06278](https://arxiv.org/pdf/2004.06278). +DOI: https://doi.org/10.48550/arXiv.2004.06278 + + +**[10]** David Blackman, Sebastiano Vigna. 2018. +*Scrambled Linear Pseudorandom Number Generators*. +Published in arXiv, March 2022 (32 pages) +Last reference: arXiv:1805.01407v3 [cs.DS] 28 Mar 2022, see [https://arxiv.org/pdf/1805.01407](https://arxiv.org/pdf/1805.01407). +DOI: https://doi.org/10.48550/arXiv.1805.01407 -**[10]** David Blackman, Sebastiano Vigna. 2018. -*Scrambled Linear Pseudorandom Number Generators*. -Published in arXiv, March 2022 (32 pages) -Last reference: arXiv:1805.01407v3 [cs.DS] 28 Mar 2022 -see [https://arxiv.org/pdf/1805.01407](https://arxiv.org/pdf/1805.01407). +**[11]** Shin Harase, Takamitsu Kimoto, 2018. +*Implementing 64-bit Maximally Equidistributed F2-Linear Generators with Mersenne Prime Period*. +In ACM Transactions on Mathematical Software, Volume 44, Issue 3, April 2018, Article No. 30 (11 Pages) +Also published in arXiv, March 2022 (11 pages) +Last reference: arXiv:1505.06582v6 [cs.DS] 20 Nov 2017, see [https://arxiv.org/pdf/1505.06582](https://arxiv.org/pdf/1505.06582). +DOI: https://doi.org/10.1145/3159444, https://doi.org/10.48550/arXiv.1505.06582 diff --git a/testCPUPerfs.py b/testCPUPerfs.py index 3b801ac..2c7b532 100644 --- a/testCPUPerfs.py +++ b/testCPUPerfs.py @@ -58,9 +58,12 @@ def test_perf(prng_class_name: str, seed_value: int, n_loops: int, n_repeats: in test_perf("LFib116" , 0x3ca5_8796_1f2e_b45a, 100_000, N) test_perf("LFib668" , 0x3ca5_8796_1f2e_b45a, 100_000, N) test_perf("LFib1340" , 0x3ca5_8796_1f2e_b45a, 100_000, N) - test_perf("MRGRand287" , 0x3ca5_8796 , 100_000, N) - test_perf("MRGRand1457" , 0x3ca5_8796 , 100_000, N) - test_perf("MRGRand49507" , 0x3ca5_8796 , 100_000, N) + test_perf("Melg607" , 0x3ca5_8796_1f2e_b45a, 100_000, N) + test_perf("Melg19937" , 0x3ca5_8796_1f2e_b45a, 100_000, N) + test_perf("Melg44497" , 0x3ca5_8796_1f2e_b45a, 100_000, N) + test_perf("Mrg287" , 0x3ca5_8796 , 100_000, N) + test_perf("Mrg1457" , 0x3ca5_8796 , 100_000, N) + test_perf("Mrg49507" , 0x3ca5_8796 , 100_000, N) test_perf("Pcg64_32" , 0x3ca5_8796_1f2e_b45a, 100_000, N) test_perf("Pcg128_64" , 0x3ca5_8796_1f2e_b45a_3ca5_8796_1f2e_b45a, 100_000, N) test_perf("Pcg1024_32" , 0x3ca5_8796_1f2e_b45a, 100_000, N) @@ -70,8 +73,9 @@ def test_perf(prng_class_name: str, seed_value: int, n_loops: int, n_repeats: in test_perf("Well1024a" , 0x3ca5_8796 , 100_000, N) test_perf("Well19937c" , 0x3ca5_8796 , 100_000, N) test_perf("Well44497b" , 0x3ca5_8796 , 100_000, N) - test_perf("Xoroshiro256" , 0x3ca5_8796 , 100_000, N) - test_perf("Xoroshiro512" , 0x3ca5_8796 , 100_000, N) + test_perf("Xoroshiro256" , 0x3ca5_8796_1f2e_b45a, 100_000, N) + test_perf("Xoroshiro512" , 0x3ca5_8796_1f2e_b45a, 100_000, N) + test_perf("Xoroshiro1024", 0x3ca5_8796_1f2e_b45a, 100_000, N) #===== end of module testCPUPerfs.py =================================== diff --git a/testED.py b/testED.py index 3167877..2a081d3 100644 --- a/testED.py +++ b/testED.py @@ -109,29 +109,32 @@ def test_algo(rnd_algo, nb_entries: int = 1_000, nb_loops: int = 1_000_000): #============================================================================= if __name__ == "__main__": - test_algo(Cwg64(), 3217, nb_loops = 2_000_000) # notice: 3217 is a prime number - test_algo(Cwg128_64(), 3217, nb_loops = 2_000_000) - test_algo(Cwg128(), 3217, nb_loops = 2_000_000) - test_algo(FastRand32(), 3217, nb_loops = 2_000_000) - test_algo(FastRand63(), 3217, nb_loops = 2_000_000) - test_algo(LFib78(), 3217, nb_loops = 2_000_000) - test_algo(LFib116(), 3217, nb_loops = 2_000_000) - test_algo(LFib668(), 3217, nb_loops = 2_000_000) - test_algo(LFib1340(), 3217, nb_loops = 2_000_000) - test_algo(MRGRand287(), 3217, nb_loops = 2_000_000) - test_algo(MRGRand1457(), 3217, nb_loops = 2_000_000) - test_algo(MRGRand49507(), 3217, nb_loops = 2_000_000) - test_algo(Pcg64_32(), 3217, nb_loops = 2_000_000) - test_algo(Pcg128_64(), 3217, nb_loops = 2_000_000) - test_algo(Pcg1024_32(), 3217, nb_loops = 2_000_000) - test_algo(Squares32(), 3217, nb_loops = 2_000_000) - test_algo(Squares64(), 3217, nb_loops = 2_000_000) - test_algo(Well512a(), 3217, nb_loops = 1_500_000) - test_algo(Well1024a(), 3217, nb_loops = 1_500_000) - test_algo(Well19937c(), nb_entries = 2029) # notice: 2029 is a prime number - test_algo(Well44497b(), nb_entries = 2029) - test_algo(Xoroshiro256(), 3217, nb_loops = 2_000_000) - test_algo(Xoroshiro512(), 3217, nb_loops = 2_000_000) + test_algo(Cwg64(), 3217, nb_loops = 2_000_000) # notice: 3217 is a prime number + test_algo(Cwg128_64(), 3217, nb_loops = 2_000_000) + test_algo(Cwg128(), 3217, nb_loops = 2_000_000) + test_algo(FastRand32(), 3217, nb_loops = 2_000_000) + test_algo(FastRand63(), 3217, nb_loops = 2_000_000) + test_algo(LFib78(), 3217, nb_loops = 2_000_000) + test_algo(LFib116(), 3217, nb_loops = 2_000_000) + test_algo(LFib668(), 3217, nb_loops = 2_000_000) + test_algo(LFib1340(), 3217, nb_loops = 2_000_000) + test_algo(Melg607(), 3217, nb_loops = 2_000_000) + test_algo(Melg19937(), 3217, nb_loops = 2_000_000) + test_algo(Melg44497(), 3217, nb_loops = 2_000_000) + test_algo(Mrg287(), 3217, nb_loops = 2_000_000) + test_algo(Mrg1457(), 3217, nb_loops = 2_000_000) + test_algo(Mrg49507(), 3217, nb_loops = 2_000_000) + test_algo(Pcg64_32(), 3217, nb_loops = 2_000_000) + test_algo(Pcg128_64(), 3217, nb_loops = 2_000_000) + test_algo(Pcg1024_32(), 3217, nb_loops = 2_000_000) + test_algo(Squares32(), 3217, nb_loops = 2_000_000) + test_algo(Squares64(), 3217, nb_loops = 2_000_000) + test_algo(Well512a(), 3217, nb_loops = 1_500_000) + test_algo(Well1024a(), 3217, nb_loops = 1_500_000) + test_algo(Well19937c(), 2029) # notice: 2029 is a prime number + test_algo(Well44497b(), 2029) + test_algo(Xoroshiro256(), 3217, nb_loops = 2_000_000) + test_algo(Xoroshiro512(), 3217, nb_loops = 2_000_000) test_algo(Xoroshiro1024(), 3217, nb_loops = 2_000_000)