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