Branch data Line data Source code
1 : :
2 : : #if defined HAVE_CONFIG_H
3 : : #include "config.h"
4 : : #endif
5 : :
6 : : #include "abi_common.h"
7 : :
8 : : #if defined HAVE_FC_SHIFTLR
9 : : #define SHIFTL_ shiftl
10 : : #define SHIFTR_ shiftr
11 : : #else
12 : : #define SHIFTL_ lshift
13 : : #define SHIFTR_ rshift
14 : : #endif
15 : :
16 : : ! xoroshiro128plus method random number generator
17 : : ! adapted by hexu for usage in Abinit (downgrade to Fortran 90 and added
18 : : ! some functions )
19 : : ! (Fortran 2003 re-enabled)
20 : :
21 : : !** License for the xoroshiro128plus random number generator **
22 : : !
23 : : !Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
24 : : !Translated to Fortran 2008 by Jannis Teunissen
25 : : !
26 : : !To the extent possible under law, the author has dedicated all copyright
27 : : !and related and neighboring rights to this software to the public domain
28 : : !worldwide. This software is distributed without any warranty.
29 : : !
30 : : !See <http://creativecommons.org/publicdomain/zero/1.0/>.
31 : : !
32 : : ! << This is the original documentation of Blackman and Vigna
33 : : !This is the successor to xorshift128+. It is the fastest full-period
34 : : !generator passing BigCrush without systematic failures, but due to the
35 : : !relatively short period it is acceptable only for applications with a
36 : : !mild amount of parallelism; otherwise, use a xorshift1024* generator.
37 : : !
38 : : !Beside passing BigCrush, this generator passes the PractRand test suite
39 : : !up to (and included) 16TB, with the exception of binary rank tests,
40 : : !which fail due to the lowest bit being an LFSR; all other bits pass all
41 : : !tests. We suggest to use a sign test to extract a random Boolean value.
42 : : !
43 : : !Note that the generator uses a simulated rotate operation, which most C
44 : : !compilers will turn into a single instruction. In Java, you can use
45 : : !Long.rotateLeft(). In languages that do not make low-level rotation
46 : : !instructions accessible xorshift128+ could be faster.
47 : : !
48 : : !The state must be seeded so that it is not everywhere zero. If you have
49 : : !a 64-bit seed, we suggest to seed a splitmix64 generator and use its
50 : : !output to fill s.
51 : : ! =================
52 : : !
53 : :
54 : : !> Module for pseudo random number generation. The internal pseudo random
55 : : !> generator is the xoroshiro128plus method.
56 : :
57 : : module m_random_xoroshiro128plus
58 : :
59 : : use defs_basis
60 : : use m_profiling_abi
61 : :
62 : : implicit none
63 : : private
64 : :
65 : : ! A 64 bit floating point type
66 : : !integer, parameter :: dp = kind(0.0d0)
67 : :
68 : : ! A 32 bit integer type
69 : : integer, parameter :: i4 = selected_int_kind(9)
70 : :
71 : : ! A 64 bit integer type
72 : : integer, parameter :: i8 = selected_int_kind(18)
73 : :
74 : : !> Random number generator type, which contains the state
75 : : type rng_t
76 : : !> The rng state (always use your own seed)
77 : : integer(i8), private :: s(2) = [123456789_i8, 987654321_i8]
78 : : integer(i8), private :: separator(32) ! Separate cache lines (parallel use)
79 : :
80 : : real(dp) :: residual =0.0d0 ! for saving residual in normal function.
81 : : logical :: has_residual = .False.
82 : : contains
83 : : procedure, non_overridable :: set_seed ! Seed the generator
84 : : procedure, non_overridable :: jump ! Jump function (see below)
85 : : procedure, non_overridable :: rand_int4 ! 4-byte random integer
86 : : procedure, non_overridable :: rand_int8 ! 8-byte random integer
87 : : procedure, non_overridable :: rand_unif_01 ! Uniform (0,1] real
88 : : procedure, non_overridable :: rand_unif_01_array ! Uniform (0,1] real
89 : : procedure, non_overridable :: rand_two_normals ! Two normal(0,1) samples
90 : : procedure, non_overridable :: rand_normal ! Two normal(0,1) samples
91 : : procedure, non_overridable :: rand_normal_array ! Two normal(0,1) samples
92 : : procedure, non_overridable :: rand_poisson ! Sample from Poisson-dist.
93 : : procedure, non_overridable :: rand_circle ! Sample on a rand_circle
94 : : procedure, non_overridable :: rand_sphere ! Sample on a rand_sphere
95 : : procedure, non_overridable :: rand_choice ! select from 1 to N randomly
96 : : procedure, non_overridable :: next ! Internal method
97 : : end type rng_t
98 : :
99 : : !> Parallel random number generator type
100 : : type prng_t
101 : : type(rng_t), allocatable :: rngs(:)
102 : : contains
103 : : procedure, non_overridable :: initialize => init_parallel
104 : : procedure, non_overridable :: finalize => finalize_parallel
105 : : end type prng_t
106 : :
107 : :
108 : : public :: rng_t
109 : : public :: prng_t
110 : : public :: set_seed
111 : : public :: jump
112 : : public :: rand_int4
113 : : public :: rand_int8
114 : : public :: rand_unif_01
115 : : public :: rand_two_normals
116 : : public :: rand_normal
117 : : public :: rand_normal_array
118 : : public :: rand_poisson
119 : : public :: rand_circle
120 : : public :: rand_sphere
121 : :
122 : :
123 : : contains
124 : :
125 : : !> Initialize a collection of rng's for parallel use
126 : : !> this can be used with openmp, each thread need an
127 : : !> different rng.
128 : 0 : subroutine init_parallel(self, n_proc, seed)
129 : :
130 : : class(prng_t), intent(inout) :: self
131 : : integer(i8), optional, intent(in) :: seed(2)
132 : : integer, intent(in) :: n_proc
133 : : integer :: n
134 : :
135 [ # # # # : 0 : ABI_MALLOC(self%rngs, (n_proc))
# # # # #
# # # # #
# # # # ]
136 [ # # ]: 0 : if (present(seed)) then
137 : 0 : call self%rngs(1)%set_seed(seed)
138 : : endif
139 [ # # ]: 0 : do n = 2, n_proc
140 : 0 : self%rngs(n) = self%rngs(n-1)
141 : 0 : call self%rngs(n)%jump()
142 : : end do
143 : 0 : end subroutine init_parallel
144 : :
145 : :
146 : : !> Finalize prng_t
147 : 0 : subroutine finalize_parallel(self)
148 : : class(prng_t), intent(inout) :: self
149 [ # # ]: 0 : ABI_FREE(self%rngs)
150 : 0 : end subroutine finalize_parallel
151 : :
152 : : !> Set a seed for the rng
153 : 6 : subroutine set_seed(self, the_seed)
154 : :
155 : : class(rng_t), intent(inout) :: self
156 : : integer(i8), intent(in) :: the_seed(2)
157 : :
158 [ + + ]: 18 : self%s = the_seed
159 : :
160 : : ! Simulate calls to next() to improve randomness of first number
161 : : !call self%jump()
162 : 6 : call jump(self)
163 : 6 : end subroutine set_seed
164 : :
165 : : ! This is the jump function for the generator. It is equivalent
166 : : ! to 2^64 calls to next(); it can be used to generate 2^64
167 : : ! non-overlapping subsequences for parallel computations.
168 : 6 : subroutine jump(self)
169 : :
170 : : class(rng_t), intent(inout) :: self
171 : : integer :: i, b
172 : : integer(i8) :: t(2), dummy
173 : :
174 : : ! The signed equivalent of the unsigned constants
175 : : integer(i8), parameter :: jmp_c(2) = &
176 : : (/-4707382666127344949_i8, -2852180941702784734_i8/)
177 : :
178 : 6 : t = 0
179 [ + + ]: 18 : do i = 1, 2
180 [ + + ]: 786 : do b = 0, 63
181 [ + + ]: 768 : if (iand(jmp_c(i), SHIFTL_(1_i8, b)) /= 0) then
182 [ + + ]: 1152 : t = ieor(t, self%s)
183 : : end if
184 : : !dummy = self%next()
185 : 780 : dummy = next(self)
186 : : end do
187 : : end do
188 : :
189 [ + + ]: 18 : self%s = t
190 : 6 : end subroutine jump
191 : :
192 : : !> Return 4-byte integer
193 : 0 : integer(i4) function rand_int4(self)
194 : :
195 : : class(rng_t), intent(inout) :: self
196 : : !rand_int4 = int(self%next(), i4)
197 : 0 : rand_int4 = int(next(self), i4)
198 : 0 : end function rand_int4
199 : :
200 : : !> Return 8-byte integer
201 : 0 : integer(i8) function rand_int8(self)
202 : :
203 : : class(rng_t), intent(inout) :: self
204 : : !rand_int8 = self%next()
205 : 0 : rand_int8 = next(self)
206 : 0 : end function rand_int8
207 : :
208 : : !> Get a uniform [0,1) random real (double precision)
209 : 1697616 : real(dp) function rand_unif_01(self)
210 : :
211 : : class(rng_t), intent(inout) :: self
212 : : integer(i8) :: x
213 : : real(dp) :: tmp
214 : :
215 : : !x = self%next()
216 : 848808 : x = next(self)
217 : 1697616 : x = ior(SHIFTL_(1023_i8, 52), SHIFTR_(x, 12))
218 : 1697616 : rand_unif_01 = transfer(x, tmp) - 1.0_dp
219 : 0 : end function rand_unif_01
220 : :
221 : :
222 : 4 : subroutine rand_unif_01_array(self, output, size_array)
223 : :
224 : : class(rng_t), intent(inout) :: self
225 : : integer, intent(in) :: size_array
226 : : real(dp), intent(inout) :: output(size_array)
227 : : integer(i8) :: x, i
228 : : real(dp) :: tmp
229 : :
230 : : !x = self%next()
231 [ + + ]: 3348 : do i=1, size_array
232 : 3344 : x = next(self)
233 : 3344 : x = ior(SHIFTL_(1023_i8, 52), SHIFTR_(x, 12))
234 : 3348 : output(i) = transfer(x, tmp) - 1.0_dp
235 : : enddo
236 : 4 : end subroutine rand_unif_01_array
237 : :
238 : : !> Return two normal random variates with mean 0 and variance 1.
239 : : !> http://en.wikipedia.org/wiki/Marsaglia_polar_method
240 [ # # ]: 0 : function rand_two_normals(self) result(rands)
241 : :
242 : : class(rng_t), intent(inout) :: self
243 : : real(dp) :: rands(2), sum_sq
244 : : do
245 : : !rands(1) = 2 * self%rand_unif_01() - 1
246 : 0 : rands(1) = 2 * rand_unif_01(self) - 1
247 : : !rands(2) = 2 * self%rand_unif_01() - 1
248 : 0 : rands(2) = 2 * rand_unif_01(self) - 1
249 [ # # ]: 0 : sum_sq = sum(rands**2)
250 [ # # # # ]: 0 : if (sum_sq < 1.0_dp .and. sum_sq > 0.0_dp) exit
251 : : end do
252 [ # # ]: 0 : rands = rands * sqrt(-2 * log(sum_sq) / sum_sq)
253 : 0 : end function rand_two_normals
254 : :
255 : : !> Return one normal random variates with mean 0 and variance 1.
256 : : !> http://en.wikipedia.org/wiki/Marsaglia_polar_method
257 : 1332464 : function rand_normal(self) result(r)
258 : :
259 : : class(rng_t), intent(inout) :: self
260 : : real(dp) :: r, rands(2), sum_sq
261 [ + + ]: 1332464 : if(self%has_residual) then
262 : 666232 : self%has_residual=.False.
263 : 666232 : r=self%residual
264 : : else
265 : : do
266 : : !rands(1) = 2 * self%rand_unif_01() - 1
267 : 848808 : rands(1) = 2 * rand_unif_01(self) - 1
268 : : !rands(2) = 2 * self%rand_unif_01() - 1
269 : 848808 : rands(2) = 2 * rand_unif_01(self) - 1
270 [ + + ]: 2546424 : sum_sq = sum(rands**2)
271 [ + + - + ]: 848808 : if (sum_sq < 1.0_dp .and. sum_sq > 0.0_dp) exit
272 : : end do
273 [ + + ]: 1998696 : rands = rands * sqrt(-2 * log(sum_sq) / sum_sq)
274 : 666232 : r=rands(1)
275 : 666232 : self%has_residual=.True.
276 : 666232 : self%residual=rands(2)
277 : : endif
278 : 1332464 : end function rand_normal
279 : :
280 : 2029 : subroutine rand_normal_array(self, a, asize)
281 : :
282 : : class(rng_t), intent(inout) :: self
283 : : integer ,intent(in) :: asize
284 : : real(dp), intent(inout) :: a(asize)
285 : : integer :: i
286 [ + + ]: 1334493 : do i=1, asize
287 : 1334493 : a(i)=rand_normal(self)
288 : : enddo
289 : 2029 : end subroutine rand_normal_array
290 : :
291 : :
292 : : !> Return Poisson random variate with rate lambda. Works well for lambda < 30
293 : : !> or so. For lambda >> 1 it can produce wrong results due to roundoff error.
294 : 0 : function rand_poisson(self, lambda) result(rr)
295 : :
296 : : class(rng_t), intent(inout) :: self
297 : : real(dp), intent(in) :: lambda
298 : : integer(i4) :: rr
299 : : real(dp) :: expl, p
300 : :
301 : 0 : expl = exp(-lambda)
302 : 0 : rr = 0
303 : 0 : p = self%rand_unif_01()
304 : :
305 [ # # ]: 0 : do while (p > expl)
306 : 0 : rr = rr + 1
307 : : !p = p * self%rand_unif_01()
308 : 0 : p = p * rand_unif_01(self)
309 : : end do
310 : 0 : end function rand_poisson
311 : :
312 : : !> Sample point on a rand_circle with given radius
313 [ # # ]: 0 : function rand_circle(self, radius) result(xy)
314 : :
315 : : class(rng_t), intent(inout) :: self
316 : : real(dp), intent(in) :: radius
317 : : real(dp) :: rands(2), xy(2)
318 : : real(dp) :: sum_sq
319 : :
320 : : ! Method for uniform sampling on rand_circle
321 : : do
322 : : !rands(1) = 2 * self%rand_unif_01() - 1
323 : 0 : rands(1) = 2 * rand_unif_01(self) - 1
324 : : !rands(2) = 2 * self%rand_unif_01() - 1
325 : 0 : rands(2) = 2 * rand_unif_01(self) - 1
326 [ # # ]: 0 : sum_sq = sum(rands**2)
327 [ # # ]: 0 : if (sum_sq <= 1) exit
328 : : end do
329 : :
330 : 0 : xy(1) = (rands(1)**2 - rands(2)**2) / sum_sq
331 : 0 : xy(2) = 2 * rands(1) * rands(2) / sum_sq
332 [ # # ]: 0 : xy = xy * radius
333 : 0 : end function rand_circle
334 : :
335 : : !> Sample point on a rand_sphere with given radius
336 [ # # ]: 0 : function rand_sphere(self, radius) result(xyz)
337 : :
338 : : class(rng_t), intent(inout) :: self
339 : : real(dp), intent(in) :: radius
340 : : real(dp) :: rands(2), xyz(3)
341 : : real(dp) :: sum_sq, tmp_sqrt
342 : :
343 : : ! Marsaglia method for uniform sampling on rand_sphere
344 : : do
345 : 0 : rands(1) = 2 * self%rand_unif_01() - 1
346 : 0 : rands(2) = 2 * self%rand_unif_01() - 1
347 [ # # ]: 0 : sum_sq = sum(rands**2)
348 [ # # ]: 0 : if (sum_sq <= 1) exit
349 : : end do
350 : :
351 : 0 : tmp_sqrt = sqrt(1 - sum_sq)
352 [ # # ]: 0 : xyz(1:2) = 2 * rands(1:2) * tmp_sqrt
353 : 0 : xyz(3) = 1 - 2 * sum_sq
354 [ # # ]: 0 : xyz = xyz * radius
355 : 0 : end function rand_sphere
356 : :
357 : 0 : function rand_choice(self, N) result(i)
358 : : class(rng_t), intent(inout) :: self
359 : : integer, intent(in) :: N
360 : : integer :: i
361 : 0 : i=int(self%rand_unif_01()*N)+1
362 : 0 : end function rand_choice
363 : :
364 : : !> Interal routine: get the next value (returned as 64 bit signed integer)
365 : 1701728 : function next(self) result(res)
366 : :
367 : : class(rng_t), intent(inout) :: self
368 : : integer(i8) :: res
369 : : integer(i8) :: t(2)
370 : :
371 [ + + ]: 5105184 : t = self%s
372 : 1701728 : res = t(1) + t(2)
373 : 1701728 : t(2) = ieor(t(1), t(2))
374 : 1701728 : self%s(1) = ieor(ieor(rotl(t(1), 55), t(2)), SHIFTL_(t(2), 14))
375 : 1701728 : self%s(2) = rotl(t(2), 36)
376 : 1701728 : end function next
377 : :
378 : : !> Helper function for next()
379 : 1701728 : pure function rotl(x, k) result(res)
380 : :
381 : : integer(i8), intent(in) :: x
382 : : integer, intent(in) :: k
383 : : integer(i8) :: res
384 : :
385 : 1701728 : res = ior(SHIFTL_(x, k), SHIFTR_(x, 64 - k))
386 : : end function rotl
387 : :
388 [ # # # # : 0 : end module m_random_xoroshiro128plus
# # # # #
# # # ]
|