Confusion about `Random`

2022-12-22 Thread jwatson-CO-edu via Digitalmars-d-learn
I am confused about why Program 1 produces random output but 
Program 2 does not.


---

### Program 1
```d
import std.stdio;
import std.conv;
import std.random;

Mt19937 rnd;

double rand01(){
// Uniform random sampling in [0,1)
return uniform( 0.0, 1.0, rnd);
}

void main(){
rnd = Random( unpredictableSeed );
for( uint i = 0; i < 6; i++ ){
writeln( rand01() );
}
}
```

Output:
```
0.35332
0.0687847
0.563096
0.37718
0.321598
0.530525
```

---

### Program 2
 sparrow_core.d
```d
// ...

Mt19937 rnd; // Randomness

void init_random(){
// Seed the RNG with the clock
rnd = Random( unpredictableSeed );
}

// ...

double rand01(){
// Uniform random sampling in [0,1)
return uniform( 0.0, 1.0, rnd);
}

// ...

// Build a dict of primitive symbols
primitiveSymbols["rand"] = function Atom*(){
// Random number on [0,1)
return new Atom( rand01() ); // Construct an Atom holding a 
random value

};

// ...

void init_SPARROW(){
// Populate necessary global structures
init_reserved(); // - Reserved symbols
init_env(); // -- Global context
init_primitives(); // Special atoms and Primitive Functions
init_specials(); // - Special forms
init_random(); // --- RNG
}
```

 app.d
```d
void main( string[] args ){

Atom* res = null;

if( _DEBUG_VERBOSE )  writeln( "Args are: " ~ args.to!string 
);


// Populate necessary interpreter components
init_SPARROW();

// ... Interpreter repeatedly invokes primitive symbol "rand"

}
```

Output:
```
0.961451
0.961451
0.961451
0.961451
0.961451
0.961451
```

Note: I have enclosed `uniform` so deeply because I am 
implementing the random number feature of a [computer 
language](https://github.com/jwatson-CO-edu/SPARROW).


---

What is the reason for this? Has the compiler optimized away the 
`uniform` call to a single double number?
What is the main difference between Program 1 and Program 2?  
Both seem to:

* Have a global RNG `rnd`
* Seed RNG after `main` starts.
* Generates a random number on [1,0) from a function.

So I would expect both programs to behave the same...




Re: Confusion about `Random`

2022-12-22 Thread Paul Backus via Digitalmars-d-learn
On Thursday, 22 December 2022 at 16:23:16 UTC, jwatson-CO-edu 
wrote:
I am confused about why Program 1 produces random output but 
Program 2 does not.


The code you have posted as "Program 2" is incomplete, and cannot 
be compiled as-is. I have made some changes in order to get it to 
compile and produce useful output, resulting in the following 
program:


 sparrow_core.d

```d
module sparrow_core;

import std.random;

Mt19937 rnd; // Randomness

void init_random(){
// Seed the RNG with the clock
rnd = Random( unpredictableSeed );
}

double rand01(){
// Uniform random sampling in [0,1)
return uniform( 0.0, 1.0, rnd);
}

void init_SPARROW(){
// Populate necessary global structures
init_random(); // --- RNG
}
```

 app.d

```d
module app;

import sparrow_core;
import std.stdio;

void main(){
init_SPARROW();
foreach (i; 0 .. 6)
writeln(rand01());
}
```

When I compile and run the above program, I get the following 
output:


```
0.289729
0.39377
0.693163
0.232496
0.388511
0.840994
```

So, as far as I can tell, there is nothing wrong with your code, 
and the random number generator is working as intended.


Most likely you have made a mistake somewhere in the part of the 
code that you did not post, and that mistake is what's causing 
the lack of randomness you observed in the output.


Re: Confusion about `Random`

2022-12-22 Thread jwatson-CO-edu via Digitalmars-d-learn

On Thursday, 22 December 2022 at 17:33:48 UTC, Paul Backus wrote:
So, as far as I can tell, there is nothing wrong with your 
code, and the random number generator is working as intended.


Most likely you have made a mistake somewhere in the part of 
the code that you did not post, and that mistake is what's 
causing the lack of randomness you observed in the output.


Right, the entire project is about 2k lines, so I didn't post it.
I've isolated the problem to instances when my program is 
interpreting a loop.

Somehow the loop context must be storing an old seed for `rnd`?
I'm still searching for the issue and I have not been able to 
replicate it in smaller example.





Re: Confusion about `Random`

2022-12-22 Thread H. S. Teoh via Digitalmars-d-learn
On Thu, Dec 22, 2022 at 08:17:56PM +, jwatson-CO-edu via 
Digitalmars-d-learn wrote:
> On Thursday, 22 December 2022 at 17:33:48 UTC, Paul Backus wrote:
> > So, as far as I can tell, there is nothing wrong with your code, and
> > the random number generator is working as intended.
> > 
> > Most likely you have made a mistake somewhere in the part of the
> > code that you did not post, and that mistake is what's causing the
> > lack of randomness you observed in the output.
> 
> Right, the entire project is about 2k lines, so I didn't post it.
> I've isolated the problem to instances when my program is interpreting
> a loop.  Somehow the loop context must be storing an old seed for
> `rnd`?  I'm still searching for the issue and I have not been able to
> replicate it in smaller example.
[...]

You could try using DustMite to reduce it to a minimal (or at least
smaller) example.

My personal guess is that you forgot a `ref` somewhere when you pass the
RNG to a function.  Given that due to historical accident std.random
uses structs for RNG implementations, and this can sometimes lead to
unexpected results when you unintentionally passed an RNG state by value
instead of by reference.  One thing to try could be to scan all your
function signatures where an RNG is passed, and make sure there's a
`ref` on it.


T

-- 
Give a man a fish, and he eats once. Teach a man to fish, and he will sit 
forever.


Re: Confusion about `Random`

2022-12-22 Thread Steven Schveighoffer via Digitalmars-d-learn

On 12/22/22 11:23 AM, jwatson-CO-edu wrote:
I am confused about why Program 1 produces random output but Program 2 
does not.


---

### Program 1
```d
import std.stdio;
import std.conv;
import std.random;

Mt19937 rnd;

double rand01(){
     // Uniform random sampling in [0,1)
     return uniform( 0.0, 1.0, rnd);
}

void main(){
     rnd = Random( unpredictableSeed );
     for( uint i = 0; i < 6; i++ ){
     writeln( rand01() );
     }
}
```

Output:
```
0.35332
0.0687847
0.563096
0.37718
0.321598
0.530525
```

---

### Program 2
 sparrow_core.d
```d
// ...

Mt19937 rnd; // Randomness

void init_random(){
     // Seed the RNG with the clock
     rnd = Random( unpredictableSeed );
}

// ...

double rand01(){
     // Uniform random sampling in [0,1)
     return uniform( 0.0, 1.0, rnd);
}

// ...

// Build a dict of primitive symbols
primitiveSymbols["rand"] = function Atom*(){
     // Random number on [0,1)
     return new Atom( rand01() ); // Construct an Atom holding a random 
value

};

// ...

void init_SPARROW(){
     // Populate necessary global structures
     init_reserved(); // - Reserved symbols
     init_env(); // -- Global context
     init_primitives(); // Special atoms and Primitive Functions
     init_specials(); // - Special forms
     init_random(); // --- RNG
}
```

 app.d
```d
void main( string[] args ){

     Atom* res = null;

     if( _DEBUG_VERBOSE )  writeln( "Args are: " ~ args.to!string );

     // Populate necessary interpreter components
     init_SPARROW();

     // ... Interpreter repeatedly invokes primitive symbol "rand"

}
```

Output:
```
0.961451
0.961451
0.961451
0.961451
0.961451
0.961451
```

Note: I have enclosed `uniform` so deeply because I am implementing the 
random number feature of a [computer 
language](https://github.com/jwatson-CO-edu/SPARROW).


---

What is the reason for this? Has the compiler optimized away the 
`uniform` call to a single double number?

What is the main difference between Program 1 and Program 2? Both seem to:
* Have a global RNG `rnd`
* Seed RNG after `main` starts.
* Generates a random number on [1,0) from a function.

So I would expect both programs to behave the same...




Without the rest of the code, and how random is called, I have a 
hunch... Are you using threads by any chance?


If, for instance, your calls to rand01 are done in a new thread, that 
new thread will have a *default* state of Mt19937.


I tried out just a non-seeded instance, and it did not produce that 
exact number, so this may not be the case.


But in case you are, you should be aware that globals get one instance 
per thread, and are default initialized.


-Steve


Re: Confusion about `Random`

2022-12-22 Thread Salih Dincer via Digitalmars-d-learn
On Thursday, 22 December 2022 at 16:23:16 UTC, jwatson-CO-edu 
wrote:
What is the reason for this? Has the compiler optimized away 
the `uniform` call to a single double number?
What is the main difference between Program 1 and Program 2?  
Both seem to:

* Have a global RNG `rnd`
* Seed RNG after `main` starts.
* Generates a random number on [1,0) from a function.

So I would expect both programs to behave the same...


I made your code runnable based on the information you provided 
us. There seems to be no problem if you try. You can try using 
static this.


```d
import std.random;

static this() { } // can try using

Mt19937 rnd;
void init_random() {
  rnd = Random(unpredictableSeed);
}

double rand01() {
return uniform(0, 1.0, rnd);
}

void main()
{
  init_random();

  struct Atom { double num; }
  alias atom = Atom* function();
  atom[string] primitiveSymbols = [
"rand" : () => new Atom(rand01)
  ];
  import std.stdio;
  writeln(*primitiveSymbols["rand"]()); // Atom(0.630001)
}
```
SDB@79



Re: Confusion about `Random`

2022-12-23 Thread jwatson-CO-edu via Digitalmars-d-learn
On Friday, 23 December 2022 at 00:58:01 UTC, Steven Schveighoffer 
wrote:
Without the rest of the code, and how random is called, I have 
a hunch... Are you using threads by any chance?


If, for instance, your calls to rand01 are done in a new 
thread, that new thread will have a *default* state of Mt19937.

-Steve


Good question, Steve, but I do not intentionally start any 
threads.  Below is the machinery that interprets a for-loop.  Do 
you see anything that would enclose a previous state of the RNG?


```d
specialForms["for"] = function ExprInContext( ExprInContext eINc 
){

// Execute a `for` loop, Default is to increment up by one

// 1. Parse loop args
Atom*[] loopArgs  = flatten_atom_list( second( eINc.expr ) ); 
// Fetch args
string  iVarName  = loopArgs[0].str; //Get the counter var 
name

boolincrByOne = (loopArgs.length == 3);
double  loBound   = 0.0;
double  hiBound   = 0.0;
double  incr  = 1.0;
double  i /*---*/ = 0.0;
Atom*   loopProg  = third( eINc.expr ); // WARNING: TYPE NOT 
CHECKED

Atom*   rtnExpr   = null;
Env*nuEnv = null;
ExprInContext runBlock;


// Case: Default loop increments by 1.0
if( incrByOne ){
loBound = loopArgs[1].num;
hiBound = loopArgs[2].num;

// Case: User-specified increment
}else if(loopArgs.length == 4){
loBound = loopArgs[1].num;
incr= loopArgs[2].num;
hiBound = loopArgs[3].num;

// Else: There is a syntax error
}else  return ExprInContext(
new Atom( F_Error.SYNTAX, loopArgs.length.to!string ~
  " was an incorrect number of loop args. 
Expected 3 or 4." ),

eINc.context,
"`for` got an unexpected number of args"
);

// 2. Create a new nested context, bind the counter var
i = loBound;
nuEnv = new Env();
nuEnv.parent = eINc.context;
bind_atom( nuEnv, iVarName, new Atom( loBound ) );

runBlock = ExprInContext(
loopProg,
nuEnv,
"loop body"
);

// 3. LOOP:
while( i <= hiBound ){
// run block in nested context
rtnExpr = block_meaning( runBlock ).expr;
i += incr; // increment
// Store new counter value so that loop body can access it
bind_atom( nuEnv, iVarName, new Atom( i ) );
}

return ExprInContext(
rtnExpr,
eINc.context,
"loop result"
);

};
```




Re: Confusion about `Random`

2022-12-23 Thread jwatson-CO-edu via Digitalmars-d-learn

On Friday, 23 December 2022 at 07:25:23 UTC, Salih Dincer wrote:

You can try using static this.

```d
import std.random;

static this() { } // can try using

Mt19937 rnd;
void init_random() {
  rnd = Random(unpredictableSeed);
}

double rand01() {
return uniform(0, 1.0, rnd);
}

void main()
{
  init_random();

  struct Atom { double num; }
  alias atom = Atom* function();
  atom[string] primitiveSymbols = [
"rand" : () => new Atom(rand01)
  ];
  import std.stdio;
  writeln(*primitiveSymbols["rand"]()); // Atom(0.630001)
}
```
SDB@79


Salih, I would like to implement this tactic, but I do not 
understand it.


What are you creating here?
```d
static this() { } // can try using
```

What is this operator?
```d
\*...*\ () => new Atom(rand01) \*...*\
```



Re: Confusion about `Random`

2022-12-23 Thread jwatson-CO-edu via Digitalmars-d-learn

On Friday, 23 December 2022 at 00:00:06 UTC, H. S. Teoh wrote:
You could try using DustMite to reduce it to a minimal (or at 
least

smaller) example.

My personal guess is that you forgot a `ref` somewhere when you 
pass the RNG to a function.  Given that due to historical 
accident std.random uses structs for RNG implementations, and 
this can sometimes lead to unexpected results when you 
unintentionally passed an RNG state by value instead of by 
reference.  One thing to try could be to scan all your function 
signatures where an RNG is passed, and make sure there's a 
`ref` on it.

T


I had not passed the RNG in any case, but instead accessed the 
global RNG from inside any function that uses it.  Is that a 
potential issue?





Re: Confusion about `Random`

2022-12-23 Thread Steven Schveighoffer via Digitalmars-d-learn

On 12/23/22 10:07 AM, jwatson-CO-edu wrote:

On Friday, 23 December 2022 at 00:58:01 UTC, Steven Schveighoffer wrote:
Without the rest of the code, and how random is called, I have a 
hunch... Are you using threads by any chance?


If, for instance, your calls to rand01 are done in a new thread, that 
new thread will have a *default* state of Mt19937.


Good question, Steve, but I do not intentionally start any threads.  
Below is the machinery that interprets a for-loop.  Do you see anything 
that would enclose a previous state of the RNG?




Your code looks like it's making a function pointer, and that function 
pointer directly uses the global RNG.


I'm not seeing how your code could be copying the RNG somehow, as I'm 
assuming it's not manipulating the generated code from the compiler.


If it's not a threading problem, the only other possibility I can think 
of is that your loop code is not truly calling that function over and over.


I'd start instrumenting rand01 with some printouts, and see if it's 
doing what you expect. If it's not, throw and catch an exception, and 
print the stack trace (or use a debugger) to help understand what is 
happening.


I have been puzzled in the past with behavior that seemed to be 
reasonable, but given the way the implementation happened, did 
unexpected things (like caching values).


-Steve


Re: Confusion about `Random`

2022-12-23 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Dec 23, 2022 at 03:21:24PM +, jwatson-CO-edu via 
Digitalmars-d-learn wrote:
> On Friday, 23 December 2022 at 00:00:06 UTC, H. S. Teoh wrote:
[...]
> > My personal guess is that you forgot a `ref` somewhere when you pass
> > the RNG to a function.  Given that due to historical accident
> > std.random uses structs for RNG implementations, and this can
> > sometimes lead to unexpected results when you unintentionally passed
> > an RNG state by value instead of by reference.  One thing to try
> > could be to scan all your function signatures where an RNG is
> > passed, and make sure there's a `ref` on it.
[...]
> I had not passed the RNG in any case, but instead accessed the global
> RNG from inside any function that uses it.  Is that a potential issue?

Hmm, in that case it's probably not a problem with `ref`.

You probably should give DustMite a shot; from the snippets you've
posted so far we haven't found any clues of what might have gone wrong.
To narrow down the issue we really need to start from the original code
and reduce it to a minimal case.

https://github.com/CyberShadow/DustMite


T

-- 
Дерево держится корнями, а человек - друзьями.


Re: Confusion about `Random`

2022-12-24 Thread jwatson-CO-edu via Digitalmars-d-learn

On Friday, 23 December 2022 at 07:25:23 UTC, Salih Dincer wrote:

You can try using static this.

```d
import std.random;

static this() { } // can try using

Mt19937 rnd;
void init_random() {
  rnd = Random(unpredictableSeed);
}

double rand01() {
return uniform(0, 1.0, rnd);
}

void main()
{
  init_random();

  struct Atom { double num; }
  alias atom = Atom* function();
  atom[string] primitiveSymbols = [
"rand" : () => new Atom(rand01)
  ];
  import std.stdio;
  writeln(*primitiveSymbols["rand"]()); // Atom(0.630001)
}
```
SDB@79


I would still like to learn about this idiom.  Can you tell me 
what it means and when I should use it?


Re: Confusion about `Random`

2022-12-24 Thread jwatson-CO-edu via Digitalmars-d-learn

On Friday, 23 December 2022 at 17:53:24 UTC, H. S. Teoh wrote:
You probably should give DustMite a shot; from the snippets 
you've posted so far we haven't found any clues of what might 
have gone wrong. To narrow down the issue we really need to 
start from the original code and reduce it to a minimal case.


https://github.com/CyberShadow/DustMite


T


No need, I have ample logging already written into the program.

False alarm everyone, there was a logical error in my code. And 
not only this, but the error appeared in **none** of the code I 
posted, sorry! I was working from a wrong assumption.


I counted calls to my `rand01` function and realized that the 
number of calls was equal to the number of times `rand` appeared 
in the file being interpreted, rather than the number of times it 
should have been evaluated.  Then it became clear that the parser 
was replacing calls to `rand` with a number, which was displayed 
repeatedly when a loop was evaluated.  I removed `rand` from the 
dictionary of substitutions the parser needs to make.


Re: Confusion about `Random`

2022-12-24 Thread Ali Çehreli via Digitalmars-d-learn

On 12/24/22 08:18, jwatson-CO-edu wrote:
> On Friday, 23 December 2022 at 07:25:23 UTC, Salih Dincer wrote:
>> You can try using static this.
>>
>> ```d
>> import std.random;
>>
>> static this() { } // can try using

static this() blocks: executed when a thread a starts (the program has 
at least one thread: the main thread); so you can put initializations here


~static this() blocks: counterparts of 'static this', executed once for 
each thread that is terminating


shared static this() blocks: executed once per program (executed by the 
main thread)


~shared static this() blocks executed once per program when the program 
is terminating


>> "rand" : () => new Atom(rand01)

That's the lambda (ananymous function) syntax.

The "rand" key of an associative array is being associated with the 
function after the ':' character. In the code above, the function 
creates a new Atom object. So, when the following code is executed:


  primitiveSymbols["rand"]

the same lambda would be returned but the execution of that lambda as 
the following


  primitiveSymbols["rand"]()

would return a new Atom which would make a call to the rand01() function 
and get a new random number from the same 'rnd' object.


Ali



Re: Confusion about `Random`

2022-12-24 Thread Siarhei Siamashka via Digitalmars-d-learn
On Saturday, 24 December 2022 at 16:16:17 UTC, jwatson-CO-edu 
wrote:
Then it became clear that the parser was replacing calls to 
`rand` with
a number, which was displayed repeatedly when a loop was 
evaluated.


Sounds like a case of https://xkcd.com/221/

BTW, you don't need to explicitly initialize unpredictableSeed, 
because that's how it is already configured dby efault. Setting 
the seed is useful if you are interested in a specific PRNG 
algorithm with a specific seed to produce a predictable 
deterministic pattern.


Another thing is that the current implementation of `std.random` 
in Phobos is extremely slow and with some tweaks it [can be up to 
3x-6x times 
faster](https://codeforces.com/blog/entry/99292#comment-881097). 
There's a more advanced 
[mir-random](https://code.dlang.org/packages/mir-random) 
third-party dub package, but I'm not sure whether they have any 
plans to backport all of their optimizations to Phobos (or 
whether such contribution would be accepted).


Re: Confusion about `Random`

2022-12-24 Thread jwatson-CO-edu via Digitalmars-d-learn
On Saturday, 24 December 2022 at 16:42:36 UTC, Siarhei Siamashka 
wrote:


Sounds like a case of https://xkcd.com/221/

BTW, you don't need to explicitly initialize unpredictableSeed,

Another thing is that the current implementation of 
`std.random` in Phobos is extremely slow


Even better, I automated the caching of dice rolls to be used 
later as random numbers!  `;P`


Thanks for the tips.





Re: Confusion about `Random`

2022-12-24 Thread jwatson-CO-edu via Digitalmars-d-learn

On Saturday, 24 December 2022 at 16:34:29 UTC, Ali Çehreli wrote:
static this() blocks: executed when a thread a starts (the 
program has at least one thread: the main thread); so you can 
put initializations here


~static this() blocks: counterparts of 'static this', executed 
once for each thread that is terminating


shared static this() blocks: executed once per program 
(executed by the main thread)


~shared static this() blocks executed once per program when the 
program is terminating


>> "rand" : () => new Atom(rand01)

That's the lambda (ananymous function) syntax.

The "rand" key of an associative array is being associated with 
the function after the ':' character. In the code above, the


Ali, your post contained at least 3 things I did not previously 
know about D; thank you!


And thank you all for helping troubleshoot this issue with my 
hobby language!


Re: Confusion about `Random`

2022-12-24 Thread Ali Çehreli via Digitalmars-d-learn

On 12/24/22 09:58, jwatson-CO-edu wrote:

>> ~static this()

Should be 'static ~this()'.

>> ~shared static this()

Should be 'shared static ~this()'.

> thank you all

Happy to be helpful...

Ali



Re: Confusion about `Random`

2022-12-24 Thread Salih Dincer via Digitalmars-d-learn
On Saturday, 24 December 2022 at 17:58:04 UTC, jwatson-CO-edu 
wrote:
Ali, your post contained at least 3 things I did not previously 
know about D; thank you!


And thank you all for helping troubleshoot this issue with my 
hobby language!


Thank you for completing me: Tesekkurler hocam in Turkish.

Meanwhile, the compile-time and associative array capabilities 
are incredible:


```d
alias oT = int;
enum opMap =
[ "×": (oT a, oT b) => a * b,
  "÷": (oT a, oT b) => a / b,
  //...
];

auto doubleAndDivide(oT first, oT second) {
  const foldedDouble = opMap["×"](first, 2);
  return opMap["÷"](foldedDouble, second);
}

void main()
{
  assert(6.doubleAndDivide(3) == 4);
}
```

Don't you think it's delicious too? It's impossible not to love D.

SDB@79


Re: Confusion about `Random`

2022-12-25 Thread Siarhei Siamashka via Digitalmars-d-learn
On Saturday, 24 December 2022 at 17:55:11 UTC, jwatson-CO-edu 
wrote:
On Saturday, 24 December 2022 at 16:42:36 UTC, Siarhei 
Siamashka wrote:


Sounds like a case of https://xkcd.com/221/

BTW, you don't need to explicitly initialize unpredictableSeed,

Another thing is that the current implementation of 
`std.random` in Phobos is extremely slow


Even better, I automated the caching of dice rolls to be used 
later as random numbers!  `;P`


Just in case if you are not joking, caching a certain amount of 
dice rolls to reuse them later in a circular fashion would make a 
bad quality pseudorandom number generator (a slightly upgraded 
version of the xkcd joke). Don't do this. Just use `std.random` 
if performance doesn't really matter and you want to avoid an 
extra library dependency. But if performance does matter and you 
need hundreds of millions of random numbers for Monte Carlo 
simulations, fuzz testing or anything else, then consider 
`mir.random`.


BTW, a few puzzles from https://adventofcode.com/2022 are 
trivialized by using randomized bruteforce instead of a more 
sophisticated algorithm. And having fast random numbers 
generation helps.


Re: Confusion about `Random`

2022-12-27 Thread jwatson-CO-edu via Digitalmars-d-learn
On Sunday, 25 December 2022 at 14:47:49 UTC, Siarhei Siamashka 
wrote:
Just in case if you are not joking, caching a certain amount of 
dice rolls to reuse them later in a circular fashion would make 
a bad quality pseudorandom number generator (a slightly 
upgraded version of the xkcd joke). Don't do this.


Naturally.  I was joking, and I was also describing the erroneous 
behavior of the parser of the language I was writing; which was 
to substitute/cache a randomly generated number into a concrete 
syntax tree when interpreted, instead of making actual calls to 
the RNG at runtime, which is the correct (now current) behavior.  
The joke was that I automated the manual dice-rolling part to 
arrive more efficiently at the incorrect solution.