I am trying to generate a sequence of random numbers $\xi_i$ uniformly distributed in [0,1] using the built-in functions in Fortran. The sequence has to be reproducible so I want to seed the random number generator by the index $i$ (that is the position of $\xi_i$ in the sequence) rather than using the system clock for the seed. Below is my code:
module rand
contains
function generate_random(iseed) result(xi1)
!!
implicit none
integer, intent(in) :: iseed
integer, dimension(:), allocatable :: seed
integer :: i, j, n
real :: xi1
!!-generate a seed first
call random_seed(size = n)
allocate(seed(n))
seed = iseed * (/(i, i=1,n,1)/)
call random_seed(PUT = seed)
deallocate(seed)
call random_number(xi1)
!!
end function generate_random
end module rand
program test
use rand
implicit none
integer :: i, imax
imax=100
do i=1,imax
print *, generate_random(i)
enddo
end program test
However the result of this shown in the plot where $\xi_i$ is plotted vs. the index $i$ clearly has some pattern, so it is not so random after all. How to improve this, i.e., to make it "more random"?
I see you are seeding the generator before every call to random_number. This is a clear abuse and you are not supposed to do that!
You should seed the generator just once using some repeatable, but not too simple, number. Even when the clock is used as a seed it is used only once.
Often there is an additional step to increase the entropy of your time or your selected repeatable seed value. Notice how lcg() is used to increase entropy of the clock value in https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gfortran/RANDOM_005fSEED.html#RANDOM_005fSEED
Related
I need to generate a random array of indices, i.e., unique integers beginning from 1.
So far I have this sequential code in Fortran:
subroutine rperm3(N, p)
integer, intent(in) :: N
integer, dimension(:), intent(out) :: p
integer :: j, k, l
real :: u
call random_seed()
p = 0
do j=1,N
call random_number(u)
k = floor(j*u) + 1
p(j) = p(k)
p(k) = j
call sleep(2)
end do
end subroutine rperm3
In every iteration a random index gets generated, value from this index is assigned to the position of the current index and the old value is rewritten with the current index itself.
But is there any way to parallelize this with OpenMP? I see, that simply using !$omp parallel for is not possible, as we are using the previous values of the array, which don't have to be assigned at the time they're needed.
And if there is no way to do it, is there any other parallel algorithm doing the same thing, i.e. Generating a random array of unique integers from some range?
I am writing a program which computes the LU decomposition of a matrix, with partial pivoting, and I would like the function to output several (2 or 3) matrices without running the program several times to output each one individually, which is a waste of time since it gets me everything I want in one run. Is there a way of doing this? For example, here is my function using Doolittle's algorithm, for square matrix which don't need pivoting. I want my output to be matrix l and u at once, but I know no means of doing that.
function lu_d(aa) result(l)
real, dimension (:,:) :: aa !input matrix
real, dimension (size(aa,1), size(aa,2)) :: a !keeping input variable intact
real, dimension (size(a,1), size(a,2)) :: l , u !lower and upper matrices
integer :: i,j,k !index
real :: s !auxiliar variable
a=aa
do j=1 , size(a,2)
u(1,j)=a(1,j)
end do
l(1,1)=1
do j=2, size(a,2)
l(1,j)=0
end do
do i=2, size(a,1)
l(i,1)=a(i,1)/u(1,1)
u(i,1)=0
do j=2, i-1
s=0
u(i,j)=0
do k=1, j-1
s=s+l(i,k)*u(k,j)
end do
l(i,j)=(a(i,j)-s)/u(j,j)
end do
l(i,i)=1
do j=i, size(a,2)
s=0
l(i,j)=0
do k=1, i-1
s=s+l(i,k)*u(k,j)
end do
u(i,j)=a(i,j)-s
end do
end do
end function
You could switch from using a function to using a subroutine. This way you can output values for multiple arrays in the arguments list. Additionally using the
INTENT definition when declaring variables in the subroutine, e.g.:
REAL,INTENT(IN)::a declares a and does not allow its values to be altered inside the subroutine/function
REAL,INTENT(OUT)::b declares b and disregards any values it has coming into the subroutine/function
REAL,INTENT(INOUT)::c this is the case by default, if you don't write anything.
I will assume you need the output to be l and u (rather than m), in which case the structure would look something like the one below. Note that l and m should either be declared in the main program and their size defined with respect to aa (as in the first case shown below) OR declared with an allocatable size in the main program, passed to the subroutine without being allocated and allocated within the subroutine (second example). The latter may require you to put the subroutine in a module so that the interfaces are handled properly.
First example:
SUBROUTINE lu_d(aa,l,m)
implicit none
real,intent(in):: a(:,:)
real,intent(out):: l(:,:), m(:,:)
integer:: i,j,k
real:: s
<operations>
RETURN
END SUBROUTINE lud_d
Second example:
SUBROUTINE lu_d(aa,l,m)
implicit none
real,intent(in):: a(:,:)
real,allocatable,intent(out):: l(:,:), m(:,:)
integer:: i,j,k,size_a1,size_a2
real:: s
size_a1=size(aa,1)
size_a2=size(aa,2)
allocate( l(size_a1,size_a2), m(size_a1,size_a2))
<operations>
RETURN
END SUBROUTINE lud_d
In Elm 0.17, I'd like to run a program that relies on random numbers, but I'd like to have a user-specified seed. This is to have reproducible results across multiple user sessions: users who enter the same seed should see the same results.
But I can't figure out how to affect the behavior of built-in functions like:
Random.list 10 (Random.int 0 100)
With a call like the one above, I want to get the same list of 10 random numbers each time I feed in the same seed. But I can't figure out how to feed in a seed at all. I'd appreciate any help!
Overview
Generating a random value with Random, using user-specified seed is possible with Random.step
You need to specify a Generator and a Seed, where Generator a is a function to produce random values of a type, using integer Seed
To create a Seed from integer, you need to use Random.initialSeed function, since Seed is not a plain integer, it's a data-structure containing meta-information for the the next steps of the Generator
Random.step
Generator a -> Seed -> (a, Seed)
Calling Random.step will return a new state (a, Seed), where a is your random value, and Seed is the seed, required for generating the next random value.
Example
I have made a comprehensive example, which shows how to use generator for producing random values: Random value with user-specified seed
The example might be too big for the answer so I will highlight the most important parts:
Create a generator
generator : Int -> Generator (List Int)
generator length =
Random.list length (Random.int 0 100)
Step through generator
The seed might be specified through user input, or you can pass it as flags upon start-up.
Random.step (generator 10) seed
The expression that you specified returns a Generator. The function that generates random values is step which takes as arguments a Generator and a Seed. A Seed can be created with the function initialSeed which takes an Int that can be specified by the user as argument.
The same argument to initialSeed will result in the same Seed as output which will result in the same List of random Int values each time. The function below illustrates this.
randomSequence : Int -> List Int
randomSequence int =
let gen = Random.list 10 (Random.int 0 100)
s = initialSeed int
(res, ns) = step gen s
in res
I am trying to compile the following simple code using Gfortran 4.7 from mac-ports (OS-X):
program main
implicit none
integer :: n = 1, clock, i
integer, dimension(1) :: iseed
! initialize the random number generator
call random_seed(size = n)
call system_clock(COUNT=clock)
iseed = clock + 37 * (/ (i - 1, i = 1, n) /)
! iseed = clock
! iseed = abs( mod((clock*181)*((1-83)*359), 104729) )
call random_seed(PUT = iseed)
end program main
and have this error:
gfortran-mp-4.7 tmp.f90
tmp.f90:17.23:
call random_seed(PUT = iseed)
1
Error: Size of 'put' argument of 'random_seed' intrinsic at (1) too small (1/12)
I don't use Fortran at all (I am a C++ guy), so would really appreciate if someone could help and make it working.
p.s. On a similar issue i found couple of forum posts, the current uncomment solution is similar to the one mentioned in this GCC bug report.
The one with abs is mentioned in this stack overflow post (added it without PID since i don't run in parallel anyway.
UPDATE:
the following works:
program main
implicit none
integer :: n = 12, clock, i
integer, dimension(:), allocatable :: iseed
! initialize the random number generator
allocate(iseed(n))
call random_seed(size = n)
call system_clock(COUNT=clock)
iseed = clock + 37 * [(i, i = 0,n-1)]
call random_seed(PUT = iseed)
end program main
To amplify somewhat on #Yossarian's comment, this
call random_seed(size = n)
returns, in n, the size of the rank 1 integer array that you have to use if you want to initialise the RNG. I'd suggest making iseed allocatable by changing its declaration to:
integer, dimension(:), allocatable :: iseed
then, after getting a value for n, allocate it:
allocate(iseed(n))
populate it with your favourite values, then put it.
You might be able to allocate and populate it in one statement like this:
allocate(iseed(n), source = clock + 37 * [(i, i = 0,n-1)])
I write might because this depends on how up to date your compiler is.
EDIT, after OP comment
No, you have not quite understood what I suggested.
Get a value for n by executing
call random_seed(size = n)
don't initialise n to 12.
Then allocate the array and populate it, either in one statement (using sourced allocation) or an allocate statement followed by an assignment.
In
allocate(iseed(n))
call random_seed(size = n)
the sequence of operations is incorrect. This sets iseed to have 12 elements (which is the value of n when the first statement is executed), and then sets n to the size of the array required by the RNG. So long as that is 12 you won't see any problems, but as soon as you port your code to another compiler, possibly even another version of the same compiler, you risk running into an RNG which requires an integer array of a different size. There is no need to hardwire a value into your code, so don't.
This a randon number generator module that I use to compile along with my main program (not listed here)
When I try to compile my random number generator module to see if it works, I get the following message:
at line 61: call random_seed( put = seed)
Error: size of 'put' argument of 'random_seed' intrinsic too small <4/12>
What does it mean? How can I fix it?
module random_angle
contains
0
integer Function random_integer (N) ! return a random integer between 1 and N
integer, intent(in) :: N
real*8 :: x
call random_number(x)
random_integer = floor(real(N)*x)+1
end function random_integer
Real*8 Function gasdev() ! ch7.pg.280:gaussian distribution function using ran1 as random # generator
implicit none
! integer, intent(inout) :: idum
integer, save::iset
real*8:: fac,rsq,v1,v2
real*8, dimension(2) :: x
real*8, save :: gset
! if (idum.lt.0) iset=0
if (iset.eq.0) then
rsq = 0.0
do while (rsq > 1.0.or.rsq==0)
call random_number(x)
v1=2.*x(1)-1
v2=2.*x(2)-1
rsq=v1**2+v2**2
! print *, v1, v2,rsq
end do
fac=sqrt(-2.*log(rsq)/rsq)
gset=v1*fac
gasdev=v2*fac
iset=1
else
gasdev=gset
iset=0
endif
return
end Function gasdev
real*8 function NormalRandom (average, stddev)
implicit none
real*8, intent(in):: average, stddev
NormalRandom = average + stddev*gasdev()
end function NormalRandom
subroutine setSEED (seed)
implicit none
real*8:: x
integer, dimension(4), intent(inout):: seed
if (seed(1) == 0.0) &
seed = floor(1000*secnds(0.0)) +(/0, 37, 74, 111 /)
call random_seed( put=seed)
end subroutine setSEED
end module random_angle
When the compiler says
Error: size of 'put' argument of 'random_seed' intrinsic too small <4/12>
it means that the size of your variable seed is too small.
In this case you have seed of size 4 (and I guess the compiler must be expecting (at least) 12).
The size of the array must be of a certain size which depends on the compiler. You can determine, portably, the required size by a call to random_seed with another argument
integer seed_size
integer, allocatable :: seed(:)
call random_seed(size=seed_size)
allocate(seed(seed_size))
seed = ...
call random_seed(put=seed)
As Vladimir F points out in a comment, the gfortran document itself has an example of this approach.
If you don't care about portable, you could just use an array of size 12 with your choice of values.
As more advanced reading, I'll say another thing. The example I gave above really wasn't very comparable to your code. That is, you say that the input to the seed setting subroutine is guaranteed to be at least of size 4 and it may, or may not, contain values you want to use as seed.
As I noted above, you could change that to 12, but that isn't portable. If you want to be portable things get more awkward.
integer, dimension(4), intent(inout):: seed
has as dummy argument an explicit shape array of size 4. The actual argument in the main program is an array at least that size. However, this size is a specification expression and, alas, call random_seed(size=seed_size) doesn't give us something we can use in a specification expression.
Perhaps something like
subroutine setSEED (seed)
integer, allocatable, intent(inout) :: seed(:)
integer seed_size
! Determine the correct size for the seed
call random_seed(size=seed_size)
! If our seed isn't set, or is too small, kill it.
if (ALLOCATED(seed)) then
if (SIZE(seed)<seed_size.or.seed(LBOUND(seed,1))==0.) deallocate(seed)
end if
! If seed isn't allocated (perhaps we killed it because it was too small)
! then allocate it to the correct size and initialize it.
if (.not.ALLOCATED(seed)) then
allocate(seed(seed_size))
seed = ... ! Our fallback seed initialization
end if
! Finally, put the seed. Using one we set, or the one originally given.
call random_seed(put=seed)
end subroutine
This, of course, requires that the actual argument be allocatable, but if you're handling portable seed setting that's a good thing.