FMS  2024.03
Flexible Modeling System
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 !> @{
93 module 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 ! -------------------------------------------------------------
132 contains
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  ! --------------------
320 end module mersennetwister_mod
321 !> @}
322 ! close documentation grouping
integer, parameter matrix_a
constant vector a (0x9908b0dfUL)
integer function, public getrandompositiveint(twister)
Generate a random integer on the interval [0,0x7fffffff] or [0,2**31]. Equivalent to genrand_int31 in...
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)
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].
The type containing the state variable.