From 0aee88696068bcf02436e18e6c390d13c219f444 Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 09:37:48 +0100 Subject: [PATCH 01/10] #110-implement class BaseMELG Completed. - modified name of one annotation type, extended one type also. - added a parameter to base random protected method _rot_left. - fixed erroneous naming for WELL algorithm. - enhanced a fex docstrings. --- PyRandLib/annotation_types.py | 10 +- PyRandLib/basecwg.py | 4 +- PyRandLib/basemelg.py | 206 ++++++++++++++++++++++++++++++++++ PyRandLib/baserandom.py | 10 +- PyRandLib/basewell.py | 6 +- PyRandLib/splitmix.py | 10 +- PyRandLib/xoroshiro1024.py | 2 +- 7 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 PyRandLib/basemelg.py 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..c9d5855 --- /dev/null +++ b/PyRandLib/basemelg.py @@ -0,0 +1,206 @@ +""" +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 WELL 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. + + 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. + See reference [11] in README.md. + + 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^19937, i.e. 4.32e+6001), + same computation time and equivalent of 626 integers memory consumption. + See Melg44497 for a very large period (2^44497, i.e. 15.1e+13466) 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 | 626 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. + """ + + #------------------------------------------------------------------------- + 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 integers and an index in this list (index value + being then in range(0,self._STATE_SIZE)). Should _seedState be + a sole integer or float then it is used as initial seed for the + random filling of the internal list of self._STATE_SIZE integers. + 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 integers and an index in + this list (index value being then in range(0,self._STATE_SIZE). + """ + return (self._state[:], self._index, self._extState) + + + #------------------------------------------------------------------------- + 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() + self._initext() + + elif count == 1: + self._index = 0 + self._initstate( _seedState[0] ) + self._initext() + + 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] ) + self._initext() + + 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] ) + self._initext( _seedState[2] ) + + except: + self._index = 0 + self._initstate( _seedState ) + self._initext() + + + #------------------------------------------------------------------------- + def _initext(self, _ext: int = None) -> None: + """Inits the internal state extension. + + Notice: if _ext is None, this method MUST NOT be called before + attributes _index and _state have been initialized. + """ + try: + self._extState = int( _ext ) # Notice: raises exception if _ext is None + except: + lastState = self._state[-1] + self._extState = (0x5851_f42d_4c95_7f2d * (lastState ^ (lastState >> 62)) + self._index) + + self._extState &= 0xffff_ffff_ffff_ffff + + + #------------------------------------------------------------------------- + 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/baserandom.py b/PyRandLib/baserandom.py index e72e4e6..7a98f60 100644 --- a/PyRandLib/baserandom.py +++ b/PyRandLib/baserandom.py @@ -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/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/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 From e21052db32083dd0a2fcc96def9ddc99925c4e78 Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 10:02:17 +0100 Subject: [PATCH 02/10] #110-implement class BaseMELG Enhanced docstrings --- PyRandLib/basemelg.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/PyRandLib/basemelg.py b/PyRandLib/basemelg.py index c9d5855..baaa30f 100644 --- a/PyRandLib/basemelg.py +++ b/PyRandLib/basemelg.py @@ -28,7 +28,7 @@ #============================================================================= class BaseMELG( BaseRandom ): - """Definition of the base class for all WELL pseudo-random generators. + """Definition of the base class for all MELG pseudo-random generators. This module is part of library PyRandLib. @@ -36,13 +36,12 @@ class BaseMELG( BaseRandom ): 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. + 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. - See reference [11] in README.md. 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. @@ -50,13 +49,13 @@ class BaseMELG( BaseRandom ): 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^19937, i.e. 4.32e+6001), + 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^44497, i.e. 15.1e+13466) with similar + 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. + 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 @@ -64,12 +63,12 @@ class BaseMELG( BaseRandom ): 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 + 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. + 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 | | --------------- | ------------------- | --------------- | ------- | ----------- | ------------ | ---------------- | ----------- | -------------- | @@ -89,14 +88,14 @@ class BaseMELG( BaseRandom ): 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 integers and an index in this list (index value - being then in range(0,self._STATE_SIZE)). Should _seedState be - a sole integer or float then it is used as initial seed for the - random filling of the internal list of self._STATE_SIZE integers. - Should _seedState be anything else (e.g. None) then the shuffling - of the local current time value is used as such an initial seed. + _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 ) @@ -109,9 +108,9 @@ def __init__(self, _seedState: SeedStateType = None) -> None: 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 integers and an index in - this list (index value being then in range(0,self._STATE_SIZE). + 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, self._extState) From c4fbb0fd277ecd4a2d56d7b006159d6b6900b36f Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 17:08:49 +0100 Subject: [PATCH 03/10] #111-implement class Melg607 Completed. Validated. --- PyRandLib/__init__.py | 2 + PyRandLib/basemelg.py | 38 +++++------- PyRandLib/baserandom.py | 2 +- PyRandLib/melg607.py | 126 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 23 deletions(-) create mode 100644 PyRandLib/melg607.py diff --git a/PyRandLib/__init__.py b/PyRandLib/__init__.py index 6f4aac8..11028d7 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,6 +24,7 @@ from .lfib116 import LFib116 from .lfib668 import LFib668 from .lfib1340 import LFib1340 +from .melg607 import Melg607 from .mrgrand287 import MRGRand287 from .mrgrand1457 import MRGRand1457 from .mrgrand49507 import MRGRand49507 diff --git a/PyRandLib/basemelg.py b/PyRandLib/basemelg.py index baaa30f..0fa6bc1 100644 --- a/PyRandLib/basemelg.py +++ b/PyRandLib/basemelg.py @@ -83,6 +83,21 @@ class BaseMELG( BaseRandom ): * _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: @@ -112,7 +127,7 @@ def getstate(self) -> StateType: 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, self._extState) + return (self._state[:], self._index) #------------------------------------------------------------------------- @@ -133,12 +148,10 @@ def setstate(self, _seedState: StateType) -> None: if count == 0: self._index = 0 self._initstate() - self._initext() elif count == 1: self._index = 0 self._initstate( _seedState[0] ) - self._initext() elif count == 2: self._initindex( _seedState[1] ) @@ -146,7 +159,6 @@ def setstate(self, _seedState: StateType) -> None: self._state = _seedState[0][:] # each entry in _seedState MUST be integer else: self._initstate( _seedState[0] ) - self._initext() else: self._initindex( _seedState[1] ) @@ -154,28 +166,10 @@ def setstate(self, _seedState: StateType) -> None: self._state = _seedState[0][:] # each entry in _seedState MUST be integer else: self._initstate( _seedState[0] ) - self._initext( _seedState[2] ) except: self._index = 0 self._initstate( _seedState ) - self._initext() - - - #------------------------------------------------------------------------- - def _initext(self, _ext: int = None) -> None: - """Inits the internal state extension. - - Notice: if _ext is None, this method MUST NOT be called before - attributes _index and _state have been initialized. - """ - try: - self._extState = int( _ext ) # Notice: raises exception if _ext is None - except: - lastState = self._state[-1] - self._extState = (0x5851_f42d_4c95_7f2d * (lastState ^ (lastState >> 62)) + self._index) - - self._extState &= 0xffff_ffff_ffff_ffff #------------------------------------------------------------------------- diff --git a/PyRandLib/baserandom.py b/PyRandLib/baserandom.py index 7a98f60..b1173f1 100644 --- a/PyRandLib/baserandom.py +++ b/PyRandLib/baserandom.py @@ -372,7 +372,7 @@ def _rotleft(cls, _value: int, _rotCount: int, _bitsCount: int = 64) -> int: """ #assert 1 <=_rotCount <= _bitsCount loMask = (1 << (_bitsCount - _rotCount)) - 1 - hiMask = ((1 << _bitsCount - 1) ^ loMask + hiMask = ((1 << _bitsCount) - 1) ^ loMask hiBits = (_value & hiMask) >> (_bitsCount - _rotCount) return ((_value & loMask) << _rotCount) | hiBits diff --git a/PyRandLib/melg607.py b/PyRandLib/melg607.py new file mode 100644 index 0000000..3314a57 --- /dev/null +++ b/PyRandLib/melg607.py @@ -0,0 +1,126 @@ +""" +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 +from .annotation_types import SeedStateType, StateType +from .splitmix import SplitMix64 + + +#============================================================================= +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^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]. + + 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 = 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 + - 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 | 626 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' constant, size of the internal state + _STATE_SIZE = 10 # the internal state of this PRNG is set on ten 64-bits integers + + # 'protected' masks values + _MASK_HIGH = 0xffff_ffff_8000_0000 + _MASK_LOW = 0x0000_0000_7fff_ffff + _A_COND = (0, 0x81f1_fd68_0123_48bc) # 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) % 9 + self._index = i_1 + + s9 = self._state[9] + + x = (self._state[i] & self._MASK_HIGH) | (self._state[i_1] & self._MASK_LOW) # 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)) + self._state[9] = s9 + + si = self._state[i] = x ^ (s9 ^ (s9 >> 35)) + return (si ^ ((si << 30) & 0xffff_ffff_ffff_ffff)) ^ ((self._state[(i + 3) % 9]) & 0x66ed_c62a_6bf8_c826) + + +#===== end of module melg607.py ======================================== From 676732e1467a1cacad33456b2a66f14909799b1a Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 21:25:29 +0100 Subject: [PATCH 04/10] #112-implement class Melg19937 Completed. Validated. --- PyRandLib/__init__.py | 1 + PyRandLib/melg19937.py | 121 +++++++++++++++++++++++++++++++++++++++++ PyRandLib/melg607.py | 26 ++++----- 3 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 PyRandLib/melg19937.py diff --git a/PyRandLib/__init__.py b/PyRandLib/__init__.py index 11028d7..89cdce4 100644 --- a/PyRandLib/__init__.py +++ b/PyRandLib/__init__.py @@ -25,6 +25,7 @@ from .lfib668 import LFib668 from .lfib1340 import LFib1340 from .melg607 import Melg607 +from .melg19937 import Melg19937 from .mrgrand287 import MRGRand287 from .mrgrand1457 import MRGRand1457 from .mrgrand49507 import MRGRand49507 diff --git a/PyRandLib/melg19937.py b/PyRandLib/melg19937.py new file mode 100644 index 0000000..9bfac25 --- /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 = 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 = 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 + - 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 | 626 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/melg607.py b/PyRandLib/melg607.py index 3314a57..79a3211 100644 --- a/PyRandLib/melg607.py +++ b/PyRandLib/melg607.py @@ -22,8 +22,6 @@ #============================================================================= from .basemelg import BaseMELG -from .annotation_types import SeedStateType, StateType -from .splitmix import SplitMix64 #============================================================================= @@ -54,10 +52,10 @@ class Melg607( BaseMELG ): 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^19,937, i.e. 4.32e+6001), + 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 + 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: @@ -94,13 +92,9 @@ class Melg607( BaseMELG ): """ #------------------------------------------------------------------------- - # 'protected' constant, size of the internal state - _STATE_SIZE = 10 # the internal state of this PRNG is set on ten 64-bits integers - - # 'protected' masks values - _MASK_HIGH = 0xffff_ffff_8000_0000 - _MASK_LOW = 0x0000_0000_7fff_ffff - _A_COND = (0, 0x81f1_fd68_0123_48bc) # this tuple will avoid an 'if' in method 'next()' + # '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... #------------------------------------------------------------------------- @@ -115,12 +109,12 @@ def next(self) -> int: s9 = self._state[9] - x = (self._state[i] & self._MASK_HIGH) | (self._state[i_1] & self._MASK_LOW) # 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)) + 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)) - return (si ^ ((si << 30) & 0xffff_ffff_ffff_ffff)) ^ ((self._state[(i + 3) % 9]) & 0x66ed_c62a_6bf8_c826) + 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 ======================================== From 0be93ae47ec10d4259818fc75e6b089979580d31 Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 21:51:49 +0100 Subject: [PATCH 05/10] #113-implement class Melg44497 Completed. Validated. --- PyRandLib/__init__.py | 1 + PyRandLib/melg19937.py | 4 +- PyRandLib/melg44497.py | 120 +++++++++++++++++++++++++++++++++++++++++ PyRandLib/melg607.py | 2 +- 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 PyRandLib/melg44497.py diff --git a/PyRandLib/__init__.py b/PyRandLib/__init__.py index 89cdce4..137b471 100644 --- a/PyRandLib/__init__.py +++ b/PyRandLib/__init__.py @@ -26,6 +26,7 @@ from .lfib1340 import LFib1340 from .melg607 import Melg607 from .melg19937 import Melg19937 +from .melg44497 import Melg44497 from .mrgrand287 import MRGRand287 from .mrgrand1457 import MRGRand1457 from .mrgrand49507 import MRGRand49507 diff --git a/PyRandLib/melg19937.py b/PyRandLib/melg19937.py index 9bfac25..306a535 100644 --- a/PyRandLib/melg19937.py +++ b/PyRandLib/melg19937.py @@ -60,13 +60,13 @@ class Melg19937( BaseMELG ): integers). This is the longest period version proposed in paper [11]. Furthermore, this class is callable: - rand = Melg607() + 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 = Well1024a() + 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 diff --git a/PyRandLib/melg44497.py b/PyRandLib/melg44497.py new file mode 100644 index 0000000..26419d2 --- /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 | 626 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 index 79a3211..0cef1a5 100644 --- a/PyRandLib/melg607.py +++ b/PyRandLib/melg607.py @@ -65,7 +65,7 @@ class Melg607( BaseMELG ): 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 = Well1024a() + 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 From 8dd2b78f7265cf21ce781f8ec4f9735a40948cfb Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 21:54:08 +0100 Subject: [PATCH 06/10] #61-implement MELG algorithm Completed. Validated. --- testCPUPerfs.py | 8 ++++++-- testED.py | 49 ++++++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/testCPUPerfs.py b/testCPUPerfs.py index 3b801ac..5a70eab 100644 --- a/testCPUPerfs.py +++ b/testCPUPerfs.py @@ -58,6 +58,9 @@ 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("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("MRGRand287" , 0x3ca5_8796 , 100_000, N) test_perf("MRGRand1457" , 0x3ca5_8796 , 100_000, N) test_perf("MRGRand49507" , 0x3ca5_8796 , 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..97657c8 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(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(), 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) From 212fd76aab837ae93e654be33b6c92adf177d09a Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 21:56:04 +0100 Subject: [PATCH 07/10] #34-implement Well algorithm Enhanced docstrings content. --- PyRandLib/well1024a.py | 11 +++++------ PyRandLib/well19937c.py | 10 +++++----- PyRandLib/well44497b.py | 10 +++++----- PyRandLib/well512a.py | 10 +++++----- 4 files changed, 20 insertions(+), 21 deletions(-) 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. From f69e7d63c09d903cab6f4ee71b75d5ddb33d1934 Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 23:17:24 +0100 Subject: [PATCH 08/10] #80-add MELG short documentation in README.md Completed. --- README.md | 91 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index e2335bc..f46835a 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,6 +393,30 @@ Please notice that the TestUO1 article states that the operator should be '*' wh +### 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 + + + +### 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 + + + ### MRGRand287 - 2^287 periodicity **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. @@ -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 From 94174b0b9de75e80d6f5bf6cbaff3a75cf71e018 Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 23:19:43 +0100 Subject: [PATCH 09/10] #61-implement MELG algorithm Fixed one error on one number in docstring. --- PyRandLib/basemelg.py | 2 +- PyRandLib/melg19937.py | 2 +- PyRandLib/melg44497.py | 2 +- PyRandLib/melg607.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PyRandLib/basemelg.py b/PyRandLib/basemelg.py index 0fa6bc1..e69665b 100644 --- a/PyRandLib/basemelg.py +++ b/PyRandLib/basemelg.py @@ -73,7 +73,7 @@ class BaseMELG( BaseRandom ): | 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 | 626 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | 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 diff --git a/PyRandLib/melg19937.py b/PyRandLib/melg19937.py index 306a535..cb34b3e 100644 --- a/PyRandLib/melg19937.py +++ b/PyRandLib/melg19937.py @@ -81,7 +81,7 @@ class Melg19937( BaseMELG ): | 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 | 626 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | 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 diff --git a/PyRandLib/melg44497.py b/PyRandLib/melg44497.py index 26419d2..2b3875a 100644 --- a/PyRandLib/melg44497.py +++ b/PyRandLib/melg44497.py @@ -80,7 +80,7 @@ class Melg44497( BaseMELG ): | 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 | 626 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | 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 diff --git a/PyRandLib/melg607.py b/PyRandLib/melg607.py index 0cef1a5..01691b8 100644 --- a/PyRandLib/melg607.py +++ b/PyRandLib/melg607.py @@ -80,7 +80,7 @@ class Melg607( BaseMELG ): | 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 | 626 x 4-bytes | 2^19937 | n.a. | 4.21 | 0 | 0 | 0 | + | 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 From e0ae4b1a3f13fdd33deb154b31459c6272cb028d Mon Sep 17 00:00:00 2001 From: Philippe Schmouker Date: Mon, 3 Mar 2025 23:34:17 +0100 Subject: [PATCH 10/10] #115-rename MRG classes Completed. Validated. --- PyRandLib/__init__.py | 6 ++--- PyRandLib/basemrg.py | 18 +++++++-------- PyRandLib/baserandom.py | 10 ++++----- PyRandLib/{mrgrand1457.py => mrg1457.py} | 20 ++++++++--------- PyRandLib/{mrgrand287.py => mrg287.py} | 26 +++++++++++----------- PyRandLib/{mrgrand49507.py => mrg49507.py} | 24 ++++++++++---------- README.md | 14 ++++++------ testCPUPerfs.py | 6 ++--- testED.py | 6 ++--- 9 files changed, 65 insertions(+), 65 deletions(-) rename PyRandLib/{mrgrand1457.py => mrg1457.py} (92%) rename PyRandLib/{mrgrand287.py => mrg287.py} (90%) rename PyRandLib/{mrgrand49507.py => mrg49507.py} (90%) diff --git a/PyRandLib/__init__.py b/PyRandLib/__init__.py index 137b471..bd75909 100644 --- a/PyRandLib/__init__.py +++ b/PyRandLib/__init__.py @@ -27,9 +27,9 @@ from .melg607 import Melg607 from .melg19937 import Melg19937 from .melg44497 import Melg44497 -from .mrgrand287 import MRGRand287 -from .mrgrand1457 import MRGRand1457 -from .mrgrand49507 import MRGRand49507 +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/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 b1173f1..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). 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/README.md b/README.md index f46835a..ab90036 100644 --- a/README.md +++ b/README.md @@ -417,9 +417,9 @@ The base MELG algorithm offers large to very large periods with the best known r -### MRGRand287 - 2^287 periodicity +### Mrg287 - 2^287 periodicity -**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. +**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: @@ -441,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: @@ -457,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: diff --git a/testCPUPerfs.py b/testCPUPerfs.py index 5a70eab..2c7b532 100644 --- a/testCPUPerfs.py +++ b/testCPUPerfs.py @@ -61,9 +61,9 @@ def test_perf(prng_class_name: str, seed_value: int, n_loops: int, n_repeats: in 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("MRGRand287" , 0x3ca5_8796 , 100_000, N) - test_perf("MRGRand1457" , 0x3ca5_8796 , 100_000, N) - test_perf("MRGRand49507" , 0x3ca5_8796 , 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) diff --git a/testED.py b/testED.py index 97657c8..2a081d3 100644 --- a/testED.py +++ b/testED.py @@ -121,9 +121,9 @@ def test_algo(rnd_algo, nb_entries: int = 1_000, nb_loops: int = 1_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(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(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)