Use ran1 FORTRAN 77 subroutine in FORTRAN 90 - random

I am trying to use ran1 from Numerical Recipes in my FORTRAN 90 code. I think a common way is to compile the old subroutine separately, then use the object file. But here I want to know what change is necessary to use it directly in my code.
FUNCTION ran1(idum)
INTEGER idum,IA,IM,IQ,IR,NTAB,NDIV
REAL ran1,AM,EPS,RNMX
PARAMETER (IA=16807,IM=2147483647,AM=1./IM,IQ=127773,IR=2836,
! NTAB=32,NDIV=1+(IM-1)/NTAB,EPS=1.2e-7,RNMX=1.-EPS)
! “Minimal” random number generator of Park and Miller with Bays-Durham shuffle and
! added safeguards. Returns a uniform random deviate between 0.0 and 1.0 (exclusive of
! the endpoint values). Call with idum a negative integer to initialize; thereafter, do not
! alter idum between successive deviates in a sequence. RNMX should approximate the largest
! floating value that is less than 1.
INTEGER j,k,iv(NTAB),iy
SAVE iv,iy
DATA iv /NTAB*0/, iy /0/
iy = 0
if (idum.le.0.or.iy.eq.0) then !Initialize.
idum=max(-idum,1)
! Be sure to prevent idum = 0.
do 11 j=NTAB+8,1,-1
! Load the shuffle table (after 8 warm-ups).
k=idum/IQ
idum=IA*(idum-k*IQ)-IR*k
if (idum.lt.0) idum=idum+IM
if (j.le.NTAB) iv(j)=idum! Compute idum=mod(IA*idum,IM) without overflows by
enddo 11
iy=iv(1)
endif
k=idum/IQ
idum=IA*(idum-k*IQ)-IR*k
! Compute idum=mod(IA*idum,IM) without overflows by
if (idum.lt.0) idum=idum+IM ! Schrage’s method.
j=1+iy/NDIV
iy=iv(j) ! Output previously stored value and refill the shuffle table.
iv(j)=idum
ran1=min(AM*iy,RNMX) ! Because users don’t expect endpoint values.
return
END

Your code is malformed. It looks like you copied it manually from the book, but not exactly. The second problem is actually present even in the book.
Firstly, there should be a line continuation and not a comment in the parameter statement on the second and third line
PARAMETER (IA=16807,IM=2147483647,AM=1./IM,IQ=127773,IR=2836, &
NTAB=32,NDIV=1+(IM-1)/NTAB,EPS=1.2e-7,RNMX=1.-EPS)
(converted to free form, see the book for the original)
Secondly, the loop is a strange combination of a do loop with numeric label and a do loop with end do:
do 11 j=NTAB+8,1,-1
...
enddo 11
should be
do j=NTAB+8,1,-1
...
enddo
or
do 11 j=NTAB+8,1,-1
...
11 continue
There may be more problems present.

Related

drand and drandm in fortran are giving numbers outside of [0,1] range

here is my script below, I use implicit none because I would like to eventually implement them in a bigger program with more variables.
program testdrandm
implicit none
real, external :: drand, drandm, rand
print *, 'drand', drand(0), drand(0)
print *, 'drandm', drandm(0), drandm(0)
print *, 'rand', rand(0), rand(0)
end program testdrandm
here is my output:
drand 4.3290930E-39 -686.1465
drandm -8.9381798E+10 1.7946890E+19
rand 0.9679557 0.1896898
The first number is within range but extremely small and will give me zero values when I use it to multiply other values. Rand works but I would like to use drandm. I would like to get random numbers between 0 to 1. Please let me know if I am using this function incorrectly.
You should use the intrinsic random_seed and random_number to generate random numbers in Fortran. The intrinsic random_number will give you real number(s) between 0 and 1.
See e.g.:
https://gcc.gnu.org/onlinedocs/gfortran/RANDOM_005fSEED.html#RANDOM_005fSEED
https://gcc.gnu.org/onlinedocs/gfortran/RANDOM_005fNUMBER.html#RANDOM_005fNUMBER
#tim18 touched on the answer, but you might not have picked up on exactly WHY you got these results.
I modified the program to print the hex representation of the values returned. When run using ifort, I get:
drand 00000000002F23C0 00000000C42B8960
drandm 00000000D1A67C90 000000005F791029
rand 000000003F77CBF2 000000003E423E09
IEEE double precision is an 8-byte format, and drand/drandm return 8 bytes, but you declared them as real (single precision), so you get only the low 4 bytes and NOT a conversion. Because the size of the exponent field is different between these types (8 bits vs. 11 bits), interpreting the low 4 bytes of a double as a real will get you wrong values.
Now see what happens if I declare drand and drandm as double precision:
drand 3EF791E0002F23C0 3FB5C4AFC42B8960
drandm 3FE33E47D1A67C90 3FEC88145F791029
rand 000000003F77CBF2 000000003E423E09
or if I go back to list-directed:
drand 2.247793601009899E-005 8.503244914348818E-002
drandm 0.601352605317418 0.891611277075303
rand 0.9679557 0.1896898
Better?
That said, I wholly agree with those who suggest using RANDOM_NUMBER instead. You would not have seen this sort of problem if you used the intrinsic procedure.

How to change seed number in Fortran stochastic simulator code

I'm running a Fortran code which performs a stochastic simulation of a marked Poisson cluster process. In practice, event properties (eg. time of occurrences) are generated by inversion method, i.e. by random sampling of the cumulative distribution function.
Because of the Poissonian randomness, I expect each generated sequence to be different, but this is not the case. I guess the reason is that the seed for the pseudorandom number generator is the same at each simulation.
I do not know Fortran, so I have no idea how to solve this issue. Here is the part of the code involved with the pseudorandom number generator, any idea?
subroutine pseud0(r)
c generation of pseudo-random numbers
c data ir/584287/
data ir/574289/
ir=ir*48828125
if(ir) 10,20,20
10 ir=(ir+2147483647)+1
20 r=float(ir)*0.4656613e-9
return
end
subroutine pseudo(random)
c wichmann+hill (1982) Appl. Statist 31
data ix,iy,iz /1992,1111,1151/
ix=171*mod(ix,177)-2*(ix/177)
iy=172*mod(iy,176)-35*(iy/176)
iz=170*mod(iz,178)-63*(iz/178)
if (ix.lt.0) ix=ix+30269
if (iy.lt.0) iy=iy+30307
if (iz.lt.0) iz=iz+30323
random=mod(float(ix)/30269.0+float(iy)/30307.0+
& float(iz)/30323.0,1.0)
return
end
First, I would review the modern literature for PRNG and pick a modern implementation. Second, I would rewrite the code in modern Fortran.
You need to follow #francescalus advice and have a method for updating the seed. Without attempting to modernizing your code, here is one method for the pseud0 prng
subroutine init0(i)
integer, intent(in) :: i
common /myseed0/iseed
iseed = i
end subroutine init0
subroutine pseud0(r)
common /myseed0/ir
ir = ir * 48828125
if (ir) 10,20,20
10 ir = (ir+2147483647)+1
20 r = ir*0.4656613e-9
end subroutine pseud0
program foo
integer i
real r1
call init0(574289) ! Original seed
do i = 1, 10
call pseud0(r1)
print *, r1
end do
print *
call init0(289574) ! New seed
do i = 1, 10
call pseud0(r1)
print *, r1
end do
print *
end program foo

MT19937 does NOT reproduce the same pseudo-random sequence by holding the seed value a constant

I'm writing a checkpoint function in my Monte Carlo simulation in Fortran 90/95, the compiler I'm using is ifort 18.0.2, before going through detail just to clarify the version of pseudo-random generator I'm using:
A C-program for MT19937, with initialization, improved 2002/1/26.
Coded by Takuji Nishimura and Makoto Matsumoto.
Code converted to Fortran 95 by Josi Rui Faustino de Sousa
Date: 2002-02-01
See mt19937 for the source code.
The general structure of my Monte Carlo simulation code is given below:
program montecarlo
call read_iseed(...)
call mc_subroutine(...)
end
Within the read_iseed
subroutine read_iseed(...)
use mt19937
if (Restart == 'n') then
call system('od -vAn -N4 -td4 < /dev/urandom > '//trim(IN_ISEED)
open(unit=7,file=trim(IN_ISEED),status='old')
read(7,*) i
close(7)
!This is only used to initialise the PRNG sequence
iseed = abs(i)
else if (Restart == 'y') then
!Taking seed value from the latest iteration of previous simulation
iseed = RestartSeed
endif
call init_genrand(iseed)
print *, 'first pseudo-random value ',genrand_real3(), 'iseed ',iseed
return
end subroutine
Based on my understanding, if the seed value holds a constant, the PRNG should be able to reproduce the pseudo-random sequence every time?
In order to prove this is the case, I ran two individual simulations by using the same seed value, they are able to reproduce the exact sequence. So far so good!
Based on the previous test, I'd further assume that regardless the number of times init_genrand() being called within one individual simulation, the PRNG should also be able to reproduce the pseudo-random value sequence? So I did a little modification to my read_iseed() subroutine
subroutine read_iseed(...)
use mt19937
if (Restart == 'n') then
call system('od -vAn -N4 -td4 < /dev/urandom > '//trim(IN_ISEED)
open(unit=7,file=trim(IN_ISEED),status='old')
read(7,*) i
close(7)
!This is only used to initialise the PRNG sequence
iseed = abs(i)
else if (Restart == 'y') then
!Taking seed value from the latest iteration of the previous simulation
iseed = RestartSeed
endif
call init_genrand(iseed)
print *, 'first time initialisation ',genrand_real3(), 'iseed ',iseed
call init_genrand(iseed)
print *, 'second time initialisation ',genrand_real3(), 'iseed ',iseed
return
end subroutine
The output is surprisingly not the case I thought would be, by all means iseed outputs are identical in between two initializations, however, genrand_real3() outputs are not identical.
Because of this unexpected result, I struggled with resuming the simulation at an arbitrary state of the system since the simulation is not reproducing the latest configuration state of the system I'm simulating.
I'm not sure if I've provided enough information, please let me know if any part of this question needs to be more specific?
From the source code you've provided (See [mt19937]{http://web.mst.edu/~vojtat/class_5403/mt19937/mt19937ar.f90} for the source code.), the init_genrand does not clear the whole state.
There are 3 critical state variables:
integer( kind = wi ) :: mt(n) ! the array for the state vector
logical( kind = wi ) :: mtinit = .false._wi ! means mt[N] is not initialized
integer( kind = wi ) :: mti = n + 1_wi ! mti==N+1 means mt[N] is not initialized
The first one is the "array for the state vector", second one is a flag that ensures we don't start with uninitialized array, and the third one is some position marker, as I guess from the condition stated in the comment.
Looking at subroutine init_genrand( s ), it sets mtinit flag, and fills the mt() array from 1 upto n. Alright.
Looking at genrand_real3 it's based on genrand_int32.
Looking at genrand_int32, it starts up with
if ( mti > n ) then ! generate N words at one time
! if init_genrand() has not been called, a default initial seed is used
if ( .not. mtinit ) call init_genrand( seed_d )
and does its arithmetic magic and then starts getting the result:
y = mt(mti)
mti = mti + 1_wi
so.. mti is a positional index in the 'state array', and it is incremented by 1 after each integer read from the generator.
Back to init_genrand - remember? it have been resetting the array mt() but it has not resetted the MTI back to its starting mti = n + 1_wi.
I bet this is the cause of the phenomenon you've observed, since after re-initializing with the same seed, the array would be filled with the same set of values, but later the int32 generator would read from a different starting point. I doubt it was intended, so it's probably a tiny bug easy to overlook.

Matlab subscript error

I am writing a simple code in matlab which has the purpose of creating the histogram of a grayscale image without using the function hist. I am stuck at the point in which mathlab displays the error "Subscript indices must either be real positive integers or logicals." Can you help me finding where is the wrong indices?
indirizzo='file.jpg';
immagine=imread(indirizzo);
immaginebn=rgb2gray(immagine);
n=zerps(0,255);
for x=0:255;
numeroennesimo=sum(sum(immaginebn==x));
n(x)=numeroennesimo;
end
plot(x,n)
you cant use 0 as index. Either make n(x+1) or for x = 1:256 and substract the 1 in your comparison. And there is a typo, I guess it means zeros instead of zerps, which also doesnt work with a 0. And one more, your plot will also not work as the x has only a size of 1 while n is an array of 266 and for a histogram I would use a barplot instead.
indirizzo='file.jpg';
immagine=imread(indirizzo);
immaginebn=rgb2gray(immagine);
n=zeros(1,256);
for x=0:255;
numeroennesimo=sum(sum(immaginebn==x-1));
n(x+1)=numeroennesimo;
end
bar(0:255,n)
or
indirizzo='file.jpg';
immagine=imread(indirizzo);
immaginebn=rgb2gray(immagine);
n=zeros(1,256);
xplot=zeros(1,256);
for x=1:256;
numeroennesimo=sum(sum(immaginebn==x-1));
n(x)=numeroennesimo;
xplot(x) = x-1;
end
plot(xplot,n)

numerical recipies ran3 generates negative numbers

I am using numerical recipes scheme to generate random numbers (ran3, page 7 in this PDF file). I didn't notice anything strange but this time, I got a negative numbers at the "warm up" stage which are larger than MBIG. The code look as if this shouldn't happen. I can easily fix this with changing the if statement to be a while statement at the line that says if(mk.lt.MZ)mk=mk+MBIG but I want to know what are the consequences.
Edit:here is the function
FUNCTION ran3a(idum)
INTEGER idum
INTEGER MBIG,MSEED,MZ
C REAL MBIG,MSEED,MZ
REAL ran3a,FAC
PARAMETER (MBIG=1000000000,MSEED=161803398,MZ=0,FAC=1./MBIG)
C PARAMETER (MBIG=4000000.,MSEED=1618033.,MZ=0.,FAC=1./MBIG)
INTEGER i,iff,ii,inext,inextp,k
INTEGER mj,mk,ma(55)
C REAL mj,mk,ma(55)
SAVE iff,inext,inextp,ma
DATA iff /0/
if(idum.lt.0.or.iff.eq.0)then
iff=1
mj=MSEED-iabs(idum)
mj=mod(mj,MBIG)
ma(55)=mj
mk=1
do 11 i=1,54
ii=mod(21*i,55)
ma(ii)=mk
mk=mj-mk
if(mk.lt.MZ)mk=mk+MBIG
mj=ma(ii)
11 continue
do 13 k=1,4
do 12 i=1,55
ma(i)=ma(i)-ma(1+mod(i+30,55))
if(ma(i).lt.MZ)ma(i)=ma(i)+MBIG
12 continue
13 continue
inext=0
inextp=31
idum=1
endif
inext=inext+1
if(inext.eq.56)inext=1
inextp=inextp+1
if(inextp.eq.56)inextp=1
mj=ma(inext)-ma(inextp)
if(mj.lt.MZ)mj=mj+MBIG
ma(inext)=mj
ran3a=mj*FAC
return
END
I was getting Seg Faults (using gfortran 4.8) because the function was trying to change the input value idum from the negative number to 1. There is no reason for that line (nor anything with iff), so I deleted it and printed out the array ma at several different places and found no negative numbers in the array.
One possibility, though, is if iabs(idum) is larger than MSEED, you might have a problem with the line mj=MSEED - iabs(idum). You should protect from this by using mj=abs(MSEED-abs(idum)) like the book has written.
Had a look at the pdf. What you need to do is
1) Seed it: value = ran3(-1)
2) Use it: value = ran3(0)

Resources