From: mjm2114 on
Hi there,
I have a question on a naive implementation of a parallel MT that I've
done using the fortran version of MT19937ar.f posted in Prof.
Matsumoto's website. (http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/
VERSIONS/FORTRAN/mt19937ar.f)

First, I setup an MT in the master node and seed it with a single
integer using subroutine init_genrand(seed). I then use the first 624
outputs from the master node to seed the MT in the first worker node
using subroutine init_by_array(array_of_seeds,624). I continue in the
same manner for all subsequent worker nodes, taking the next 624
outputs from the master node and using them to seed the MT in all
worker nodes. Once this is done, I have a different MT ready for use
in all the worker nodes. Do you think this is a good approach? I know
that the all blocks of 624 seeds used to set up the worker node MTs
have some correlation (since they are adjacent blocks produced by the
same MT), but given the equidistribution properties and the gigantic
period of the algorithm, wouldn't these correlations be insignificant
for all practical purposes? Would it be better if I used a different
PRNG algorithm for the master node? If that is so, which one would you
recommend? Are gfortran's RAND or RANDOM_NUMBER up to the task?

I know I can't expect perfectly uncorrelated streams of outputs in
each node as in serious parallel PRNGs (i.e. SPRNG), but I'm only
going to use the code for small workstations/clusters of 8-16 nodes
(using mpi).

I also have a question on the output of the MT. The output is supposed
to be signed 32-bit integers, there should be on problem in using
negative integers as seeds for the initialization subroutine right?

Here's the code

c
c A C-program for MT19937, with initialization improved 2002/1/26.
c Coded by Takuji Nishimura and Makoto Matsumoto.
c
c Before using, initialize the state by using init_genrand(seed)
c or init_by_array(init_key, key_length).
c
c Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
c All rights reserved.
c Copyright (C) 2005, Mutsuo Saito,
c All rights reserved.
c
c Redistribution and use in source and binary forms, with or without
c modification, are permitted provided that the following conditions
c are met:
c
c 1. Redistributions of source code must retain the above copyright
c notice, this list of conditions and the following disclaimer.
c
c 2. Redistributions in binary form must reproduce the above
copyright
c notice, this list of conditions and the following disclaimer
in the
c documentation and/or other materials provided with the
distribution.
c
c 3. The names of its contributors may not be used to endorse or
promote
c products derived from this software without specific prior
written
c permission.
c
c THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
c "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
c LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR
c A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR
c CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL,
c EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
c PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
c PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF
c LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING
c NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
c SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
c
c
c Any feedback is very welcome.
c http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
c email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
c
c-----------------------------------------------------------------------
c FORTRAN77 translation by Tsuyoshi TADA. (2005/12/19)
c
c ---------- initialize routines ----------
c subroutine init_genrand(seed): initialize with a seed
c subroutine init_by_array(init_key,key_length): initialize by an
array
c
c ---------- generate functions ----------
c integer function genrand_int32(): signed 32-bit integer
c integer function genrand_int31(): unsigned 31-bit integer
c double precision function genrand_real1(): [0,1] with 32-bit
resolution
c double precision function genrand_real2(): [0,1) with 32-bit
resolution
c double precision function genrand_real3(): (0,1) with 32-bit
resolution
c double precision function genrand_res53(): (0,1) with 53-bit
resolution
c
c This program uses the following non-standard intrinsics.
c ishft(i,n): If n>0, shifts bits in i by n positions to left.
c If n<0, shifts bits in i by n positions to right.
c iand (i,j): Performs logical AND on corresponding bits of i and
j.
c ior (i,j): Performs inclusive OR on corresponding bits of i and
j.
c ieor (i,j): Performs exclusive OR on corresponding bits of i and
j.
c
c-----------------------------------------------------------------------
c initialize mt(0:N-1) with a seed
c-----------------------------------------------------------------------
subroutine init_genrand(s)
integer s
integer N
integer DONE
integer ALLBIT_MASK
parameter (N=624)
parameter (DONE=123456789)
integer mti,initialized
integer mt(0:N-1)
common /mt_state1/ mti,initialized
common /mt_state2/ mt
common /mt_mask1/ ALLBIT_MASK
c
call mt_initln
mt(0)=iand(s,ALLBIT_MASK)
do 100 mti=1,N-1
mt(mti)=1812433253*
& ieor(mt(mti-1),ishft(mt(mti-1),-30))+mti
mt(mti)=iand(mt(mti),ALLBIT_MASK)
100 continue
initialized=DONE
c
return
end
c-----------------------------------------------------------------------
c initialize by an array with array-length
c init_key is the array for initializing keys
c key_length is its length
c-----------------------------------------------------------------------
subroutine init_by_array(init_key,key_length)
integer init_key(0:*)
integer key_length
integer N
integer ALLBIT_MASK
integer TOPBIT_MASK
parameter (N=624)
integer i,j,k
integer mt(0:N-1)
common /mt_state2/ mt
common /mt_mask1/ ALLBIT_MASK
common /mt_mask2/ TOPBIT_MASK
c
call init_genrand(19650218)
i=1
j=0
do 100 k=max(N,key_length),1,-1
mt(i)=ieor(mt(i),ieor(mt(i-1),ishft(mt(i-1),-30))*1664525)
& +init_key(j)+j
mt(i)=iand(mt(i),ALLBIT_MASK)
i=i+1
j=j+1
if(i.ge.N)then
mt(0)=mt(N-1)
i=1
endif
if(j.ge.key_length)then
j=0
endif
100 continue
do 200 k=N-1,1,-1
mt(i)=ieor(mt(i),ieor(mt(i-1),ishft(mt(i-1),-30))*1566083941)-
i
mt(i)=iand(mt(i),ALLBIT_MASK)
i=i+1
if(i.ge.N)then
mt(0)=mt(N-1)
i=1
endif
200 continue
mt(0)=TOPBIT_MASK
c
return
end
c-----------------------------------------------------------------------
c generates a random number on [0,0xffffffff]-interval
c-----------------------------------------------------------------------
function genrand_int32()
integer genrand_int32
integer N,M
integer DONE
integer UPPER_MASK,LOWER_MASK,MATRIX_A
integer T1_MASK,T2_MASK
parameter (N=624)
parameter (M=397)
parameter (DONE=123456789)
integer mti,initialized
integer mt(0:N-1)
integer y,kk
integer mag01(0:1)
common /mt_state1/ mti,initialized
common /mt_state2/ mt
common /mt_mask3/ UPPER_MASK,LOWER_MASK,MATRIX_A,T1_MASK,T2_MASK
common /mt_mag01/ mag01
c
if(initialized.ne.DONE)then
call init_genrand(21641)
endif
c
if(mti.ge.N)then
do 100 kk=0,N-M-1
y=ior(iand(mt(kk),UPPER_MASK),iand(mt(kk+1),LOWER_MASK))
mt(kk)=ieor(ieor(mt(kk+M),ishft(y,-1)),mag01(iand(y,1)))
100 continue
do 200 kk=N-M,N-1-1
y=ior(iand(mt(kk),UPPER_MASK),iand(mt(kk+1),LOWER_MASK))
mt(kk)=ieor(ieor(mt(kk+(M-N)),ishft(y,-1)),mag01(iand(y,1)))
200 continue
y=ior(iand(mt(N-1),UPPER_MASK),iand(mt(0),LOWER_MASK))
mt(kk)=ieor(ieor(mt(M-1),ishft(y,-1)),mag01(iand(y,1)))
mti=0
endif
c
y=mt(mti)
mti=mti+1
c
y=ieor(y,ishft(y,-11))
y=ieor(y,iand(ishft(y,7),T1_MASK))
y=ieor(y,iand(ishft(y,15),T2_MASK))
y=ieor(y,ishft(y,-18))
c
genrand_int32=y
return
end
c-----------------------------------------------------------------------
c generates a random number on [0,0x7fffffff]-interval
c-----------------------------------------------------------------------
function genrand_int31()
integer genrand_int31
integer genrand_int32
genrand_int31=int(ishft(genrand_int32(),-1))
return
end
c-----------------------------------------------------------------------
c generates a random number on [0,1]-real-interval
c-----------------------------------------------------------------------
function genrand_real1()
double precision genrand_real1,r
integer genrand_int32
r=dble(genrand_int32())
if(r.lt.0.d0)r=r+2.d0**32
genrand_real1=r/4294967295.d0
return
end
c-----------------------------------------------------------------------
c generates a random number on [0,1)-real-interval
c-----------------------------------------------------------------------
function genrand_real2()
double precision genrand_real2,r
integer genrand_int32
r=dble(genrand_int32())
if(r.lt.0.d0)r=r+2.d0**32
genrand_real2=r/4294967296.d0
return
end
c-----------------------------------------------------------------------
c generates a random number on (0,1)-real-interval
c-----------------------------------------------------------------------
function genrand_real3()
double precision genrand_real3,r
integer genrand_int32
r=dble(genrand_int32())
if(r.lt.0.d0)r=r+2.d0**32
genrand_real3=(r+0.5d0)/4294967296.d0
return
end
c-----------------------------------------------------------------------
c generates a random number on [0,1) with 53-bit resolution
c-----------------------------------------------------------------------
function genrand_res53()
double precision genrand_res53
integer genrand_int32
double precision a,b
a=dble(ishft(genrand_int32(),-5))
b=dble(ishft(genrand_int32(),-6))
if(a.lt.0.d0)a=a+2.d0**32
if(b.lt.0.d0)b=b+2.d0**32
genrand_res53=(a*67108864.d0+b)/9007199254740992.d0
return
end
c-----------------------------------------------------------------------
c initialize large number (over 32-bit constant number)
c-----------------------------------------------------------------------
subroutine mt_initln
integer ALLBIT_MASK
integer TOPBIT_MASK
integer UPPER_MASK,LOWER_MASK,MATRIX_A,T1_MASK,T2_MASK
integer mag01(0:1)
common /mt_mask1/ ALLBIT_MASK
common /mt_mask2/ TOPBIT_MASK
common /mt_mask3/ UPPER_MASK,LOWER_MASK,MATRIX_A,T1_MASK,T2_MASK
common /mt_mag01/ mag01
CC TOPBIT_MASK = Z'80000000'
CC ALLBIT_MASK = Z'ffffffff'
CC UPPER_MASK = Z'80000000'
CC LOWER_MASK = Z'7fffffff'
CC MATRIX_A = Z'9908b0df'
CC T1_MASK = Z'9d2c5680'
CC T2_MASK = Z'efc60000'
TOPBIT_MASK=1073741824
TOPBIT_MASK=ishft(TOPBIT_MASK,1)
ALLBIT_MASK=2147483647
ALLBIT_MASK=ior(ALLBIT_MASK,TOPBIT_MASK)
UPPER_MASK=TOPBIT_MASK
LOWER_MASK=2147483647
MATRIX_A=419999967
MATRIX_A=ior(MATRIX_A,TOPBIT_MASK)
T1_MASK=489444992
T1_MASK=ior(T1_MASK,TOPBIT_MASK)
T2_MASK=1875247104
T2_MASK=ior(T2_MASK,TOPBIT_MASK)
mag01(0)=0
mag01(1)=MATRIX_A
return
end

Any advice would be sincerely appreciated. Keep up the good work!

Manuel
From: mjm2114 on
Thanks for your reply. Maybe I wasn't clear. I take the first block of
624 outputs from the master node to seed the first worker node. I then
take the second block of 624 outputs from the master node to seed the
second worker node, and so forth. In this way, every worker node has
been initialized with a different array of seeds. The master node is
only used to produce seeds for the worker nodes, it is not used for
anything else.
From: mjm2114 on
On Apr 24, 12:01 pm, Gordon Sande <g.sa...(a)worldnet.att.net> wrote:
> On 2008-04-24 12:22:49 -0300, mjm2...(a)columbia.edu said:
>
> > Thanks for your reply. Maybe I wasn't clear. I take the first block of
> > 624 outputs from the master node to seed the first worker node. I then
> > take the second block of 624 outputs from the master node to seed the
> > second worker node, and so forth. In this way, every worker node has
> > been initialized with a different array of seeds. The master node is
> > only used to produce seeds for the worker nodes, it is not used for
> > anything else.
>
> Your description still makes it sound like each node will be only
> a delayed version of a lower numbered node.
>
> The internal state of the PRNG is usually called the seed. There may be
> shortcut initial startups, labelled seeds, for those generators that have
> large seeds like MT. Leads to confusion on the meaning of the words when
> the adjectives are dropped. The purpose of a full seed is to pick up again
> EXACTLY where you left off. Having the master step along and give seeds
> to each slave just means that the slaves will exactly follow what the
> master would have done. Exact reproducability of PRNGs is important for
> debugging. That is what the saving and restoring of seeds is all about.
>
> It would be easy to form the conclusion that you are rather confused
> about what the role of the seed is. Neither your initial nor followup
> posting serve to dispell that. For MT, "THE SEED" is all of the 600+
> words. It is not an array of seeds. How else do you think they achieve
> their immense cycle length?

Thanks again for your comments. You are correct, my terminology was
confusing.
If we consider "seed" as synonymous with internal state then MT has a
seed of 624 32-bit integers.
To initialize the internal state of an MT in each worker node I use a
contiguous chunk of outputs from the MT in the master node. This chunk
happens to have length 624 (recommended by MT literature) but is is
not going to be the same as the initial internal state in the worker
MT (algorithm takes care of this), nor is it equal to the previous
internal state in the master node (because this previous state was
only used to create the last number in the chunk). Hope this clears
things up.

thanks again for your comments

Manuel
From: e p chandler on
On Apr 24, 3:52 pm, Gordon Sande <g.sa...(a)worldnet.att.net> wrote:

> In the case of the "one word internal state" a case could be made
> that the seed could take states that were not internally possible
> but that the reported internal state would be a seed as well. This
> was for the situation where the 31 bits had to end in "01" (3 mod 8)
> so some of bits in a 32 bit seed could be invalid. Seed is a curious
> uasge at best but seems to be rather well establshed.

True only if the additive constant is ZERO.

> The OP seemed to be saying that each word in the many word internal
> state was a seed so in an attempt to draw his attention to the whole
> thing I used "full seed" on the fly. Better suggestions are welcome. ;-)

I'll stay away from this. :-(. IMO it's more important to make sure
that ALL bits of the initial state are initialized AND that all bits
are initialized properly. [For a counter example look at how MS
Basic's RANDOMIZE n works in version 5.x]

[Sorry it's been years since I've read about GFSR routines. Back then
not all the bits could be zero. Now it seems that an excess of zero
bits in the intial state is bad.... Too complex for me to understand
anyway. :-(.]

-- e