module RandomClass !! It gives Pseudorandom numbers and random operations/functions. use, intrinsic :: iso_fortran_env use MathClass implicit none !> A class for random number type::Random_ integer(int32) :: random_int !! it will be deplicated. integer(int32), allocatable :: random_int_seed(:) !! It is a seed number integer(int32), allocatable :: random_int_vec(:) !! it will be deplicated. real(real64) :: random_real !! it will be deplicated. real(real64), allocatable :: random_real_vec(:) !! it will be deplicated. contains !> It initializes the random number by a seed. procedure :: init => initRandom !> Returns random number in [0,1) procedure :: random => getRandom !> Returns integer random number procedure :: randint => getRandomInt !> Returns random name procedure :: name => nameRandom !> Random choice procedure :: choiceInt => choiceRandomInt !> Random choice procedure :: choiceReal => choiceRandomReal !> Random choice, and the number of the set will be (-1) procedure :: drawOne => drawOneRandomInt !> Random choice, and the number of the set will be (-n) procedure :: draw => drawRandomInt !> Random rearrange of a integer array. procedure :: shuffle => shuffleRandomInt !> Generate uniform distribution procedure :: uniform => uniformRandom !> Generate Gaussian distribution procedure, pass :: gauss_scalar_Random !> Generate Gaussian distribution procedure, pass :: gauss_vector_Random !> Generate Gaussian distribution procedure, pass :: gauss_tensor_Random !> Generate Gaussian distribution generic :: gauss => gauss_scalar_Random, gauss_vector_Random,gauss_tensor_Random !> Generate Chi-Squared distribution procedure :: ChiSquared => ChiSquaredRandom !> Generate Cauchy distribution procedure :: Cauchy => CauchyRandom !> Generate Lognormal distribution procedure :: Lognormal => LognormalRandom !> Generate InverseGaussian distribution procedure :: InverseGauss => InverseGaussRandom !> save random number procedure :: save => saveRandom !> Generate While-Gaussian signal procedure :: white => whiteRandom !> Generate a vector of uniform random number. procedure, pass :: randnRandomVector !> Generate a array of uniform random number. procedure, pass :: randnRandomArray !> Generate a vector/array of uniform random number. generic :: randn => randnRandomArray, randnRandomvector !> Generate a vector of uniform random number. procedure :: fill => fillRandom !> Generate a histogram of a vector procedure :: histogram => histogramRandom !> Generate a random scalar of uniform distribution [0,1) procedure :: scalar => scalarRandom !> Generate a random vector of uniform distribution [0,1) procedure :: vector => vectorRandom !> Generate a random matrix of uniform distribution [0,1) procedure :: matrix => matrixRandom !> Generate a random cube (3rd order tensor) of uniform distribution [0,1) procedure :: cube => cubeRandom end type !> Generate a uniformly distributed random integers interface randi module procedure :: randi_range_rank2, randi_imax, randi_imax_n end interface !> Generate a uniformly distributed random integers interface rand module procedure :: rand_n, rand_sz2, rand_sz3 end interface !> Compute expectation value interface EV module procedure :: ExpectationValue_r64_i32_i32 end interface contains !########################################## subroutine initRandom(obj) class(Random_), intent(inout)::obj !integer(int32),optional,intent(in)::SeedSize integer(int32)::SeedSize call random_seed(size=SeedSize) if (.not. allocated(obj%random_int_seed)) then allocate (obj%random_int_seed(SeedSize)) end if !call random_seed(get=obj%random_real_vec) call random_seed(get=obj%random_int_seed) end subroutine !########################################## !########################################## function getRandom(obj, distribution) result(x) class(Random_)::obj character(*), optional, intent(in)::distribution real(real64) :: x, val, y integer(int32) :: i if (trim(distribution) == "Binomial" .or. trim(distribution) == "binomial") then val = 0.0d0 do i = 1, 20 call random_number(y) val = val + y end do x = val - 10.0d0 return end if call random_number(x) end function !########################################## !########################################## subroutine saveRandom(obj) class(Random_), intent(inout)::obj call random_seed(put=obj%random_int_seed) end subroutine !########################################## !########################################## function uniformRandom(obj, From, To) result(x) class(Random_), intent(in)::obj real(real64) :: x, a, diff, val(2) real(real64), intent(in) :: From, To val(1) = From val(2) = To diff = abs(from - to) call random_number(a) x = a*diff + minval(val) end function !########################################## !########################################## function getRandomInt(obj, From, To) result(x) class(Random_), intent(in)::obj real(real64) :: xr, a, diff, val(2) integer(int32) :: x integer(int32), intent(in) :: From, To val(1) = From val(2) = To diff = abs(dble(from) - dble(to)) call random_number(a) xr = a*diff + minval(val) x = nint(xr) if (x == From - 1) then x = From end if if (x == To + 1) then x = To end if end function !########################################## !########################################## function choiceRandomInt(obj, Vector, Array) result(val) class(Random_), intent(in)::obj integer(int32), optional, intent(in) :: Vector(:) integer(int32), Optional, intent(in) :: Array(:, :) integer(int32) :: val, posi, posi2 ! it should be over-rided if (present(Vector)) then posi = obj%randint(1, size(Vector)) val = Vector(posi) return end if if (present(Array)) then print *, size(Array, 1) posi = obj%randint(1, size(Array, 1)) posi2 = obj%randint(1, size(Array, 2)) val = Array(posi, posi2) return end if print *, "No list is imported." end function !########################################## !########################################## function choiceRandomReal(obj, Vector, Array) result(val) class(Random_), intent(in)::obj real(real64), Optional, intent(in) :: Vector(:) real(real64), Optional, intent(in) :: Array(:, :) real(real64) :: val integer(int32) :: posi, posi2 ! it should be over-rided if (present(Vector)) then posi = obj%randint(1, size(Vector)) val = Vector(posi) return end if if (present(Array)) then print *, size(Array, 1) posi = obj%randint(1, size(Array, 1)) posi2 = obj%randint(1, size(Array, 2)) val = Array(posi, posi2) return end if print *, "No list is imported." end function !########################################## !########################################## function randnRandomArray(obj, d0, d1) result(array) class(Random_), intent(inout)::obj real(real64), allocatable :: array(:, :) integer(int32), intent(in) :: d0, d1 integer(int32) :: n, m, i, j n = d0 m = d1 allocate (array(n, m)) call obj%init() do i = 1, n do j = 1, m array(i, j) = obj%random() end do end do end function !########################################## !########################################## function randnRandomVector(obj, d0) result(array) class(Random_), intent(inout)::obj real(real64), allocatable :: array(:) integer(int32), intent(in) :: d0 integer(int32) :: n, m, i, j n = d0 allocate (array(n)) call obj%init() do i = 1, n array(i) = obj%random() end do end function !########################################## !########################################## !function choiceRandomString(obj,Str) result(val) ! class(Random_),intent(in) :: obj ! character(*), intent(in) :: Str ! character(1) :: val ! integer(int32) :: posi,length ! ! length=len(Str) ! ! ! it should be over-rided ! posi=obj%randint(1,length ) ! val=Str(posi) ! ! !end function !########################################## !########################################## function histogramRandom(obj, list, division) result(histogram) class(Random_), intent(inout) :: obj real(real64), intent(in) :: list(:) ! data-list real(real64), allocatable :: histogram(:, :) integer(int32), optional, intent(in) :: division integer(int32) :: i, j, n real(real64) :: maxv, minv, val, intval n = input(default=10, option=division) maxv = maxval(list) minv = minval(list) intval = (maxv - minv)/dble(n) allocate (histogram(n, 2)) histogram(:, :) = 0 val = minv - 0.00000010d0 do i = 1, size(histogram, 1) histogram(i, 1) = val val = val + intval end do do i = 1, size(list, 1) val = minv - 0.00000010d0 do j = 1, size(histogram, 1) if (val < list(i) .and. list(i) <= val + intval) then histogram(j, 2) = histogram(j, 2) + 1.0d0 exit end if val = val + intval end do end do end function !########################################## !########################################## function nameRandom(obj) result(str) class(Random_), intent(inout) :: obj character(200) :: str integer(int32) :: n call obj%init() n = int(obj%random()*1000000) str = "RandName"//fstring_int(n) !########################################## end function ! Reference: omitakahiro.github.io !########################################## function gauss_scalar_Random(obj, mu, sigma) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: mu, sigma real(real64) :: ret real(real64) :: pi = 3.141592653d0 ret = sqrt(-2.0d0*log(obj%random()))*sin(2.0d0*pi*obj%random()) ret = mu + sigma*ret end function !########################################## !########################################## function gauss_vector_Random(obj, mu, sigma, n) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: mu, sigma real(real64) :: ret(n) real(real64) :: pi = 3.141592653d0 integer(int32), intent(in) :: n integer(int32) :: i do i = 1, n ret(i) = obj%gauss(mu=mu, sigma=sigma) end do end function !########################################## !########################################## function gauss_tensor_Random(obj, mu, sigma, n, m) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: mu, sigma real(real64) :: ret(n,m) real(real64) :: pi = 3.141592653d0 integer(int32), intent(in) :: n, m integer(int32) :: i,j do j=1,m do i = 1, n ret(i,j) = obj%gauss(mu=mu, sigma=sigma) end do end do end function !########################################## !########################################## function ChiSquaredRandom(obj, k) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: k real(real64) :: ret, z, w real(real64) :: pi = 3.141592653d0 integer(int32) :: i w = 0.0d0 z = 0.0d0 ret = 0.0d0 do i = 1, int(k) z = sqrt(-2.0d0*log(obj%random()))*sin(2.0d0*pi*obj%random()) w = w + z*z end do ret = w end function !########################################## !########################################## function CauchyRandom(obj, mu, gamma) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: mu, gamma real(real64) :: ret, z, w real(real64) :: pi = 3.141592653d0 ret = mu + gamma*tan(pi*(obj%random() - 0.50d0)) end function !########################################## !########################################## function LognormalRandom(obj, mu, sigma) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: mu, sigma real(real64) :: ret, z, w real(real64) :: pi = 3.141592653d0 ret = obj%gauss(mu=mu, sigma=sigma) ret = exp(ret) end function !########################################## !########################################## function InverseGaussRandom(obj, mu, lambda) result(ret) class(Random_), intent(inout) :: obj real(real64), intent(in) :: mu, lambda real(real64) :: ret, x, y, z, w real(real64) :: pi = 3.141592653d0 x = obj%gauss(mu=0.0d0, sigma=1.0d0) y = x*x w = mu + 0.50d0*y*mu*mu/lambda - (0.50d0*mu/lambda)*sqrt(4.0d0*mu*lambda*y + mu*mu*y*y) z = obj%random() if (z < mu/(mu + w)) then ret = w else ret = mu*mu/w end if end function !########################################## !########################################## subroutine fillRandom(obj, array, vector) class(Random_), intent(inout) :: obj real(real64), optional, intent(inout) :: array(:, :), vector(:) integer(int32) :: i, j call obj%init() if (present(array)) then do i = 1, size(Array, 2) do j = 1, sizE(Array, 1) array(j, i) = obj%random() end do end do end if if (present(vector)) then do i = 1, size(vector, 1) vector(i) = obj%random() end do end if end subroutine !########################################## !########################################## function cubeRandom(obj, size1, size2, size3) result(ret) class(Random_), intent(inout) :: obj integer(int32), intent(in) :: size1, size2, size3 real(real64), allocatable :: ret(:, :, :) integer(int32) :: i, j, k allocate (ret(size1, size2, size3)) do i = 1, size3 do j = 1, size2 do k = 1, size1 ret(k, j, i) = obj%random() end do end do end do end function !########################################## !########################################## function matrixRandom(obj, size1, size2) result(ret) class(Random_), intent(inout) :: obj integer(int32), intent(in) :: size1, size2 real(real64), allocatable :: ret(:, :) integer(int32) :: j, k allocate (ret(size1, size2)) do j = 1, size2 do k = 1, size1 ret(k, j) = obj%random() end do end do end function !########################################## !########################################## function vectorRandom(obj, size1) result(ret) class(Random_), intent(inout) :: obj integer(int32), intent(in) :: size1 real(real64), allocatable :: ret(:) integer(int32) :: k allocate (ret(size1)) do k = 1, size1 ret(k) = obj%random() end do end function !########################################## !########################################## function scalarRandom(obj, size1) result(ret) class(Random_), intent(inout) :: obj integer(int32), intent(in) :: size1 real(real64), allocatable :: ret integer(int32) :: k ret = obj%random() end function !########################################## !########################################## function randi_range_rank2(valrange, size1, size2) result(ret) integer(int32), intent(in) :: valrange(2), size1, size2 type(Random_) :: random integer(int32) :: i, j integer(int32), allocatable :: ret(:, :) allocate (ret(size1, size2)) do i = 1, size2 do j = 1, size1 ret(j, i) = nint(dble(valrange(2) - valrange(1))*random%random()*1.00050d0 + dble(valrange(1))) end do end do end function !########################################## !########################################## function randi_imax(imax) result(ret) integer(int32), intent(in) :: imax type(Random_) :: random integer(int32) :: ret ret = nint(random%random()*dble(imax - 1) + 1.0d0) end function !########################################## !########################################## function randi_imax_n(imax, n) result(ret) integer(int32), intent(in) :: imax, n type(Random_) :: random integer(int32) :: ret(n), i do i = 1, n ret(i) = nint(random%random()*dble(imax - 1) + 1.0d0) end do end function !########################################## !########################################## function rand_n(n) result(ret) integer(int32), intent(in) :: n type(Random_) :: random real(real64) :: ret(n) integer(int32) :: i do i = 1, n ret(i) = random%random() end do end function !########################################## !########################################## function rand_sz2(sz1, sz2) result(ret) integer(int32), intent(in) :: sz1, sz2 type(Random_) :: random real(real64) :: ret(sz1, sz2) integer(int32) :: i, j do j = 1, sz2 do i = 1, sz1 ret(i, j) = random%random() end do end do end function !########################################## !########################################## function rand_sz3(sz1, sz2, sz3) result(ret) integer(int32), intent(in) :: sz1, sz2, sz3 type(Random_) :: random real(real64) :: ret(sz1, sz2, sz3) integer(int32) :: i, j, k do k = 1, sz3 do j = 1, sz2 do i = 1, sz1 ret(i, j, k) = random%random() end do end do end do end function !########################################## function whiteRandom(this, num_sample, mu, sigma) result(ret) class(Random_), intent(inout) :: this real(real64), allocatable :: ret(:) real(real64), optional, intent(in) :: mu, sigma real(real64) :: mu_d, sigma_d integer(int32), intent(in) :: num_sample integer(int32) :: i allocate (ret(num_sample)) mu_d = input(default=0.0d0, option=mu) sigma_d = input(default=1.0d0, option=sigma) do i = 1, num_sample ret(i) = this%gauss(mu=mu_d, sigma=sigma_d) end do end function ! ############################################ function drawOneRandomInt(this, vec) result(ret) class(Random_), intent(in) :: this integer(int32), allocatable, intent(inout) :: vec(:) integer(int32), allocatable :: buf(:) integer(int32) :: ret, id ret = 0 if (.not. allocated(vec)) then return end if if (size(vec) == 1) then ret = vec(1) deallocate (vec) return end if id = this%randint(1, size(vec)) ret = vec(id) if (id == 1) then vec = vec(2:size(vec)) elseif (id == size(vec)) then vec = vec(1:size(vec) - 1) else buf = vec deallocate (vec) allocate (vec(size(buf) - 1)) vec(1:id - 1) = buf(1:id - 1) vec(id:) = buf(id + 1:) return end if end function ! ####################################################### recursive function drawRandomInt(this, vec, num) result(ret) class(Random_), intent(in) :: this integer(int32), intent(in) :: num integer(int32), allocatable, intent(inout) :: vec(:) integer(int32), allocatable :: buf(:) logical :: selected_entity(num) integer(int32) :: i, n integer(int32), allocatable :: ret(:) if (.not. allocated(vec)) then allocate (ret(0)) return end if if (size(vec) == 0) then allocate (ret(0)) return end if n = size(vec) allocate (ret(minval([num, n]))) ret = 0 do i = 1, minval([num, n]) ret(i) = this%drawOne(vec) end do end function ! ################################################# subroutine shuffleRandomInt(this, intvec) class(Random_), intent(in) :: this integer(int32), intent(inout) :: intvec(:) logical, allocatable :: selected_entity(:) integer(int32), allocatable :: retvec(:) integer(int32) :: i, n, id, itr n = size(intvec) allocate (selected_entity(n)) allocate (retvec(n)) selected_entity(:) = .false. itr = 0 do id = this%randint(1, n) if (selected_entity(id)) then cycle else itr = itr + 1 retvec(itr) = intvec(id) selected_entity(id) = .true. if (itr == n) then intvec = retvec return end if end if end do end subroutine ! ################################################# function ExpectationValue_r64_i32_i32(func,arg1,arg2,arg3) result(ret) interface function func(arg1,arg2,arg3) result(ret) use iso_fortran_env real(real64),intent(in) :: arg1 integer(int32),intent(in) :: arg2,arg3 real(real64) :: ret end function end interface real(real64),intent(in) :: arg1 integer(int32),intent(in) :: arg2,arg3 integer(int32) :: i,n real(real64) :: ret n = 1000 ret = 0.0d0 do i=1,n ret = ret + func(arg1,arg2,arg3) enddo ret = ret/dble(n) end function end module