1

It appears that SAS/IML has the ability to reset it's random number stream (doc link) .

Is there a similar feature for random number routines in the SAS data step?

Based on this post, it seems that subsequent calls to streaminit are ignored within a single datastep.

For example, the below code produces different random numbers for each row:

data want;
  do i = 1 to 2;
    call streaminit(123);  * <-- WANT THIS TO RESET THE STREAM;
    ran1 = rand('uniform');      
    ran2 = rand('uniform');      
    ran3 = rand('uniform');      
    put _all_;
    output;
  end;
run;

Output:

i=1 ran1=0.5817000773 ran2=0.0356216603 ran3=0.0781806207 
i=2 ran1=0.3878454913 ran2=0.3291709244 ran3=0.3615948586 

I would like the output to be:

i=1 ran1=0.5817000773 ran2=0.0356216603 ran3=0.0781806207 
i=2 ran1=0.5817000773 ran2=0.0356216603 ran3=0.0781806207 
Robert Penridge
  • 8,424
  • 2
  • 34
  • 55
  • Use the CALL routines for random numbers instead of the RAN* functions. The routines let you manage independent streams of random values. The use case for you is that when two or more streams are all initialized with the same seed they will deliver the same sequence of values. – Richard Dec 04 '17 at 19:18
  • Out of curiosity, why do you want to do this? – Rick Dec 29 '17 at 00:19
  • @Rick I was testing out some different encryption algorithms and wanted to be able to replay the same random values within an fcmp function whenever it was called. – Robert Penridge Jan 30 '18 at 00:44

3 Answers3

1

You could work around this using generated code, though, with CALL EXECUTE or perhaps DOSUBL, for example:

data _null_;
  do i = 1 to 2;
    rc=dosubl(cats("data want_",i,";
    call streaminit(123);  * <-- WANT THIS TO RESET THE STREAM;
    ran1 = rand('uniform');      
    ran2 = rand('uniform');      
    ran3 = rand('uniform');    
    i=",i,"; 
    put _all_;
    output;
    run;
    "));
  end;
  rc = dosubl("data want; set want_1 want_2; run;");
run;

Obviously easier/better to write a macro to do this part.

This is a limitation unfortunately of the 'new' RAND routine; the old one was much easier to work with in this regard (as it actually truly had just one seed). The new one's seed properties are more complex, and so while you can initialize it with a single number, it's not as straightforward, hence the complications.

Joe
  • 62,789
  • 6
  • 49
  • 67
  • Randseed takes an optional second parameter to re-initialize the stream. I linked the doc in my question but it's pretty easy to miss. Here it is again: http://documentation.sas.com/?docsetId=imlug&docsetTarget=imlug_langref_sect379.htm&docsetVersion=14.3&locale=en – Robert Penridge Dec 04 '17 at 18:30
  • Ah- my apologies, I did miss that. Removed that part; the rest of my answer though I think answers your question ultimately. – Joe Dec 04 '17 at 18:31
  • No problem. I guess the ultimate answer is, no, there's no built-in way to achieve this in a data step as of 9.4TS1M5. Thanks. – Robert Penridge Dec 04 '17 at 18:34
  • I think that's by design... but yes, you'd have to work around it. You can't even use FCMP to work around it, sadly, you really do have to run separate datasteps (either literally, or using a workaround like DOSUBL or CALL EXECUTE that runs multiple datasteps). – Joe Dec 04 '17 at 18:37
  • Yup, unfortunately finding that out now as I came across the issue while trying to create an `fcmp` function. – Robert Penridge Dec 04 '17 at 18:51
  • Maybe RUN_MACRO within FCMP might get it to work? Or DS2 - perhaps each thread would separately initialize its own stream? – Joe Dec 04 '17 at 18:52
  • Either way I'd keep this open for now; Rick posts in here periodically, and he'd be the expert here. I've seen this issue in the past and I don't think there's an answer, but if there is one he'd know. – Joe Dec 04 '17 at 18:52
  • That's why I added the IML tag ;-) – Robert Penridge Dec 04 '17 at 18:53
1

You cannot reset the streams for the RAND function in SAS 9.4M4. However, you can rewind a stream in SAS 9.4M5 (which shipped in Sep 2017) by using the new STREAMREWIND routine. The following program shows the syntax:

data want;
  call streaminit(123);  
  do i = 1 to 2;
    call streamrewind;
    ran1 = rand('uniform');      
    ran2 = rand('uniform');      
    ran3 = rand('uniform');      
    put _all_;
    output;
  end;
run;
Rick
  • 1,210
  • 6
  • 11
0

You can use call ranuni to use the same seed for two different random number streams.

Note that this uses a different, inferior PRNG, with a much shorter cycle and poorer statistical properties than the rand() function.

Example:

data x;
  seed1 = 123;
  seed2 = 123;
  do i =1 to 3;
    call ranuni(seed1, x); 
    call ranuni(seed2, y); 
    output;
  end;
run;

Output:

i=1 x=0.7503960881 y=0.7503960881
i=2 x=0.3209120251 y=0.3209120251
i=3 x=0.178389649 y=0.178389649
Robert Penridge
  • 8,424
  • 2
  • 34
  • 55
  • This is true, but it should be made clear that this is _not_ the same thing as the PRNG in the question; it is a different, inferior PRNG, with a much shorter cycle and poorer statistical properties. It may well still be usable for your purposes, depending on what those purposes are. This is what I was referring to in my answer as the "old" routine. – Joe Dec 04 '17 at 19:06
  • Yeah TBH I'm not sure it solves my current issue. It does feel like it's one step closer though. Figured it may help someone else so I thought I'd post it as an answer. I think the `dosubl` approach is still the most 'correct' approach. – Robert Penridge Dec 04 '17 at 19:10