Octopus
quickrnd.F90
Go to the documentation of this file.
1!! Copyright (C) 2002-2024 M. Marques, A. Castro, A. Rubio, G. Bertsch, X. Andrade, A. Buccheri, H. Menke
2!!
3!! This program is free software; you can redistribute it and/or modify
4!! it under the terms of the GNU General Public License as published by
5!! the Free Software Foundation; either version 2, or (at your option)
6!! any later version.
7!!
8!! This program is distributed in the hope that it will be useful,
9!! but WITHOUT ANY WARRANTY; without even the implied warranty of
10!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11!! GNU General Public License for more details.
12!!
13!! You should have received a copy of the GNU General Public License
14!! along with this program; if not, write to the Free Software
15!! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16!! 02110-1301, USA.
17!!
18
19#include "global.h"
20
21module quickrnd_oct_m
22 use, intrinsic :: iso_c_binding
23 use, intrinsic :: iso_fortran_env
24 use debug_oct_m
25 use global_oct_m
28
29 implicit none
30
31 private
32 public :: &
33 quickrnd, &
34 shiftseed, &
39
40 ! xorshift64 needs a good seed, in particular it cannot be seeded with 0.
41 ! These seeds are generated with the splitmix64 generator and have a
42 ! reasonable amount of entropy.
43 integer(int64), parameter, public :: &
44 splitmix64_123 = int(z'B4DC9BD462DE412B', int64), & ! splitmix64(123)
45 splitmix64_321 = int(z'E95F1C4EFCF85DEE', int64) ! splitmix64(321)
46
47 interface quickrnd
49 end interface quickrnd
50
51 ! Interfaces for C functions, defined in quickrnd.c
52 interface
53 function to_double_open01(x) result(z) bind(C, name='i64_to_open01')
54 use, intrinsic :: iso_c_binding
55 integer(c_int64_t), value, intent(in) :: x
56 real(c_double) :: z
57 end function to_double_open01
58
59 function splitmix64_mixer(x) result(z) bind(C, name='splitmix64_mixer_2pow63')
60 use, intrinsic :: iso_c_binding
61 integer(c_int64_t), value, intent(in) :: x
62 integer(c_int64_t) :: z
63 end function splitmix64_mixer
64
65 function hash_combine_ids(ids, n) result(s) bind(C, name='hash_combine_ids')
66 use, intrinsic :: iso_c_binding
67 integer(c_int64_t), intent(in) :: ids(*)
68 integer(c_int32_t), value, intent(in) :: n
69 integer(c_int64_t) :: s
70 end function hash_combine_ids
71 end interface
72
73contains
74
79 function xorshift64(x) result(res)
80 integer(int64), intent(inout) :: x
81 integer(int64) :: res
82
83 assert(x /= 0_int64)
84
85 x = ieor(x, shiftl(x, 13))
86 x = ieor(x, shiftr(x, 7))
87 x = ieor(x, shiftl(x, 17))
88
89 res = x
90 end function xorshift64
91
100 function to_double(x) result(d)
101 integer(int64), intent(in) :: x
102 real(real64) :: d
103 real(real64), parameter :: two_pow_minus_53 = real(z'3CA0000000000000', real64)
104
105 d = real(shiftr(x, 11), real64) * two_pow_minus_53
106 end function to_double
107
108 ! ---------------------------------------------------------
109
110 subroutine dquickrnd_single(iseed, rnd)
111 integer(int64), intent(inout) :: iseed
112 real(real64), intent(out) :: rnd
113
114 ! no PUSH_SUB, called too often
115
116 rnd = to_double(xorshift64(iseed))
117
118 end subroutine dquickrnd_single
119
120 ! ---------------------------------------------------------
121
122 subroutine dquickrnd_array(iseed, nn, rnd)
123 integer(int64), intent(inout) :: iseed
124 integer, intent(in) :: nn
125 real(real64), intent(inout) :: rnd(:)
126
127 integer :: ii
128
129 push_sub(quickrnd_array)
130
131 do ii = 1, nn
132 rnd(ii) = to_double(xorshift64(iseed))
133 end do
134
135 pop_sub(quickrnd_array)
136
137 end subroutine dquickrnd_array
139 ! ---------------------------------------------------------
140
141 subroutine zquickrnd_array(iseed, nn, rnd)
142 integer(int64), intent(inout) :: iseed
143 integer, intent(in) :: nn
144 complex(real64), intent(inout) :: rnd(:)
145
146 real(real64), parameter :: inv_sqrt2 = 1.0_real64 / sqrt(m_two)
147 real(real64) :: re, im
148 integer :: ii
149
150 push_sub(quickrnd_array)
151
152 do ii = 1, nn
153 re = to_double(xorshift64(iseed))
155 rnd(ii) = cmplx(re, im, real64) * inv_sqrt2
156 end do
157
158 pop_sub(quickrnd_array)
159
160 end subroutine zquickrnd_array
161
162 ! ---------------------------------------------------------
163
164 subroutine shiftseed(iseed, n)
165 integer(int64), intent(inout) :: iseed
166 integer(int64), intent(in) :: n
167
168 integer(int64) :: discard
169 integer(int64) :: ii
170
171 push_sub(shiftseed)
172
173 do ii = 1, n
174 discard = xorshift64(iseed)
175 end do
176
177 pop_sub(shiftseed)
178
179 end subroutine shiftseed
180
181
192 function random_integer_in_range(x_min, x_max, seed) result(rand)
193 integer(int64), intent(in ) :: x_min, x_max
194 integer(int64), intent(inout) :: seed
195 integer(int64) :: rand
196
197 integer(int64) :: range
198
199 assert(seed /= 0_int64)
200 seed = xorshift64(seed)
201 if (seed < 0_int64) seed = -seed ! Enforce a positive result
202 range = x_max - x_min + 1_int64
203 rand = x_min + mod(seed, range)
204
206
207
213 subroutine fisher_yates_shuffle(m, n, seed, values)
214 integer(int32), intent(in ) :: m
215 integer(int64), intent(in ) :: n
216 integer(int64), intent(inout) :: seed
217 integer(int64), intent( out) :: values(:)
218
219 integer(int64), allocatable :: indices(:)
220 integer(int64) :: i, j, temp
221
222 push_sub(fisher_yates_shuffle)
223
224 safe_allocate(indices(1:n))
225 do j = 1, n
226 indices(j) = j
227 end do
228
229 do i = 1, m
230 j = random_integer_in_range(i, n, seed)
231 ! Perform the swap
232 temp = indices(i)
233 indices(i) = indices(j)
234 indices(j) = temp
235 values(i) = indices(i)
236 end do
237
238 values(m) = indices(m)
239 safe_deallocate_a(indices)
240
241 pop_sub(fisher_yates_shuffle)
242
243 end subroutine fisher_yates_shuffle
244
245
254 real(real64) function u01_from_ids(ids) result(u)
255 integer(int64), intent(in) :: ids(:)
256
257 integer(int64) :: s, s_mixed
258 integer :: n_ids
260 n_ids = size(ids)
261 assert(n_ids > 0)
262
263 s = hash_combine_ids(ids, n_ids)
264 s_mixed = splitmix64_mixer(s)
265 u = to_double_open01(s_mixed)
266
267 end function u01_from_ids
268
269end module quickrnd_oct_m
270
271!! Local Variables:
272!! mode: f90
273!! coding: utf-8
274!! End:
int rand(void)
Definition: getopt_f.c:1459
double sqrt(double __x) __attribute__((__nothrow__
real(real64), parameter, public m_two
Definition: global.F90:193
subroutine dquickrnd_array(iseed, nn, rnd)
Definition: quickrnd.F90:218
real(real64) function to_double(x)
Generating uniform doubles in the unit interval.
Definition: quickrnd.F90:196
integer(int64), parameter, public splitmix64_321
Definition: quickrnd.F90:138
subroutine, public shiftseed(iseed, n)
Definition: quickrnd.F90:260
integer(int64) function random_integer_in_range(x_min, x_max, seed)
Generate a random int64 in the range .
Definition: quickrnd.F90:288
real(real64) function, public u01_from_ids(ids)
Mix an integer array of ids to give a random number in the range U(0,1), which is deterministic w....
Definition: quickrnd.F90:350
subroutine, public fisher_yates_shuffle(m, n, seed, values)
Return m random numbers from a range of with no replacement.
Definition: quickrnd.F90:309
subroutine dquickrnd_single(iseed, rnd)
Definition: quickrnd.F90:206
integer(int64) function xorshift64(x)
xorshift64 pseudorandom number generator
Definition: quickrnd.F90:175
subroutine zquickrnd_array(iseed, nn, rnd)
Definition: quickrnd.F90:237