FMS 2025.01-dev
Flexible Modeling System
Loading...
Searching...
No Matches
mersennetwister.F90
1!***********************************************************************
2!* GNU Lesser General Public License
3!*
4!* This file is part of the GFDL Flexible Modeling System (FMS).
5!*
6!* FMS is free software: you can redistribute it and/or modify it under
7!* the terms of the GNU Lesser General Public License as published by
8!* the Free Software Foundation, either version 3 of the License, or (at
9!* your option) any later version.
10!*
11!* FMS is distributed in the hope that it will be useful, but WITHOUT
12!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14!* for more details.
15!*
16!* You should have received a copy of the GNU Lesser General Public
17!* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18!***********************************************************************
19
20!> @defgroup mersennetwister_mod MersenneTwister_mod
21!> @ingroup random_numbers
22!> @brief Fortran-95 implementation of the Mersenne Twister 19937 algorithm
23!> @author Robert Pincus
24!!
25!! Users must declare one or more variables of type randomNumberSequence in the calling
26!! procedure which are then initialized using a required seed. If the
27!! variable is not initialized the random numbers will all be 0.
28!! For example:
29!! program testRandoms
30!! use RandomNumbers
31!! type(randomNumberSequence) :: randomNumbers
32!! integer :: i
33!!
34!! randomNumbers = new_RandomNumberSequence(seed = 100)
35!! do i = 1, 10
36!! print ('(f12.10, 2x)'), getRandomReal(randomNumbers)
37!! end do
38!! end program testRandoms
39!!
40!! Fortran-95 implementation by
41!! Robert Pincus
42!! NOAA-CIRES Climate Diagnostics Center
43!! Boulder, CO 80305
44!! email: Robert.Pincus@colorado.edu
45!!
46!! This documentation in the original C program reads:
47!! -------------------------------------------------------------
48!! A C-program for MT19937, with initialization improved 2002/2/10.
49!! Coded by Takuji Nishimura and Makoto Matsumoto.
50!! This is a faster version by taking Shawn Cokus's optimization,
51!! Matthe Bellew's simplification, Isaku Wada's real version.
52!!
53!! Before using, initialize the state by using init_genrand(seed)
54!! or init_by_array(init_key, key_length).
55!!
56!! Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
57!! All rights reserved.
58!!
59!! Redistribution and use in source and binary forms, with or without
60!! modification, are permitted provided that the following conditions
61!! are met:
62!!
63!! 1. Redistributions of source code must retain the above copyright
64!! notice, this list of conditions and the following disclaimer.
65!!
66!! 2. Redistributions in binary form must reproduce the above copyright
67!! notice, this list of conditions and the following disclaimer in the
68!! documentation and/or other materials provided with the distribution.
69!!
70!! 3. The names of its contributors may not be used to endorse or promote
71!! products derived from this software without specific prior written
72!! permission.
73!!
74!! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
75!! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
76!! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
77!! A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
78!! CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
79!! EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
80!! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
81!! PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
82!! LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
83!! NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
84!! SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
85!!
86!!
87!! Any feedback is very welcome.
88!! http://www.math.keio.ac.jp/matumoto/emt.html
89!! email: matumoto@math.keio.ac.jp
90
91!> @addtogroup mersennetwister_mod
92!> @{
93module mersennetwister_mod
94! -------------------------------------------------------------
95 use platform_mod, only: r8_kind, i8_kind
96
97 implicit none
98 private
99
100 ! Algorithm parameters
101 ! -------
102 ! Period parameters
103 integer, parameter :: blocksize = 624, &
104 m = 397, &
105 matrix_a = -1727483681, & !< constant vector a (0x9908b0dfUL)
106 umask = -2147483648_i8_kind, & !< most significant w-r bits (0x80000000UL)
107 lmask = 2147483647 !< least significant r bits (0x7fffffffUL)
108 !> Tempering parameters
109 integer, parameter :: tmaskb= -1658038656, & !< (0x9d2c5680UL)
110 tmaskc= -272236544 !< (0xefc60000UL)
111 ! -------
112 !> @}
113
114 !> The type containing the state variable
115 !> @ingroup mersennetwister_mod
117 integer :: currentElement ! = blockSize
118 integer, dimension(0:blockSize -1) :: state ! = 0
119 end type randomnumbersequence
120
121 !> @ingroup mersennetwister_mod
123 module procedure initialize_scalar, initialize_vector
124 end interface new_randomnumbersequence
125
126 public :: randomnumbersequence
127 public :: new_randomnumbersequence, finalize_randomnumbersequence, &
129!> @addtogroup mersennetwister_mod
130!> @{
131! -------------------------------------------------------------
132contains
133 ! -------------------------------------------------------------
134 ! Private functions
135 ! ---------------------------
136 function mixbits(u, v)
137 integer, intent( in) :: u, v
138 integer :: mixbits
139
140 mixbits = ior(iand(u, umask), iand(v, lmask))
141 end function mixbits
142 ! ---------------------------
143 function twist(u, v)
144 integer, intent( in) :: u, v
145 integer :: twist
146
147 ! Local variable
148 integer, parameter, dimension(0:1) :: t_matrix = (/ 0, matrix_a /)
149
150 twist = ieor(ishft(mixbits(u, v), -1), t_matrix(iand(v, 1)))
151 twist = ieor(ishft(mixbits(u, v), -1), t_matrix(iand(v, 1)))
152 end function twist
153 ! ---------------------------
154 subroutine nextstate(twister)
155 type(randomNumberSequence), intent(inout) :: twister
156
157 ! Local variables
158 integer :: k
159
160 do k = 0, blocksize - m - 1
161 twister%state(k) = ieor(twister%state(k + m), &
162 twist(twister%state(k), twister%state(k + 1)))
163 end do
164 do k = blocksize - m, blocksize - 2
165 twister%state(k) = ieor(twister%state(k + m - blocksize), &
166 twist(twister%state(k), twister%state(k + 1)))
167 end do
168 twister%state(blocksize - 1) = ieor(twister%state(m - 1), &
169 twist(twister%state(blocksize - 1), twister%state(0)))
170 twister%currentElement = 0
171
172 end subroutine nextstate
173 ! ---------------------------
174 elemental function temper(y)
175 integer, intent(in) :: y
176 integer :: temper
177
178 integer :: x
179
180 ! Tempering
181 x = ieor(y, ishft(y, -11))
182 x = ieor(x, iand(ishft(x, 7), tmaskb))
183 x = ieor(x, iand(ishft(x, 15), tmaskc))
184 temper = ieor(x, ishft(x, -18))
185 end function temper
186 ! -------------------------------------------------------------
187 ! Public (but hidden) functions
188 ! --------------------
189 function initialize_scalar(seed) result(twister)
190 integer, intent(in ) :: seed
191 type(randomNumberSequence) :: twister
192
193 integer :: i
194 ! See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. In the previous versions,
195 ! MSBs of the seed affect only MSBs of the array state[].
196 ! 2002/01/09 modified by Makoto Matsumoto
197
198 twister%state(0) = iand(seed, -1)
199 do i = 1, blocksize - 1 ! ubound(twister%state)
200 twister%state(i) = 1812433253 * ieor(twister%state(i-1), &
201 ishft(twister%state(i-1), -30)) + i
202 twister%state(i) = iand(twister%state(i), -1) ! for >32 bit machines
203 end do
204 twister%currentElement = blocksize
205 end function initialize_scalar
206 ! -------------------------------------------------------------
207 function initialize_vector(seed) result(twister)
208 integer, dimension(0:), intent(in) :: seed
209 type(randomNumberSequence) :: twister
210
211 integer :: i, j, k, nFirstLoop, nWraps
212
213 nwraps = 0
214 twister = initialize_scalar(19650218)
215
216 nfirstloop = max(blocksize, size(seed))
217 do k = 1, nfirstloop
218 i = mod(k + nwraps, blocksize)
219 j = mod(k - 1, size(seed))
220 if(i == 0) then
221 twister%state(i) = twister%state(blocksize - 1)
222 twister%state(1) = ieor(twister%state(1), &
223 ieor(twister%state(1-1), &
224 ishft(twister%state(1-1), -30)) * 1664525) + &
225 seed(j) + j ! Non-linear
226 twister%state(i) = iand(twister%state(i), -1) ! for >32 bit machines
227 nwraps = nwraps + 1
228 else
229 twister%state(i) = ieor(twister%state(i), &
230 ieor(twister%state(i-1), &
231 ishft(twister%state(i-1), -30)) * 1664525) + &
232 seed(j) + j ! Non-linear
233 twister%state(i) = iand(twister%state(i), -1) ! for >32 bit machines
234 end if
235 end do
236
237 !
238 ! Walk through the state array, beginning where we left off in the block above
239 !
240 do i = mod(nfirstloop, blocksize) + nwraps + 1, blocksize - 1
241 twister%state(i) = ieor(twister%state(i), &
242 ieor(twister%state(i-1), &
243 ishft(twister%state(i-1), -30)) * 1566083941) - i ! Non-linear
244 twister%state(i) = iand(twister%state(i), -1) ! for >32 bit machines
245 end do
246
247 twister%state(0) = twister%state(blocksize - 1)
248
249 do i = 1, mod(nfirstloop, blocksize) + nwraps
250 twister%state(i) = ieor(twister%state(i), &
251 ieor(twister%state(i-1), &
252 ishft(twister%state(i-1), -30)) * 1566083941) - i ! Non-linear
253 twister%state(i) = iand(twister%state(i), -1) ! for >32 bit machines
254 end do
255
256 twister%state(0) = umask
257 twister%currentElement = blocksize
258
259 end function initialize_vector
260 ! -------------------------------------------------------------
261 ! Public functions
262 ! --------------------
263 !> Generate a random integer on the interval [0,0xffffffff]
264 !!
265 !> Equivalent to genrand_int32 in the C code.
266 !! Fortran doesn't have a type that's unsigned like C does,
267 !! so this is integers in the range -2**31 - 2**31
268 !! All functions for getting random numbers call this one,
269 !! then manipulate the result
270 function getrandomint(twister)
271 type(randomnumbersequence), intent(inout) :: twister
272 integer :: getrandomint
273
274 if(twister%currentElement >= blocksize) call nextstate(twister)
275
276 getrandomint = temper(twister%state(twister%currentElement))
277 twister%currentElement = twister%currentElement + 1
278
279 end function getrandomint
280 ! --------------------
281 !> Generate a random integer on the interval [0,0x7fffffff]
282 !! or [0,2**31].
283 !! Equivalent to genrand_int31 in the C code.
284 function getrandompositiveint(twister)
285 type(randomnumbersequence), intent(inout) :: twister
286 integer :: getrandompositiveint
287
288 ! Local integers
289 integer :: localint
290
291 localint = getrandomint(twister)
292 getrandompositiveint = ishft(localint, -1)
293
294 end function getrandompositiveint
295 ! --------------------
296 !> Generate a random number on [0,1]
297 !! Equivalent to genrand_real1 in the C code.
298 !! The result is stored as double precision but has 32 bit resolution
299 function getrandomreal(twister)
300 type(randomnumbersequence), intent(inout) :: twister
301 real(r8_kind) :: getrandomreal
302
303 integer :: localint
304
305 localint = getrandomint(twister)
306 if(localint < 0) then
307 getrandomreal = dble(localint + 2.0d0**32)/(2.0d0**32 - 1.0d0)
308 else
309 getrandomreal = dble(localint )/(2.0d0**32 - 1.0d0)
310 end if
311 end function getrandomreal
312 ! --------------------
313 subroutine finalize_randomnumbersequence(twister)
314 type(randomnumbersequence), intent(inout) :: twister
315
316 twister%currentElement = blocksize
317 twister%state(:) = 0
318 end subroutine finalize_randomnumbersequence
319 ! --------------------
320end module mersennetwister_mod
321!> @}
322! close documentation grouping
real(r8_kind) function, public getrandomreal(twister)
Generate a random number on [0,1] Equivalent to genrand_real1 in the C code. The result is stored as ...
integer function, public getrandomint(twister)
Generate a random integer on the interval [0,0xffffffff].
integer, parameter matrix_a
constant vector a (0x9908b0dfUL)
integer, parameter lmask
least significant r bits (0x7fffffffUL)
integer, parameter umask
most significant w-r bits (0x80000000UL)
integer, parameter tmaskc
(0xefc60000UL)
integer, parameter tmaskb
(0x9d2c5680UL)
integer function, public getrandompositiveint(twister)
Generate a random integer on the interval [0,0x7fffffff] or [0,2**31]. Equivalent to genrand_int31 in...
The type containing the state variable.