Jump to page: 1 2 3
Thread overview
Confusion about `Random`
Dec 22, 2022
jwatson-CO-edu
Dec 22, 2022
Paul Backus
Dec 22, 2022
jwatson-CO-edu
Dec 23, 2022
H. S. Teoh
Dec 23, 2022
jwatson-CO-edu
Dec 23, 2022
H. S. Teoh
Dec 24, 2022
jwatson-CO-edu
Dec 24, 2022
Siarhei Siamashka
Dec 24, 2022
jwatson-CO-edu
Dec 25, 2022
Siarhei Siamashka
Dec 28, 2022
jwatson-CO-edu
Dec 23, 2022
jwatson-CO-edu
Dec 23, 2022
Salih Dincer
Dec 23, 2022
jwatson-CO-edu
Dec 24, 2022
jwatson-CO-edu
Dec 24, 2022
Ali Çehreli
Dec 24, 2022
jwatson-CO-edu
Dec 24, 2022
Ali Çehreli
Dec 24, 2022
Salih Dincer
December 22, 2022

I am confused about why Program 1 produces random output but Program 2 does not.


Program 1

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

// ...

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

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.


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

December 22, 2022

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

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

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.

December 22, 2022

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.

December 22, 2022
On Thu, Dec 22, 2022 at 08:17:56PM +0000, 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.
December 22, 2022

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

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

// ...

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

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.


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

December 23, 2022

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.

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

December 23, 2022

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?

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
    bool    incrByOne = (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"
    );

};
December 23, 2022

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

>

You can try using static this.

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?

static this() { } // can try using

What is this operator?

\*...*\ () => new Atom(rand01) \*...*\
December 23, 2022

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?

December 23, 2022
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
« First   ‹ Prev
1 2 3