New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Normal/Gaussian random number generation #1029
Conversation
Add StaticInstance to avoid unnecessary proliferation of normal random number engine instances.
Also simplified the handling of the case when layer 0 is selected, got rid of separate tailX and tailXInterval fields. Also added an optimization to zigguratAlgorithm - xor the sign bit of the result with a random bit instead of checking if random bit is set and negating the result.
…ng the struct API.
// this can happen if the function is completelly flat on the interval | ||
// this happens when called from zigguratInitialize using 256 layers | ||
// and single precision. | ||
assert(x0 == x1, "fderiv has the same sign on the entire interval"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enforce
is better in this case because this is not an error of logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
Terrific, thanks. I see Mashiro is already at it, let's review this in good time. |
unittest | ||
{ | ||
// Check the type rules for normal() | ||
assert(is(typeof(normal(0, 1)) == double)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, such type test should be static assert
.
See other Phobos modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected.
(another correction requested by Masahiro Nakagawa)
Thanks to everyone for the great feedback -- the latest couple of patches should fix most of those. A few extra things to add to the mix:
|
is(typeof(ct!(a.max))) && is(typeof(ct!(a.min))); | ||
} | ||
|
||
private bool isPowerOfTwo(I)(I i){ return (i & (i - 1)) == 0; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation will wrongly return true
if i == 0
-- should be corrected to i && !(i & (i - 1))
to avoid this. See: http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 (thanks to Bearophile for pointing me to this page).
Also suggest renaming to isPow2
and moving to std.math
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed by Jerro's latest commit.
* moved uniform01 so that it is documented right after the standard uniform() function * separated out documentation of normal(), Normal and normalRNG() * documented member functions for Normal and NormalBoxMullerEngine * extended/clarified some of the examples * a few extra unittests based on examples
One design decision query. Currently auto nrng = Normal!real(3, 7);
// Generate a number using default RNG (i.e. rndGen)
auto x = nrng();
// Generate number using specified RNG
auto r = Xorshift(1);
auto y = nrng(r); However, I've been unable to come up with a solution that allows calling |
I'm not sure this would be a good thing to have. It would make it easy to accidentally call |
Happy New Year to all, and apologies for the delay in following up here. First, I hope the design/doc changes already made haven't caused difficulty in proceeding with the review process. Given the design questions I've raised here I wonder if it might have been better to ask for review via another channel than a pull request. However, since we're here, I'll raise the questions here too, and will happily move them to another discussion space if it's better. The current design follows Boost.Random in having the Normal struct returning variates via However, there is an alternative, which is to have D's struct implementations of non-uniform random number generation follow the example of e.g. RandomSample, which couples with a uniform random number generator at construction time and then operates as a lazily-evaluated range. For this to really work (or indeed for a VariateGenerator struct akin to Boost's to work), it would be necessary for random number generators to be reworked as reference types (@monarchdodra did work on this, but I believe it was rejected in the short term as a breaking change: #893). My own inclination, on careful thought, is that it's preferable to keep things as-is and to develop a VariateGenerator in future, but I'd appreciate others' thoughts here. @jerro: re deterministic vs. nondeterministic results, isn't the same true of |
The same is true for uniform() and normal(), I haven't thought about that. So I guess that if we have it for functions, we may as well have it for structs, too. |
While I realize no-one likes a breaking change, I really think something needs to be done with PRNGs. The first problem is that a PRNG is both range and container. This means the sizeof of the basic PRNG (for, say, MT19997) is somewhere in the kilobytes (!!!) One of the PRNG's I was working on: Lagged Fibonacci, could have a payload in mega byte sizes (!!!!!). Passing these to any algorithm is enough to make any program grind to a halt, if not simply stack overflow. And still, that's without taking into account the problems of PRNGs generating the same sequences multiple times, at the coder's knowledge... This leaves us with (IMO), 1 of 2 options:
Both are actually essentially the same thing though, and, if anything, 1 introduces even more problems... I had gotten around to implementing a Well, that's for my 0.02$ here. @andralex : Do you think it is worth putting more effort into |
@monarchdodra - let's open this up as a discussion on the mailing list/forum so we can keep the focus here on the current pull request. But broadly I agree with you that we need a better design for PRNGs since as-is it results in a variety of unpleasant effects. As the changes required will potentially break downstream code that surely mandates a |
Regarding a prospective VariateGenerator, it seems C++11 decided not to preserve Boost.Random's variate_generator class for reasons outlined here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1933.pdf |
This is very relevant to the current code: since the struct Normal and the normal random number engines are all currently value types, this means they will suffer from the same problem. This seems to me a fairly severe design flaw and I'm minded to withdraw the pull request until we have a clear spec for RNG design. |
I just realized this hasn't been closed and has been taking up time in the testing queue. My apologies. I'll resubmit a new normal distribution when std.random2 is done. |
A little Christmas present for D :-) This code offers both function and struct interfaces for generating random numbers drawn from a normal/Gaussian distribution, with a choice of algorithms: for now, the Box-Muller algorithm (adapted from Boost's implementation), and Jerro's adaptation of his own Ziggurat implementation. Cf. the following discussions for some background and details:
http://www.digitalmars.com/d/archives/digitalmars/D/Normal_Gaussian_random_number_generation_for_D_179010.html
WebDrake#1
WebDrake#2
WebDrake#3
The different algorithms are implemented as different internal "engines" which can be chosen by the user as template parameters. This approach is proposed as a general solution for incorporating multiple different random number algorithms into Phobos, and can be used in future for other random number generating functions (e.g. for exponentially-distributed random numbers).
The struct implementations are designed to pave the way for functionality akin to Boost/C++11's variate_generator which couples a distribution and a random number generator.
Several issues for consideration when reviewing this code:
Jerro has also generated some interesting results based on tests of randomness (see discussion WebDrake#1) which may be worth taking into account in the bigger picture of std.random development.
.... oh, and -- should we include a partridge in a pear tree? :-)