LCOV - code coverage report
Current view: top level - shared/common/src/28_numeric_noabirule - m_random_xoroshiro128plus.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 46.7 % 105 49
Test Date: 2026-09-20 15:27:41 Functions: 36.8 % 19 7

            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
        

Generated by: LCOV version 2.3-1