digitalmars.D.learn - Confusion about `Random`
- jwatson-CO-edu (93/93) Dec 22 2022 I am confused about why Program 1 produces random output but
- Paul Backus (50/52) Dec 22 2022 The code you have posted as "Program 2" is incomplete, and cannot
- jwatson-CO-edu (7/12) Dec 22 2022 Right, the entire project is about 2k lines, so I didn't post it.
- H. S. Teoh (14/27) Dec 22 2022 [...]
- jwatson-CO-edu (4/16) Dec 23 2022 I had not passed the RNG in any case, but instead accessed the
- H. S. Teoh (12/22) Dec 23 2022 [...]
- jwatson-CO-edu (12/18) Dec 24 2022 No need, I have ample logging already written into the program.
- Siarhei Siamashka (17/21) Dec 24 2022 Sounds like a case of https://xkcd.com/221/
- jwatson-CO-edu (5/9) Dec 24 2022 Even better, I automated the caching of dice rolls to be used
- Siarhei Siamashka (15/26) Dec 25 2022 Just in case if you are not joking, caching a certain amount of
- jwatson-CO-edu (9/13) Dec 27 2022 Naturally. I was joking, and I was also describing the erroneous
- Steven Schveighoffer (10/130) Dec 22 2022 Without the rest of the code, and how random is called, I have a
- jwatson-CO-edu (66/71) Dec 23 2022 Good question, Steve, but I do not intentionally start any
- Steven Schveighoffer (15/26) Dec 23 2022 Your code looks like it's making a function pointer, and that function
- Salih Dincer (28/36) Dec 22 2022 I made your code runnable based on the information you provided
- jwatson-CO-edu (11/35) Dec 23 2022 Salih, I would like to implement this tactic, but I do not
- jwatson-CO-edu (3/27) Dec 24 2022 I would still like to learn about this idiom. Can you tell me
- =?UTF-8?Q?Ali_=c3=87ehreli?= (20/28) Dec 24 2022 static this() blocks: executed when a thread a starts (the program has
- jwatson-CO-edu (5/18) Dec 24 2022 Ali, your post contained at least 3 things I did not previously
- =?UTF-8?Q?Ali_=c3=87ehreli?= (5/8) Dec 24 2022 Should be 'shared static ~this()'.
- Salih Dincer (23/27) Dec 24 2022 Thank you for completing me: Tesekkurler hocam in Turkish.
I am confused about why Program 1 produces random output but Program 2 does not. --- ```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 ``` --- ```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 } ``` ```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...
Dec 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: ```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 } ``` ```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.
Dec 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.
Dec 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:[...] 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.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.
Dec 22 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. TI 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?
Dec 23 2022
On Fri, Dec 23, 2022 at 03:21:24PM +0000, 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 -- Дерево держится корнями, а человек - друзьями.
Dec 23 2022
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 TNo 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.
Dec 24 2022
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).
Dec 24 2022
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 slowEven better, I automated the caching of dice rolls to be used later as random numbers! `;P` Thanks for the tips.
Dec 24 2022
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: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.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 slowEven better, I automated the caching of dice rolls to be used later as random numbers! `;P`
Dec 25 2022
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.
Dec 27 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. --- ```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 ``` --- ```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 } ``` ```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
Dec 22 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. -SteveGood 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 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" ); }; ```
Dec 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: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). -SteveWithout 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?
Dec 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. ```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
Dec 22 2022
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 79Salih, 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) \*...*\ ```
Dec 23 2022
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 79I would still like to learn about this idiom. Can you tell me what it means and when I should use it?
Dec 24 2022
On 12/24/22 08:18, jwatson-CO-edu wrote:On Friday, 23 December 2022 at 07:25:23 UTC, Salih Dincer 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 terminatingYou can try using static this. ```d import std.random; static this() { } // can try usingThat'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"rand" : () => new Atom(rand01)
Dec 24 2022
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 terminatingAli, 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!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"rand" : () => new Atom(rand01)
Dec 24 2022
On 12/24/22 09:58, jwatson-CO-edu wrote:Should be 'static ~this()'.~static this()Should be 'shared static ~this()'.~shared static this()thank you allHappy to be helpful... Ali
Dec 24 2022
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
Dec 24 2022