-
Notifications
You must be signed in to change notification settings - Fork 0
/
rng.f90
336 lines (245 loc) · 6.5 KB
/
rng.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
!Module that contains routines for random number generation. Some of this has been taken (where indicated) from RosettaCode.org and from other sites on the net.
module rng
implicit none
interface init_random_seed
module procedure init_random_seed_parallel
module procedure init_random_seed_serial
end interface
interface shuffle
module procedure shuffle_dp_dimn
module procedure shuffle_int_dimn
module procedure shuffle_dp_1
module procedure shuffle_int_1
end interface
!Default to public.
contains
!Generate an array of n numbers sampled from a Gaussian normal distribution with mean and standard deviation given.
!Adapted from RosettaCode.org.
function normal(n,mean,std)
use types, only : dp
implicit none
integer, intent(in) :: n
integer :: i
real(dp) :: normal(n), temp
real(dp), intent(in) :: mean, std
real(dp), parameter :: pi = 4.0*ATAN(1.0)
!Get uniform distribution.
call random_number(normal)
!Now convert to normal distribution
do i = 1, n-1, 2
temp = std * SQRT(-2.0*LOG(normal(i))) * COS(2*pi*normal(i+1)) + mean
normal(i+1) = std * SQRT(-2.0*LOG(normal(i))) * SIN(2*pi*normal(i+1)) + mean
normal(i) = temp
end do
end function normal
!Generate a random seed based on the clock time.
!Adapted from GNU site.
subroutine init_random_seed_serial()
implicit none
integer :: i, n, clock
integer, dimension(:), allocatable :: seed
call random_seed(size = n)
allocate(seed(n))
call system_clock(count=clock)
seed = clock + 37* (/ (i - 1, i = 1, n) /)
call random_seed(PUT = seed)
deallocate(seed)
end subroutine init_random_seed_serial
!Generate a random seed based on the clock time --- For use in parallel.
subroutine init_random_seed_parallel(rank)
implicit none
integer :: i, n, clock
integer, dimension(:), allocatable :: seed
integer, intent(in) :: rank
call random_seed(size = n)
allocate(seed(n))
call system_clock(count=clock)
seed = clock + 37*rank* (/ (i - 1, i = 1, n) /)
call random_seed(PUT = seed)
deallocate(seed)
end subroutine init_random_seed_parallel
!Subroutine to shuffle an array by Knuth Shuffle.
!Inspired by RosettaCode.org.
subroutine shuffle_int_1(a)
implicit none
integer, intent(inout) :: a(:)
integer :: i, randpos, temp
real :: r
!Count backwards
do i = size(a), 2, -1
!Get a random number.
call random_number(r)
randpos = int(r * i) + 1
!Exchange the rows.
temp = a(randpos)
a(randpos) = a(i)
a(i) = temp
end do
end subroutine shuffle_int_1
subroutine shuffle_dp_dimn(a)
use types, only : dp
implicit none
real(dp), dimension(:,:), intent(inout) :: a
real(dp), dimension(size(a,2)) :: temp
integer :: i, hold
real(dp) :: rand
!Count backwards.
do i=size(a,1), 1, -1
!Generate a random int from 1-i.
call random_number(rand)
hold=int(rand*i)+1
!Swap the ith row with the rand row.
temp(:)=a(hold,:)
a(hold,:)=a(i,:)
a(i,:) = temp(:)
end do
end subroutine shuffle_dp_dimn
subroutine shuffle_dp_1(a)
use types, only : dp
implicit none
real(dp), dimension(:), intent(inout) :: a
real(dp) :: temp
integer :: i, hold
real(dp) :: rand
!Count backwards.
do i=size(a,1), 1, -1
!Generate a random int from 1-i.
call random_number(rand)
hold=int(rand*i)+1
!Swap the ith row with the rand row.
temp=a(hold)
a(hold)=a(i)
a(i) = temp
end do
end subroutine shuffle_dp_1
subroutine shuffle_int_dimn(a)
use types, only : dp
implicit none
integer, dimension(:,:), intent(inout) :: a
integer, dimension(size(a,2)) :: temp
integer :: i, hold
real(dp) :: rand
!Count backwards.
do i=size(a,1), 1, -1
!Generate a random int from 1-i.
call random_number(rand)
hold=int(rand*i)+1
!Swap the ith row with the rand row.
temp(:)=a(hold,:)
a(hold,:)=a(i,:)
a(i,:) = temp(:)
end do
end subroutine shuffle_int_dimn
!Subroutine that takes two sets, shuffles them together n times, then divides them along the columns, returning same sized arrays that are a mix of both sets.
subroutine shuffle_cut(setA, setB, n)
use types, only : dp
implicit none
real(dp), dimension(:,:), intent(inout) :: setA, setB
integer, optional, intent(in) :: n
real(dp), dimension((size(setA,1)+size(setB,1)),size(setA,2)) :: work
real(dp) :: rand
integer :: i, iend
!Load the work function.
do i=1,size(work,1)
if (i .le. size(setA,1)) then
work(i,:)=setA(i,:)
else
work(i,:)=setB(i-size(setA,1),:)
end if
end do
!Shuffle the set.
if (present(n)) then
iend=n
else
iend=2
end if
do i=1,iend
call shuffle(work)
end do
!Unload work.
do i=1,size(work,1)
if (i .le. size(setA,1)) then
setA(i,:)=work(i,:)
else
setB(i-size(setA,1),:)=work(i,:)
end if
end do
end subroutine shuffle_cut
!Function to make a cluster of "n" points centered at "center" with given std "sigma."
function make_blob(n, center, sigma)
use types, only : dp
implicit none
real(dp), dimension(:), intent(in) :: center
real(dp), optional, intent(in) :: sigma
real(dp):: std
integer, intent(in) :: n
real(dp), dimension(:,:), allocatable :: make_blob
integer :: i,j
!Optional argument for standard deviation.
if (present(sigma)) then
std = sigma
else
std = 1_dp
end if
call init_random_seed()
allocate(make_blob(n,size(center)))
do j=1,size(center)
make_blob(:,j) = normal(n,center(j),std)
end do
end function make_blob
!Subroutine that will return a random sample from a target distribution. Uses
!the Metropolis algorithm with a random Gaussian walk proposal function.
!subroutine sample(func,table,n, std)
! use types, only : dp
! implicit none
!
! interface
! real(dp) function func(x)
! use types, only : dp
! implicit none
! real(dp), intent(in) :: x
! end function
! end interface
! integer, intent(in) :: n
! real(dp), dimension(n), intent(out) :: table
! real(dp), optional, intent(in) :: std
! real(dp) :: sigma, y1, y2, a, rand, mean
! integer :: i
!
! if (.not. present(std)) then
! sigma=1.0_dp
! else
! sigma=std
! end if
!
! !Get a random point.
! mean = 0_dp
! y1 = normal(1,mean, sigma)
!
! do i=1,n
! !Propose a new point.
! y2 = normal(1,y1,sigma)
!
! !Accept with probability a.
! if ( (func(y2)/func(y1)) < 1_dp) then
! a = (func(y2)/func(y1))
! else
! a = 1_dp
! end if
!
! call random_number(rand)
! if (rand<a) then
! !Accept
! table(i)=y2
! !Set old pt to new pt.
! y1=y2
! else
! !Decline
! table(i)=y1
! end if
! !Set old pt to new pt.
! y1=y2
! end do
!
!end subroutine sample
end module rng