MsWsII PRNG plus Help file

General FreeBASIC programming questions.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

MsWsII PRNG plus Help file

Post by deltarho[1859] »

This project was motivated by speedfixer's disquiet about fbmath.bi; now called fbprng.bi. Actually, this project has been on my mind for some time, and speedfixer pushed me over the edge. So, if you are saying “Oh no, not another PRNG” then blame speedfixer and not me. :)

Most generators have just one sequence. All of FreeBASIC's generators are single sequenced. Most of my implementations have just one sequence, except for PCG32II which has 2^63 sequences. The MsWsII package comes with 16,384 sequences. More to the point, it comes with 16,384 constants which are used in the generator's engines; one for the 32-bit engine and two for the 64-bit engine. Yes, MsWsII has two engines. MsWs uses Widynski's Version 3. MsWsII uses Widynski's Version 4, published three weeks ago, when the 64-bit engine was introduced. The 32-bit engine remains as was. The constants are in a bi file, Mysvalues.bi, loaded by MsWsII.bas. I did that rather than have the constants hard-wired into MsWsII.bas so that you can crop the 16384 if needs be.

Rather than post the 16384 constants, here is a link to a zipped file which contains Mysvaluesbi, which is 340KB unzipped. 340KB may seem large, but it gets loaded into MsWsII.bas from a hard drive in the blink of an eye. MsWsII.zip is a zip of the help file.

Mysvalues.zip
MsWsIIHelp.zip

MsWsII.bas is small – just less than 350 lines of code.

The Help file is small but covers all aspects of WsMsII. In fact, it is only 32.9KB. FB's Help file is 2.19MB.

Most of us find reading Help files tedious, but MsWsII.chm is essential reading if you want to get the most out of MsWsII. Some of you may find the MsWsII source code intimidating. If it is any consolation, so do I and I wrote it and why MsWsII.chm was written. Fortunately I don't have to read the Help file. :D

I am fairy confident that MsWsII is bug free. I had very few during development. I am not into bugs – I find that they slow me down. :)

If, like speedfixer, you find fbprng.bi a bit of a pain, then just ignore it and use MsWsII.

So with all the PRNG implementations that I have been involved in, which one will I use? Well, I will use MsWsII - I don't have a compelling reason not to. OK, I may have one or two procedures in other implementations that may be faster, but they are not significantly faster, and I doubt that their use will have any influence on my applications or yours. MsWs Version 4 is a gem and well done Bernard Widynski. :!:

Getting started:

1) Download Mysvalues.zip and unzip.
2) Put MsWsII.bas and Msvalues.bi into your application folder.
3) At the head of your application type #Include Once "MsWsII.bas"
On running your application, MsWsII.bas will load Mysvalues.bi and the generator will be available for use.
4) Download MsWsII.zip and unzip. You now have the Help file.

Many topics in the Help file have code snippets. These are meant to be run. You will get up to speed on MsWsII much quicker by running them.

MsWsII.bas

Code: Select all

' MsWsII.bas Version 1.6

#Include Once "Mysvalues.bi"
#Include Once "file.bi"

#define dodrange(f,l) Int(Rnd*(((l)+1)-(f))+(f)) ' By dodicat
#Ifndef Uint32
  #define Uint32 UInteger<32>
#EndIf
#Ifndef Uint16
  #define Uint16 UInteger<16>
#EndIf
#Ifndef Uint64
  #define Uint64 UInteger<64>
#EndIf
#Ifndef Int32
  #define Int32 Integer<32>
#EndIf

#Macro Engine1
  Dim As Uint32 Engine1Result
  This.Seed *= This.Seed : This.Sequence += This.svalue(0) : This.Seed += This.Sequence
  This.Seed = ( This.Seed Shr 32 ) Or ( This.Seed Shl 32 )
  Engine1Result = This.Seed
#EndMacro

#Macro Engine2
  Dim As Uint64 Engine2Result
  This.Seed1 *= This.Seed1
  This.Sequence1 += This.svalue(1)
  This.Seed1 += This.Sequence1
  Engine2Result = This.Seed1
  This.Seed1 = ( This.Seed1 Shr 32 ) Or ( This.Seed1 Shl 32 )
  This.Seed2 *= This.Seed2
  This.Sequence2 += This.svalue(2)
  This.Seed2 += This.Sequence2
  This.Seed2 = ( This.Seed2 Shr 32 ) Or ( This.Seed2 Shl 32 )
  Engine2Result Xor= This.Seed2
#EndMacro

Type MsWsParams
  Public:
  Declare Constructor( As Boolean = FALSE )
  Declare Sub Setup()
  Declare Sub Reboot()
  Declare Sub Initialize()
  Declare Function rand() As Uint32
  Declare Function rand64() As Uint64
  Declare Function randse() As Double
  Declare Function randd() As Double
  Declare Function range OverLoad ( First As Double, Last As Double ) As Double
  Declare Function range OverLoad ( First As Int32, Last As Int32 ) As Int32
  Declare Sub GetEngine1Snapshot()
  Declare Sub SetEngine1SnapShot()
  Declare Sub GetEngine2Snapshot()
  Declare Sub SetEngine2SnapShot()
  Declare Sub RestoreEngine1InitialState()
  Declare Sub RestoreEngine2InitialState()
  Declare Sub RestoreAllInitialState()
  Declare Sub SaveMetrics( As String )
  Declare Sub LoadMetrics( As String )
  Declare Function Gauss() As Double
  'Declare Sub DumpState() ' Used during development
  Private:
  Declare Sub GetInitialState()
  Declare Sub ClearDecks()
  As Uint64 SnapStateVector(0 To 5)
  As Uint64 InitialState(0 To 5)
  ' State vectors
  Seed As Uint64
  Sequence As Uint64
  Seed1 As Uint64
  Seed2 As Uint64
  Sequence1 As Uint64
  Sequence2 As Uint64
  As Uint64 svalue(0 To 2)
  Cache As Boolean
End Type

' ***********************************
' Seeding functions
Function HammingSeed64() As Uint64
Dim As Uint32 tscSeed0, tscSeed1, numBits
Dim As Uint64 Seed, CopySeed
  
  While Not ((numBits > 30) And (numBits < 34)) ' Gd quality entropy - we don't need exactly 32
    Asm
      rdtsc
      mov ecx, eax
      bswap eax ' fast moving to upper bits
      mov Dword Ptr [tscSeed0], eax
      Add ecx, 1073741824 ' A quarter of a spin
      bswap ecx ' fast moving to upper bits
      mov Dword Ptr [tscSeed1], ecx
    End Asm
    Seed = (Cast( Uint64, tscSeed0 ) Shl 32) Or Cast( Uint64, tscSeed1 ) 
    CopySeed = Seed : numBits = 0
    While CopySeed <> 0
      CopySeed = CopySeed And CopySeed - 1
      numBits += 1
    Wend
  Wend
  Return Seed
End Function

Sub ShuffleUint64( ByRef x As Uint64 )
  Randomize , 5 ' For dodrange
  Union localUDT
    As Uint64 ul
    As Byte b(7)
  End Union
  Dim As localUDT l
  l.ul = x
  For i As Uint32 = 0 To 6
    Swap l.b(i), l.b(dodrange(i, 7))
  Next
  x = l.ul ' <- without this x will not change
  ' Restore FB's default
  Randomize , 3
End Sub
' ***********************************

' 32-bit random number
Function MsWsParams.rand() As Uint32
  Engine1
  Return Engine1Result
End Function

' 64-bit random number
Function MsWsParams.rand64() As Uint64
  Engine2
  Return Engine2Result
End Function

' 32-bit granularity [0,1)
Private Function MsWsParams.randse() As Double
  Engine1
  Return Engine1Result/2^32
End Function

' 53-bit granularity [0,1)
 Private Function MsWsParams.randd() As Double
    Engine2
    Return (Engine2Result Shr 11)/2^53
  End Function

' Floating point range
Private Function MsWsParams.range( First As Double, Last As Double ) As Double
  Engine1
  Function = Engine1Result/2^32 * ( Last - First ) + First 
End Function

' Integral range - Int32 (Long)
Private Function MsWsParams.range(First As Int32, Last As Int32) As Int32
  Engine1
  Return CLng( Engine1Result Mod (Last-First+1)) + First
End Function

' Normal distribution
Private Function MsWsParams.Gauss As Double
Static As Double u1, u2, x1, x2, w
  If This.Cache = TRUE Then
    This.Cache = FALSE
    Function = u2
  Else
    Do
      x1 = This.randse
      x2 = This.randse
      w = x1 * x1 + x2 * x2
    Loop While w >= 1
    w = Sqr( -2 * Log(w)/w )
    u1 = x1 * w
    u2 = x2 * w
    This.Cache = TRUE
    Function = u1
  End If
End Function

Private Sub MsWsParams.GetEngine1Snapshot( )
  If This.Cache Then This.Gauss
  snapStateVector(0) = Seed
  snapStateVector(3) = Sequence
End Sub

Private Sub MsWsParams.SetEngine1Snapshot( )
  This.Cache = FALSE
  If snapStateVector(0)=0 AndAlso snapStateVector(3)=0 Then
    ' ie a GetSnapshot has not been executed so use InitialState()
    Seed = InitialState(0)
    Sequence = InitialState(3)
  Else
    Seed = SnapStateVector(0)
    Sequence = SnapStateVector(3)
  End If
End Sub

Private Sub MsWsParams.GetEngine2Snapshot( )
  snapStateVector(1) = Seed1
  snapStateVector(2) = Seed2
  snapStateVector(4) = Sequence1
  snapStateVector(5) = Sequence2
End Sub

Private Sub MsWsParams.SetEngine2Snapshot( )
  If snapStateVector(1)=0  AndAlso snapStateVector(2)=0 AndAlso snapStateVector(4)=0  AndAlso snapStateVector(5)=0  Then
    Seed1 = InitialState(1)
    Seed2 = InitialState(2)
    Sequence1 = InitialState(4)
    Sequence2 = InitialState(5)
  Else
    Seed1 = SnapStateVector(1)
    Seed2 = SnapStateVector(2)
    Sequence1 = SnapStateVector(4)
    Sequence2 = SnapStateVector(5)
  End If
End Sub

Sub MsWsParams.GetInitialState( )
  InitialState(0) = Seed
  InitialState(1) = Seed1
  InitialState(2) = Seed2
  InitialState(3) = Sequence
  InitialState(4) = Sequence1
  InitialState(5) = Sequence2
End Sub

Private Sub MsWsParams.RestoreEngine1InitialState( )
  This.Cache = FALSE
  Seed = InitialState(0)
  Sequence = InitialState(3)
End Sub

Private Sub MsWsParams.RestoreEngine2InitialState( )
  Seed1 = InitialState(1)
  Seed2 = InitialState(2)
  Sequence1 = InitialState(4)
  Sequence2 = InitialState(5)
End Sub

Private Sub MsWsParams.RestoreAllInitialState()
    RestoreEngine1InitialState
    RestoreEngine2InitialState
End Sub

' Random seeding for all
Sub MsWsParams.Initialize( )
  ' For Engine1
  Seed = HammingSeed64 : ShuffleUint64( Seed )
  ' For Engine2
  Seed1  = HammingSeed64 : ShuffleUint64( Seed1 )
  Seed2 = HammingSeed64 : ShuffleUint64( Seed2 )
  ' For Engine1
  Sequence = HammingSeed64 : ShuffleUint64( Sequence )
  ' For Engine2
  Sequence1 = HammingSeed64 : ShuffleUint64( Sequence1 )
  Sequence2 = HammingSeed64 : ShuffleUint64( Sequence2 )
End Sub

Constructor MsWsParams( SkipSetup As Boolean = FALSE )
  If SkipSetUp = FALSE Then Setup
End Constructor

Sub MsWsParams.Setup()
  Dim As Uint32 ubnd
  Randomize , 5 ' For dodrange
  ubnd = UBound( Mysvalues )
  Swap Mysvalues( 0 ), Mysvalues( dodrange( 0, ubnd ) )
  Swap Mysvalues( 1 ), Mysvalues( dodrange( 1, ubnd ) )
  Swap Mysvalues( 2 ),Mysvalues( dodrange( 2, ubnd ) )
  This.svalue(0) = Mysvalues( 0 )
  This.svalue(1) = Mysvalues( 1 )
  This.svalue(2) = Mysvalues( 2 )
  Initialize
  GetInitialState
  ' Restore FB's default
  Randomize , 3
End Sub

Private Sub MsWsParams.ClearDecks
  Erase This.SnapStateVector
  This.Cache = FALSE
End Sub

Private Sub MsWsParams.Reboot
Dim As Uint32 ubnd, first, Second, third
Static count As Uint16 = 3
  Randomize , 5 ' For dodrange
  ubnd = UBound( Mysvalues )
  Swap Mysvalues( count ), Mysvalues( dodrange( count, ubnd ) )
  Swap Mysvalues( count + 1 ), Mysvalues( dodrange( count + 1, ubnd ) )
  Swap Mysvalues( count + 2 ),Mysvalues( dodrange( count + 2, ubnd ) )
  This.svalue(0) = Mysvalues( count )
  This.svalue(1) = Mysvalues( count + 1 )
  This.svalue(2) = Mysvalues( count  + 2 )
  count += 3
  ClearDecks
  Initialize
  GetInitialState
  ' Restore FB's default
  Randomize , 3
End Sub

Private Sub MsWsParams.SaveMetrics( filename As String )
Dim As Long f = FreeFile
  If FileExists( filename ) Then Kill ( filename )
  Open filename For Binary As #f
    Put #f, , this '  by dodicat
  Close #f
End Sub

Private Sub MsWsParams.LoadMetrics( filename As String )
Dim As Long f = FreeFile
  Open filename For Binary As #f
    Get #f, , this '  by dodicat
  Close #f
End Sub

' Used during development 
'Private Sub MsWsParams.DumpState
'   Print Hex(This.Seed,16);" ";Bin(This.Seed,64)
'   Print Hex(This.Seed1,16);" ";Bin(This.Seed1,64)
'   Print Hex(This.Seed2,16);" ";Bin(This.Seed2,64)
'   Print Hex(This.Sequence,16);" ";Bin(This.Sequence,64)
'   Print Hex(This.Sequence1,16);" ";Bin(This.Sequence1,64)
'   Print Hex(This.Sequence2,16);" ";Bin(This.Sequence2,64)
'End Sub

Dim Shared As MsWsParams msws
#undef Rnd
#define Rnd msws.randse
#define RndD msws.randd
#define Range_ msws.range
Last edited by deltarho[1859] on May 23, 2022 21:44, edited 23 times in total.
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: MsWsII PRNG plus Help file

Post by srvaldez »

deltarho[1859] wrote: Apr 12, 2022 12:50 I use Mixed Case in WinFBE but if I forget to use the shift key lower case gets copied as lower case even though in WinFBE lower case is displayed
hi deltarho[1859]
I use a plugin for geany IDE called FBeauty https://github.com/DTJF/FBeauty see also viewtopic.php?t=16701&start=15
in geany IDE select-all then toggle-case to make it all lower-case then press Ctrl-1 and you are done
[edit]
if you do the steps I just mentioned then the FB keywords will be capitalized or ucased depending on the switch that you pass to FBeauty but the rest will be lower-case
[edit2]
where can I get "Mysvalues.bi" ?
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

Hi srvaldez

I don't use Linux.
where can I get "Mysvalues.bi" ?
Oops :!:

In the zip file is "Mysvalues16384.bi". Rename that to "Mysvalues.bi"

I had an idea at the last minute, changed my mind, but did not backtrack far enough. :oops:

I have renamed the bi file in the zip and uploaded it to my server. That way, I don't need to edit MsWsII.bas or the Help file.

Thank you. :)
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: MsWsII PRNG plus Help file

Post by srvaldez »

thank you :D
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

I referred to "Mysvalues16384.bi" in the opening post and have just corrected that. :roll:

Lesson to be learned: Don't make last-minute changes – a minute is not long enough. :lol:
speedfixer
Posts: 606
Joined: Nov 28, 2012 1:27
Location: CA, USA moving to WA, USA
Contact:

Re: MsWsII PRNG plus Help file

Post by speedfixer »

I would like to take all the credit - uh, blame - for your excellent work.

But it is your work. I am happy to have tickled another superior generator from your keyboard to our forum. As always, an interesting read. Again, I think you give yourself too litlle credit, and assume far less general interest than is true.

At least, all that is needed is in one place without having to dig through all the still incomplete references in the wiki. That was all I was asking for. I am sad that too many are too quick to defend status quo and too slow to hear the message of the OP. Guess that will never change.

It will be a bit before I can run my own validations and add my personal front-ends to use this.

Thank you.


speedfixer == david
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

Thanks, speedfixer - your cheque is in the post. :wink:
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: MsWsII PRNG plus Help file

Post by fxm »

speedfixer wrote: Apr 12, 2022 16:52 At least, all that is needed is in one place without having to dig through all the still incomplete references in the wiki. That was all I was asking for. I am sad that too many are too quick to defend status quo and too slow to hear the message of the OP. Guess that will never change.

I already proposed below to add an example (in the RND documentation page) to illustrate the use of the other PRNGs available in fbmath.bi (future fbprng.bi).
The use being similar for each of these 5 PRNGs:
fxm wrote: Apr 08, 2022 21:37 In 'fbmath.bi', 5 other PRNGs are defined (each producing either a 'ulong' or a 'double').

Their uses is trivial:
- Declare an instance of the chosen PRNG type.
- Call on this instance the one of 2 member functions repeatedly (returning either a 'ulong', or a 'double' in [0,1)).

Example for the fifth PRNG and 'double':

Code: Select all

#include "fbmath.bi"

Dim As FB.Rndxoroshiro128 r128
For I As Integer = 1 To 20
    Print r128.rnd()
Next I

Sleep
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

I have not looked at 'fbmath.bi' in detail because I am not interested.

Having said that, I have just run the code in your last post, fxm.

It will always return the same sequence because it uses a fixed seed.

I am not going to waste my time studying 'fbmath.bi' to see how we can seed it, but you should have done. Erm, you do know how to seed it, don't you?
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: MsWsII PRNG plus Help file

Post by fxm »

Presently, there is no seed algorithm.
These 5 PRNGs, as is, are unfinished.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

fxm wrote:Presently, there is no seed algorithm.
When I read that, I very nearly wet myself laughing. I should imagine that speedfixer is in a sorry state as well.

If anyone wants to use xoroshiro128 they can always pop over to this
post. Let me see who wrote that. Oh, I did. My memory is getting worse. :twisted:

'fbmath.bi' ('fbprng.bi') needs to be put out of its misery. It is outrageous being allowed to suffer so. :(
adeyblue
Posts: 299
Joined: Nov 07, 2019 20:08

Re: MsWsII PRNG plus Help file

Post by adeyblue »

speedfixer wrote: Apr 12, 2022 16:52 At least, all that is needed is in one place
Considering this needs two files to work, and they live in different places, that is by definition not in one place. Whenever his site and/or the forum is inaccessible - well, you won't get very many random numbers with only half the implementation.

The bas amounts to ~8K though, would seem less hassle to just tack that onto the end of the other 340k and have it be self contained in one file. It's not like downloading the bits from his site is optional, so stick it all in that one package and be done.

----

The help file is very del though. Big on the fascinating theory to those who like that sort of thing, but a bit lacking in the practicals.
It doesn't answer basic questions like:
Are the two engines independent? If so, why are they smushed together into one object? If not, what happens if I reset one and not the other?

The GetDuo page mentions it uses engine 2, that's cool. The reset sequences page mention RndD is also 2, ok. What about Rnd? Is that 1 or 2?

Why is GetSnapshot called that if my code doesn't get anything from it, like it does with GetDuo? Seems inconsistent, unless it does return something and all the examples throw it away?

What happens if I GetSnapshot once and Set twice? Is that ok or is it only one set for one get? That's all the example does and nothing says otherwise.

Also, as a suggestion, couldn't you have a snapshot UDT that your snapshot functions fill in and restore from? Then I can have as many snapshots as I want rather than just the one. At least, I presume it's one and not say a stack or something anyway? Who knows?

What's the range for range? Your example says "Integral range (10,20)" - does that mean both exclusive, or is that just the FB syntax for the call?
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: MsWsII PRNG plus Help file

Post by fxm »

deltarho[1859] wrote: Apr 12, 2022 20:32 'fbmath.bi' ('fbprng.bi') needs to be put out of its misery. It is outrageous being allowed to suffer so. :(
I too often suffer when I read a number of codes on this forum, but I don't say anything so as not to be only critical and not constructive.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

Some good points there, adeyblue.

I laboured for some time on whether to hard-wire the constants or have them in a file.

I think that I suffer a little from Windows 98ism when the system went down if the registry exceeded 9MB or the system resources were limited to 64KB. In those days, the onboard RAM was pitiful compared to today's machines.

What you are proposing will probably not put a strain on any member's machine today.

The "Mysvalues.bi" will take 4097 lines to display on our screens. I even baulked at that. Admin may say: “That is not a problem.”

What we have is someone who lives both in the future and in the past. The funny thing is that I can remember things in my past that I couldn't remember 10 years ago. Perhaps our memories undergo some kind of housekeeping when we get to a certain age.

Anyway, I shall reconsider hard-wiring.
Are the two engines independent?
If so, why are they smushed together into one object?
What object?
If not, what happens if I reset one and not the other?
There is no answer to that because they are independent.
What about Rnd? Is that 1 or 2?
Two procedures are cited as being associated with Engine2. It follows then that all others are associated with Engine1; otherwise I would have cited them as well.
Why is GetSnapshot called that if my code doesn't get anything from it, like it does with GetDuo?
GetSnapshot is passive. It is like making a recording without listening to it. When we use SetSnapshot the same analogy is in winding back the tape to listen to it. SetSapshot is then active. Each time we use GetSnapshot it is like recording over a previous recording.
What happens if I GetSnapshot once and Set twice?
Well, that is like listening to a recording, winding back and listening to it again.
Then I can have as many snapshots as I want, rather than just the one.
I did think of that, but reckoned that no one would want more than one.
Your example says, "Integral range (10,20)" - does that mean both exclusive
No, inclusive as per, of many, dictionary definitions of range.

OK, I will let the dust settle and then go over the Help file again.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: MsWsII PRNG plus Help file

Post by deltarho[1859] »

fxm wrote:I too often suffer when I read a number of codes on this forum, but I don't say anything so as not to be only critical and not constructive.
When a horse breaks a leg, it gets shot. As far as I am concerned, 'fbmath.bi' broke a leg as soon as it came out of the stable.
Post Reply