RandStream is a handle class and I get a bit confused about that. I am a bit behind on learning the new Matlab OOP. I am pretty comfortable with value class but not with handle class.
For example,
function foo
h = RandStream('mt19937ar');
end
Everytime I call this function, does Matlab create an instance of RandStream (i.e. allocate memory) and then delete the instance (i.e. free memory)? So after the function returns, I won't have access to the object created inside foo( ). Right?
Why do we want to implement a random number generator as a handle class? I think in this case, a value class will serve the purpose.
That is correct. But if you returned the handle h from the foo function,
you could use the generator from outside the function.
> Why do we want to implement a random number generator as a handle class? I
> think in this case, a value class will serve the purpose.
h1 = RandStream('mt19937ar');
h2 = h1;
x1 = rand(h1, 1, 10);
x2 = rand(h2, 1, 10);
Should x1 and x2 be the same? If the RandStream class were a value class,
they would be because h2 would be _a copy_ of h1 from before it was used to
generate random numbers, and so its state would be a copy of the state of
h1.
Because RandStream is a handle class, h1 and h2 refer to _the same random
stream_. Thus after using the stream (via h1) to generate 10 numbers, its
state has changed and so using that same stream (via h2) to generate 10
numbers generates different values in x1 and x2.
I believe the latter behavior is what users would expect from a random
number generator. If you wanted to restore the state of the generator to
the state it was in prior to the first of those RAND calls, use the RESET
method.
reset(h1);
x3 = rand(h2, 1, 10); % should be the same as x1
x4 = rand(h1, 1, 10); % should be the same as x2
And yes, I did swap the order in which I used the variables in the
construction of x3 and x4. Since they refer to the same stream, that's
okay.
Note that if you don't want to worry about the RandStream class, you can
just call RAND or RANDN like you used to -- they will draw numbers from a
"default" random stream.
--
Steve Lord
sl...@mathworks.com
comp.soft-sys.matlab (CSSM) FAQ: http://matlabwiki.mathworks.com/MATLAB_FAQ
The same thing can be said about this function
function foo
x = 0;
end
x is a value, not a handle, and after the function returns, you won't have access to it. I realize that that doesn't answer your question, but it's hard for me to say what you need to do, because I don't know what you're trying to do. Let me take a shot, and bear with me.
First, there's no rule that says to generate random values, you have to create a random stream. You can simply call rand, and randn, and randi, as you always have. Even if you want to control how random values are generated, you need not necessarily create a stream. Many times you can just get the (global) default stream, as
dfltStream = RandStream.getDefaultStream;
and then read or write its properties (e.g., dfltStream.State) or reset it. The three most common reasons for doing the latter are
a) You want to put the default stream back to its beginning, as if you'd restarted MATLAB. This is most easily done as
reset(RandStream.getDefaultStream);
b) You want to be able to put it in some predictable state so that when you run a simulation repeatedly in the same MATLAB session, you get the same result over and over. This is most easily done as
reset(RandStream.getDefaultStream,1);
b) You want to put it in some unpredictable state when you start up a new MATLAB session so that when you run a simulation, you get a different result each in each session. This is most easily done as
reset(RandStream.getDefaultStream,sum(100*clock));
But there are two reasons why you might _want_ to create a random stream instead of just getting the default stream:
1) You want to create a stream s and execute a statement such as
x = rand(s,10,1) % note the first input
that generates random values, but does not affect what other parts of your code would get when they call rand in the ordinary way, e.g.
y = rand(5,1) % note _no_ stream input
In other words, that first example draws numbers from the stream s, while the second draws from the default stream. Ordinarily, you probably would not care what specific random values you get when you call rand, because after all, they're supposed to be "random". But it's sometimes easier to debug or reproduce results if you can isolate the different sources of randomness. Thus, "private" streams. Create as many as you want, but understand why you want them. Often, you don't.
2) You might want to make sure the default stream uses a particular generator algorithm. Notice that the three reset examples above make the assumption that you're happy with the generator algorithm that the current default stream uses, or that you don't care. And not caring is perfectly valid -- if all you want is to repeat the same "random" calculations while you're debugging something, and then never care about repeating that again, then it's just a matter of getting the same values repeatedly over the short term.
On the other hand, you might be building an algorithm that uses random numbers, and you want to benchmark the algorithm by running it using a fixed set of random values, and checking for a specific result. You want that benchmark test to work forever, no matter what MATLAB session or version run it's run in, no matter what code has been executed previously in that session. In other words, you need repeatability over the long term. To get that, you'll not only want to reset the default stream, but make sure it's using a specific generator algorithm.
It's important to realize that "the" default stream is really "the stream that is current designated as" the default stream in same way that you computer has a printer designated as the default. You, or code that you call, can designate another stream as the global default. if code that you call does that, it's probably bad etiquette, but, for example, the line
rand('state',0)
does just that.
The different generator algorithms spit out different sequences of values, so even if you reset the default stream, you might get different values than you expect if the default stream is not what you think it is. So if you want to be absolutely guaranteed that the first "random" number you get from RAND is
>> rand
ans =
0.81472
then you need to be sure that the default stream is one that uses the Mersenne Twister, and was seeded with zero. To do that, you can create such a stream and designate it as the default stream:
mtStream = RandStream('mt19937ar','Seed',0);
RandStream.setDefaultStream(mtStream);
But it's important to remember that this is often not at all necessary.
> Why do we want to implement a random number generator as a handle class?
> I think in this case, a value class will serve the purpose.
The short answer is that you want to be able to pass around a single stream and have every function that gets it be drawing numbers from that same stream. If RandStream was value-based, everyone would be working with copies. In particular, MATLAB creates a default random stream at startup, and you need to be able to modify the properties of that same stream, not of a copy of it.
Hope this all helps.
- Peter Perkins
The MathWorks, Inc.
I guess it is safe to assume that Matlab would keep the legacy mode, i.e. the old way of using rand(...) and randn(...). I had some bad experience with the Link to Code Composer Toolbox. The toolbox has made a lot of incompatible changes to its functions going from one version to the next version (this happened couple years ago). I hope that this would not happen to Matlab.
Now I understand the reason why RandStream is a handle class. I just need to remember that the statement "h2 = h1" does not create a new object but both h1 and h2 "point" to the same object. Also if I create h1 in the Matlab workspace and pass h1 to a function, although the function does not return h1 as an output argument, h1 in the Matlab workspace has different property values after the function returns. This is another thing about handle class.
I was reading R2009b documentation (User Guide -> Mathematics -> Random Numbers) and this statement (under "Managing the Default Stream"):
rand, randn, and randi draw random numbers from an underlying random number
stream, called the default stream.
I think this statement is half true. Under "Legacy Mode", there is this statement:
Prior to MATLAB Version 7.7, the rand and randn functions used a different syntax
for controlling the random number generators. In addition, rand and randn had
separate underlying random number streams.
So I think it is true that rand and randn had separate underlying random number streams. I verify this by the following example code:
>> clear all
>> defaultStream=RandStream.getDefaultStream
defaultStream =
legacy random stream (current default)
RAND algorithm: V7.4 (Mersenne Twister)
RANDN algorithm: V5 (Ziggurat)
>> myState=defaultStream.State;
>> A=rand(1,100);
>> defaultStream.State=myState;
>> randn(100);
>> B=rand(1,100);
>> isequal(A,B)
ans =
1
Peter Perkins <Peter....@MathRemoveThisWorks.com> wrote in message <hnb49n$755$1...@fred.mathworks.com>...
> I was reading R2009b documentation (User Guide -> Mathematics -> Random
> Numbers) and this statement (under "Managing the Default Stream"):
>
> rand, randn, and randi draw random numbers from an underlying random
> number stream, called the default stream.
> I think this statement is half true. Under "Legacy Mode", there is this
> statement:
>
> Prior to MATLAB Version 7.7, the rand and randn functions used a
> different syntax for controlling the random number generators. In
> addition, rand and randn had separate underlying random number streams.
>
> So I think it is true that rand and randn had separate underlying random
> number streams. I verify this by the following example code:
Correct, at least in one sense. If the legacy generators are active (if you typed rand('state',0), for example), then rand and randn do not affect each other's numbers. The old syntax (whic as I said is all still supported) had you go to rand and randn separately to control those two streams. In that sense, it's two streams. This one special case is for backwards compatibility.
On the other hand, for the purposes of resetting and saving/restoring state, even with the legacy generators, the new RandStream syntax lets you go to one place and work with one stream object, so in that sense it's one stream.
But you have to go out of your way to make the legacy generators active -- since R2008b, a fresh MATLAB session will get you
>> RandStream.getDefaultStream
ans =
mt19937ar random stream (current default)
Seed: 0
RandnAlg: Ziggurat
and rand, randn, and randi all draw from the same stream.
Before knowing about these new fancy ways in the new Matlab, I would think that it is trivial to do that in Matlab. But things are not that simple anymore.
In conclusion, just change the code to use the new RandStream and forget about how to make things to be backward compatible.
Kevin, I'm not sure what you're reacting to, but I'd like to find out so that we can hopefully make things easier to understand in the future. In a couple threads, you've asked about several different things, some of them new since R2008b, some of them having to do with backwards compatibility. Many of new things introduced in R2008b were to support parallel random number generation, and those require learning some new ideas, as well as syntax. But for the things that 95% of MATLAB users want to do with random number generation, the syntaxes are pretty straightforward:
=== Generate random values ===
This is tha same as it has always been:
>> rand(2)
ans =
0.79814 0.78109
0.79564 0.3511
>> randn(2)
ans =
-1.3385 1.4472
0.65223 -1.2901
== Save and restore state to reproduce results ===
You now read and write directly to a state property. You can do this without needing to know what generator is active (even the legacy generators).
>> dfltStream = RandStream.getDefaultStream;
>> savedState = dfltStream.State;
>> rand(2)
ans =
0.11357 0.48166
0.91288 0.85181
>> dfltStream.State = savedState;
>> rand(2)
ans =
0.11357 0.48166
0.91288 0.85181
=== Reinitialize the generators ===
You now call a method that has a name describing exactly what it does: "reset". You can do this without needing to know what generator is active (with the caveat I mentioned earlier in the thread).
>> reset(RandStream.getDefaultStream) % just as if restarting MATLAB
>> reset(RandStream.getDefaultStream,1) % different than above
>> reset(RandStream.getDefaultStream,sum(100*clock)) % unpredictable reinitialization
There are some things in the new RandStream syntax you can't do with the legacy stream (including calling reset with a new seed), but any code that activates those ought to be considered deprecated. Is the problem you're having in writing code thatwill work with the legacy generators?
Just want to say that I really appreciate your reply. I thought that by now I probably won't get any response. I also appreciate your effort of making Matlab a good tool for us to use. So let me summarize what I think about the new RandStream class.
(1) This point is about creating new terms or new concepts. I understand that in order to provide new features, we sometimes need to create new concept. But Mathworks needs to understand that this imposes a learning curve to users. What makes Matlab so good and popular is that it is very easy to learn and understand (and so the learning curve is pretty minimal). There are not that many people (except the people working at Mathworks) producing Matlab code as a product. I would guess that most people are using Matlab code to design algorithm and to see what is possible (under 64-bit double precision math and lots of memory). This makes learning Matlab as a side job (for me, I have whole bunch of other things to learn to create the final product). I understand that you might say that "even though matlab provides new features, that does not mean that I need to learn and use every single new
feature. I can use whatever I need to get my job done". This is half true. I agree that I don't need to use every single new feature, other people in my team might use the new feature and this forces me to learn and fully understand what the new feature does. Let me give you an example. RandStream is a handle class and so I need to understand what is a handle class and its implication, eg. assignment statement like "h2 = h1" does not make a copy of the RandStream object. Steve already mentioned the rationale behind this and that is a design and usage decision made by Mathworks (and I am fine with it). But now, in order to understand how to generate random numbers in Matlab, I also need to understand Matlab's new OOP concept. Luckily I learned the new OOP before and so I am not starting from scratch.
(2) This point is about the documentation (I am using R2009b) on whether rand and randn are using the same random number stream or not. Now after thinking about it more and reading your reply, the answer is yes (if we are using the new RandStream object. this is true for both default stream and user-created stream) but the answer is no (if we are using legacy random stream).
(3) This is about your last posting on "Save and restore state to reproduce results". Even if I save the state, I still cannot guarantee that I will get the same random numbers. For example, after I save the state, someone's code changes the default stream to use different algorithm, eg.
>> dfltStream = RandStream.getDefaultStream;
>> savedState = dfltStream.State;
>> h = RandStream('mt19937ar', 'Seed', 101);
>> RandStream.setDefaultStream(h)
Things are not that simple anymore since Matlab allows user to set the default stream to something else. This is a gotcha. Correct me if I am wrong on this.
(4) I have posted this question in a new thread but I did not get any reply. So I will post it here. I got the impression from the documentation ("Legacy Mode") that I can use RandStream to generate the same random numbers as generated by the legacy code. But I did the following experiment and found out that I am wrong.
>> h = RandStream('mt19937ar', 'Seed', 0); rand(h, 1, 3)
ans =
0.8147 0.9058 0.1270
>> rand('twister', 0); rand(1,3)
ans =
0.5488 0.7152 0.6028
So am I having the wrong concept thinking that I can use RandStream to generate the same random numbers as generated by the legacy code.
(5) This is about the concept of Substream in those generator that supports multiple streams and substream. I admit that I know very little about random number generator algorithm. I would hope that the documentation would tell me the interaction between setting Substream and setting State in those generators. For example, if I want to generate the same random numbers in those generators (i.e. mlfg6331_64 and mrg32k3a), do I need to save-and-set both substream index and state or just either one of them?
(6) I like the fact that Mathworks is thinking ahead of me in providing parallel random number generators. I don't need to use this now. But it would be nice to know that it is there waiting for me to use. All I need to do is to pay the software maintenance fee to get the upgrade. Keep up the good work.
I guess I will stop for now. It is pretty late. Peter, since you are 3 hours ahead of me (I am assuming that you live in Boston area), I look forward to read your thoughts on my question tomorrow morning.
Good night.
> (4) I have posted this question in a new thread but I did not get any
> reply. So I will post it here. I got the impression from the
> documentation ("Legacy Mode") that I can use RandStream to generate the
> same random numbers as generated by the legacy code. But I did the
> following experiment and found out that I am wrong.
>
>>> h = RandStream('mt19937ar', 'Seed', 0); rand(h, 1, 3)
>
> ans =
>
> 0.8147 0.9058 0.1270
>
>>> rand('twister', 0); rand(1,3)
>
> ans =
>
> 0.5488 0.7152 0.6028
>
> So am I having the wrong concept thinking that I can use RandStream to
> generate the same random numbers as generated by the legacy code.
You've discovered the one inconsistency that I know of between old and new (other than rand and randn working separately). This is a bit arcane, and much/all of what follows is not something that anyone would really need to worry about, unless they happened to type exactly what you typed and said, huh?"
You will note that none of the old help recommended using 0 as in rand('twister',0), it used 5489, which is the default seed used by the designers of the Mersenne Twister algorithm. It was not clear if 0 is a "good" seed for the Mersenne Twister (it is "valid", but that's not the same thing), so in RandStream, we chose to make 0 map to the one that the designers chose as their default, because people like to type 0 and we want that to be "good". Presumably you used zero because that is the "default" seed for the even older generators activated by 'seed' and 'state' (in fact, those zeros also really map to something other than zero, for the same reason). Is this a real concern, i.e., that you can't reproduce those same numbers for that one seed, or just something you noticed?
Pick any other seed, or any other of the old generators, and results are identical.
>> rand('twister', 5489); rand(1,3)
ans =
0.81472 0.90579 0.12699
>> h = RandStream('mt19937ar', 'Seed', 5489); rand(h, 1, 3)
ans =
0.81472 0.90579 0.12699
>> rand('state', 0); rand(1,3)
ans =
0.95013 0.23114 0.60684
>> h = RandStream('swb2712', 'Seed', 0); rand(h, 1, 3)
ans =
0.95013 0.23114 0.60684
> (5) This is about the concept of Substream in those generator that
> supports multiple streams and substream. I admit that I know very little
> about random number generator algorithm. I would hope that the
> documentation would tell me the interaction between setting Substream
> and setting State in those generators. For example, if I want to
> generate the same random numbers in those generators (i.e. mlfg6331_64
> and mrg32k3a), do I need to save-and-set both substream index and state
> or just either one of them?
This one I know I answered on another thread. In short, either, not both. Setting the substream index alters the state. Each substream index is in effect an alias for a specific internal state. You might then say, "Isn't that what seeds are for?", and I direct you to the blog entries I cited in my full response.
> (2) This point is about the documentation (I am using R2009b) on whether
> rand and randn are using the same random number stream or not. Now after
> thinking about it more and reading your reply, the answer is yes (if we
> are using the new RandStream object. this is true for both default
> stream and user-created stream) but the answer is no (if we are using
> legacy random stream).
We're looking at what improvements the doc needs. Explaining the difference between "legacy mode" and the current behavior is definitely one of those areas. Thanks for pointing out these specific places we need to focus on.
> (3) This is about your last posting on "Save and restore state to
> reproduce results". Even if I save the state, I still cannot guarantee
> that I will get the same random numbers. For example, after I save the
> state, someone's code changes the default stream to use different
> algorithm, eg.
>
>>> dfltStream = RandStream.getDefaultStream;
>>> savedState = dfltStream.State;
>>> h = RandStream('mt19937ar', 'Seed', 101);
>>> RandStream.setDefaultStream(h)
>
> Things are not that simple anymore since Matlab allows user to set the
> default stream to something else. This is a gotcha. Correct me if I am
> wrong on this.
You are right, but that has ALWAYS been true, even when you were typing things like rand('state',0). And in that older world, there was no good way to even tell that that had happened. Now at least you can get the (current) default stream and easily tell if it's been changed out from under you.
Actually, if you run your example in a fresh MATLAB session, you _will_ get what you want, but not for the right reasons. If however, someone had typed 'mrg32k3a', then your saved state would not be relevant for the (now) current default stream. But again, that's ALWAYS been true.
I can't tell you what to do about this, because I don't know the context in which you are writing this code. Code that wants to be a "good citizen" should _never_ replace the default stream, any more than code buried in some function, and written for MATLAB prior to R2009b, should have done things like rand('state',0), or for that matter, "close all force". It has global side effects. There aren't many things in MATLAB that have global side effects but there are some. Messing with the default stream (other than drawing values from it) is something that code in functions should leave to the "end user" as much as possible, so that they know and control what is going on. I touch on this in the second of these two blog entries:
<http://blogs.mathworks.com/loren/2008/11/05/new-ways-with-random-numbers-part-i/>
<http://blogs.mathworks.com/loren/2008/11/13/new-ways-with-random-numbers-part-ii/>
Just to elaborate on what would happen in your example: if your code did this:
dfltStream = RandStream.getDefaultStream;
savedState = dfltStream.State;
and then some code that you called did this:
RandStream.setDefaultStream(RandStream('mrg32k3a','Seed',101))
and then your code did this:
dfltStream.State = savedState;
or even
dfltStream = RandStream.getDefaultStream;
dfltStream.State = savedState;
you'd get an error, because the internal states for those two generators are a completely different format.
Thanks for you reply to my question (4). Now I understand what is going on. Thanks for sharing the info here. Is it possible for you to mention this in the Matlab documentation, something like this: The following two methods will generate identical random streams except for seed = 0,
Method 1:
---------------
h = RandStream('mt19937ar', 'Seed', seed);
rand(h, 1, 3)
Method 2:
---------------
rand('twister', 0);
rand(1,3)
Now I think about this more. I get a bit confused. Do both RandStream('mt19937ar') and rand('twister', seed) use the same Mersenne twister algorithm? Or it is the mapping of 0 being different in the old and new ways?
This is a concern to me because I would like to replace the old rand and randn with RandStream and at the same time I want to make sure that I get the same random numbers.
I just happen to choose 0 as my seed in my example. I can add a checking in my code to avoid using seed 0.
Peter Perkins <Peter....@MathRemoveThisWorks.com> wrote in message <hnorhg$5a8$1...@fred.mathworks.com>...
> > You might then say, "Isn't that what seeds are for?", and I direct you to the blog entries I cited in my full response.
I guess my answer is that it is not a good idea to use different seeds to generate different random numbers (it works) but the random numbers may not have a good statistical properties. I trust that the algorithm will do the right thing and all I do is to save-and-restore the state when I generate the random numbers.
Peter Perkins <Peter....@MathRemoveThisWorks.com> wrote in message <hnoro9$89t$1...@fred.mathworks.com>...
Thanks a lot for all the information being shared here. I learned a great deal. This is a very good discussion on RandStream.
> Now I think about this more. I get a bit confused. Do both
> RandStream('mt19937ar') and rand('twister', seed) use the same Mersenne
> twister algorithm? Or it is the mapping of 0 being different in the old
> and new ways?
There are more than one Mersenne Twister algorithms (which is why we used the apparently cryptic string 'mt19937ar' to identify this ONE -- it is completely unambiguous, google it and see). But the old rand('twister') and the new RandStream('mt19937ar') are the same one, and the thing you noticed about a zero seed is the ONLY difference.
> This is a concern to me because I would like to replace the old rand and
> randn with RandStream and at the same time I want to make sure that I
> get the same random numbers.
I can't say whether or not this is an appropriate requirement, but unless you are trying to replicate a benchmark test, it seems unnecessary. Most MATLAB users _won't_ need to worry about this.
If the need to replicate exactly _is_ for a benchmark, then you have to consider that RAND and RANDN are now drawing from the same stream. In addition, while the Mersenne Twister that you get at MATLAB startup as of R2008b replicates the values from RAND exactly, the values from RANDN are different -- you'd need to use a stream created as RandStream('shr3cong') to replicate the old RANDN behavior. If you only need to replicate one or the other, or you can live with using two streams, then do that, but otherwise, consider using rand('twister',...) and randn('state',...) and ignoring the M-Lint warnings.
Again, none of the above is important unless you really care about getting exactly the same random numbers as you did prior to 2008b, and in most cases, you shouldn't.