Skip to content

Commit a1c911c

Browse files
Jim-215-Fisherjvdp1milancurcic
authored
Probability Distribution and Statistical Functions -- PRNG Module (#271)
* change in Makefile.manual changed the object name from stdlib_stats_distribution_implementation.o to stdlib_stats_distribution_imp.o * New * New * New * convert .fypp .f90 for test file * correct mispell in CMakeLists.txt * change rand to dist_rand * change rand to dist_rand * Add files via upload * remove tab * minor change * Add specs * correct random_seed * minor change * correct syntax * Update doc/specs/stdlib_stats_distribution_PRNG.md Co-authored-by: Jeremie Vandenplas <[email protected]> * Update doc/specs/stdlib_stats_distribution_PRNG.md Co-authored-by: Jeremie Vandenplas <[email protected]> * Update doc/specs/stdlib_stats_distribution_PRNG.md Co-authored-by: Jeremie Vandenplas <[email protected]> * Update doc/specs/stdlib_stats_distribution_PRNG.md Co-authored-by: Jeremie Vandenplas <[email protected]> * Update src/stdlib_stats_distribution_PRNG.fypp Co-authored-by: Jeremie Vandenplas <[email protected]> * Update src/stdlib_stats_distribution_PRNG.fypp Co-authored-by: Jeremie Vandenplas <[email protected]> * Update src/stdlib_stats_distribution_PRNG.fypp Co-authored-by: Jeremie Vandenplas <[email protected]> * Update src/stdlib_stats_distribution_PRNG.fypp Co-authored-by: Jeremie Vandenplas <[email protected]> * Update src/stdlib_stats_distribution_PRNG.fypp Co-authored-by: Jeremie Vandenplas <[email protected]> * Update src/tests/stats/test_distribution_PRNG.f90 Co-authored-by: Jeremie Vandenplas <[email protected]> * Some grammer change * Add reference link * Add reference links * change description for dist_rand function * Add files via upload * Add files via upload * Add files via upload * Update stdlib_stats_distribution_PRNG.md * Update stdlib_stats_distribution_PRNG.md * Update Makefile.manual * Update Makefile.manual * Update Makefile.manual * Update Makefile.manual * Update CMakeLists.txt * Update CMakeLists.txt * Update Makefile.manual * Update CMakeLists.txt * Update Makefile.manual * Update CMakeLists.txt * Update CMakeLists.txt * Update Makefile.manual * Update CMakeLists.txt * Update CMakeLists.txt * Update Makefile.manual * Update CMakeLists.txt * Update Makefile.manual * Update Makefile.manual * Update CMakeLists.txt * Update Makefile.manual * Delete test_distribution_PRNG.fypp * Update Makefile.manual * Update Makefile.manual * Removed jump and long_jump procedures * Update Makefile.manual * Update stdlib_stats_distribution_PRNG.fypp * Update stdlib_stats_distribution_PRNG.fypp * Update stdlib_stats_distribution_PRNG.fypp * Update stdlib_stats_distribution_PRNG.fypp * Update Makefile.manual * Update Makefile.manual * Update Makefile.manual * explicit import * makr rol64 pure * minor style fix * minor style fix to line continuation * Update src/stdlib_stats_distribution_PRNG.fypp Co-authored-by: Milan Curcic <[email protected]> * Update stdlib_stats_distribution_PRNG.fypp * Update stdlib_stats_distribution_PRNG.fypp Co-authored-by: Jeremie Vandenplas <[email protected]> Co-authored-by: milancurcic <[email protected]>
1 parent cfe8d55 commit a1c911c

8 files changed

+358
-3
lines changed

doc/specs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This is and index/directory of the specifications (specs) for each new module/fe
1919
- [optval](./stdlib_optval.html) - Fallback value for optional arguments
2020
- [quadrature](./stdlib_quadrature.html) - Numerical integration
2121
- [stats](./stdlib_stats.html) - Descriptive Statistics
22+
- [stats_distribution_PRNG](./stdlib_stats_distribution_PRNG.html) - Probability Distributions random number generator
2223

2324
## Missing specs
2425

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
title: stats_distribution
3+
---
4+
5+
# Statistical Distributions -- Pseudorandom Number Generator Module
6+
7+
[TOC]
8+
9+
## `random_seed` - set or get a value of seed to the probability distribution pseudorandom number generator
10+
11+
### Status
12+
13+
Experimental
14+
15+
### Description
16+
17+
Set or get the seed value before calling the probability distribution pseudorandom number generator for variates.
18+
19+
### Syntax
20+
21+
`call [[stdlib_stats_distribution_PRNG(module):random_seed(interface)]](put, get)`
22+
23+
### Arguments
24+
25+
`put`: argument has `intent(in)` and may be a scalar of type `integer`.
26+
27+
`get`: argument has `intent(out)` and is a scalar of type `integer`.
28+
29+
### Return value
30+
31+
Return a scalar of type `integer`.
32+
33+
### Example
34+
35+
```fortran
36+
program demo_random_seed
37+
use stdlib_stats_distribution_PRNG, only : random_seed
38+
implicit none
39+
integer :: seed_put, seed_get
40+
41+
seed_put = 1234567
42+
call random_seed(seed_put, seed_get) ! set and get current value of seed
43+
end program demo_random_seed
44+
```
45+
46+
## `dist_rand` - Get a random integer with specified kind
47+
48+
### Status
49+
50+
Experimental
51+
52+
### Description
53+
54+
Generate an integer pseudorandom number in a specific range [-2^k, 2^k - 1] according to the input integer kind n. This pseudorandom number will be operated by bit opeartors instead of normal arithmetic operators.
55+
56+
### Syntax
57+
58+
`result = [[stdlib_stats_distribution_PRNG(module):dist_rand(interface)]](n)`
59+
60+
### Arguments
61+
62+
`n`: argument has `intent(in)` is a scalar of type `integer`.
63+
64+
### Return value
65+
66+
Return a scalar of type `integer`.
67+
68+
### Example
69+
70+
```fortran
71+
program demo_dist_rand
72+
use stdlib_stats_distribution_PRNG, only : dist_rand, random_seed
73+
implicit none
74+
integer :: put, get
75+
76+
put = 135792468
77+
call random_seed(put, get) ! set and get current value of seed
78+
print *, dist_rand(1_int8) ! random integer in [-2^7, 2^7 - 1]
79+
! -90
80+
print *, dist_rand(1_int16) ! random integer in [-2^15, 2^15 - 1]
81+
! -32725
82+
print *, dist_rand(1_int32) ! random integer in [-2^31, 2^31 - 1]
83+
! -1601563881
84+
print *, dist_rand(1_int64) ! random integer in [-2^63, 2^63 - 1]
85+
! 180977695517992208
86+
end program demo_dist_rand
87+
```

src/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ set(fppFiles
2121
stdlib_quadrature.fypp
2222
stdlib_quadrature_trapz.fypp
2323
stdlib_quadrature_simps.fypp
24+
stdlib_stats_distribution_PRNG.fypp
2425
)
2526

2627

src/Makefile.manual

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ SRCFYPP =\
1717
stdlib_stats_moment_all.fypp \
1818
stdlib_stats_moment_mask.fypp \
1919
stdlib_stats_moment_scalar.fypp \
20-
stdlib_stats_var.fypp
20+
stdlib_stats_var.fypp \
21+
stdlib_stats_distribution_PRNG.fypp
2122

2223
SRC = f18estop.f90 \
2324
stdlib_ascii.f90 \
@@ -104,3 +105,6 @@ stdlib_stats_var.o: \
104105
stdlib_optval.o \
105106
stdlib_kinds.o \
106107
stdlib_stats.o
108+
stdlib_stats_distribution_PRNG.o: \
109+
stdlib_kinds.o \
110+
stdlib_error.o
+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#:include "common.fypp"
2+
module stdlib_stats_distribution_PRNG
3+
use stdlib_kinds, only: int8, int16, int32, int64
4+
use stdlib_error, only: error_stop
5+
implicit none
6+
private
7+
integer, parameter :: MAX_INT_BIT_SIZE = bit_size(1_int64)
8+
integer(int64) :: st(4) ! internal states for xoshiro256ss function
9+
integer(int64) :: si = 614872703977525537_int64 ! default seed value
10+
logical :: seed_initialized = .false.
11+
12+
public :: random_seed
13+
public :: dist_rand
14+
15+
16+
interface dist_rand
17+
!! Version experimental
18+
!!
19+
!! Generation of random integers with different kinds
20+
!! ([Specification](../page/specs/stdlib_stats_distribution_PRNG.html#
21+
!! description))
22+
#:for k1, t1 in INT_KINDS_TYPES
23+
module procedure dist_rand_${t1[0]}$${k1}$
24+
#:endfor
25+
end interface dist_rand
26+
27+
interface random_seed
28+
!! Version experimental
29+
!!
30+
!! Set seed value for random number generator
31+
!! ([Specification](../page/specs/stdlib_stats_distribution_PRNG.html#
32+
!! description))
33+
!!
34+
#:for k1, t1 in INT_KINDS_TYPES
35+
module procedure random_distribution_seed_${t1[0]}$${k1}$
36+
#:endfor
37+
end interface random_seed
38+
39+
40+
contains
41+
42+
#:for k1, t1 in INT_KINDS_TYPES
43+
function dist_rand_${t1[0]}$${k1}$(n) result(res)
44+
!! Random integer generation for various kinds
45+
!! result = [-2^k, 2^k - 1], k = 7, 15, 31, 63, depending on input kind
46+
!! Result will be operated by bitwise operators to generate desired integer
47+
!! and real pseudorandom numbers
48+
!!
49+
${t1}$, intent(in) :: n
50+
${t1}$ :: res
51+
integer :: k
52+
53+
k = MAX_INT_BIT_SIZE - bit_size(n)
54+
if(k < 0) call error_stop("Error(dist_rand): Integer bit size is" &
55+
//" greater than 64bit")
56+
res = shiftr(xoshiro256ss( ), k)
57+
end function dist_rand_${t1[0]}$${k1}$
58+
59+
#:endfor
60+
61+
function xoshiro256ss( ) result (res)
62+
! Generate random 64-bit integers
63+
! Written in 2018 by David Blackman and Sebastiano Vigna ([email protected])
64+
! http://prng.di.unimi.it/xoshiro256starstar.c
65+
!
66+
! This is xoshiro256** 1.0, one of our all-purpose, rock-solid
67+
! generators. It has excellent (sub-ns) speed, a state (256 bits) that is
68+
! large enough for any parallel application, and it passes all tests we
69+
! are aware of.
70+
!
71+
! The state must be seeded so that it is not everywhere zero. If you have
72+
! a 64-bit seed, we suggest to seed a splitmix64 generator and use its
73+
! output to fill st.
74+
!
75+
! Fortran 90 version translated from C by Jim-215-Fisher
76+
!
77+
integer(int64) :: res, t
78+
79+
if(.not. seed_initialized) call random_distribution_seed_iint64(si,t)
80+
res = rol64(st(2) * 5, 7) * 9
81+
t = shiftl(st(2), 17)
82+
st(3) = ieor(st(3), st(1))
83+
st(4) = ieor(st(4), st(2))
84+
st(2) = ieor(st(2), st(3))
85+
st(1) = ieor(st(1), st(4))
86+
st(3) = ieor(st(3), t)
87+
st(4) = rol64(st(4), 45)
88+
end function xoshiro256ss
89+
90+
pure function rol64(x, k) result(res)
91+
integer(int64), intent(in) :: x
92+
integer, intent(in) :: k
93+
integer(int64) :: t1, t2, res
94+
95+
t1 = shiftr(x, (64 - k))
96+
t2 = shiftl(x, k)
97+
res = ior(t1, t2)
98+
end function rol64
99+
100+
101+
function splitmix64(s) result(res)
102+
! Written in 2015 by Sebastiano Vigna ([email protected])
103+
! This is a fixed-increment version of Java 8's SplittableRandom
104+
! generator.
105+
! See http://dx.doi.org/10.1145/2714064.2660195 and
106+
! http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
107+
!
108+
! It is a very fast generator passing BigCrush, and it can be useful if
109+
! for some reason you absolutely want 64 bits of state.
110+
!
111+
! Fortran 90 translated from C by Jim-215-Fisher
112+
!
113+
integer(int64) :: res
114+
integer(int64), intent(in), optional :: s
115+
integer(int64) :: int01 = -7046029254386353131_int64, &
116+
int02 = -4658895280553007687_int64, &
117+
int03 = -7723592293110705685_int64
118+
! Values are converted from C unsigned integer of 0x9e3779b97f4a7c15,
119+
! 0xbf58476d1ce4e5b9, 0x94d049bb133111eb
120+
121+
if(present(s)) si = s
122+
res = si
123+
si = res + int01
124+
res = ieor(res, shiftr(res, 30)) * int02
125+
res = ieor(res, shiftr(res, 27)) * int03
126+
res = ieor(res, shiftr(res, 31))
127+
end function splitmix64
128+
129+
#:for k1, t1 in INT_KINDS_TYPES
130+
subroutine random_distribution_seed_${t1[0]}$${k1}$(put, get)
131+
!! Set seed value for random number generator
132+
!!
133+
${t1}$, intent(in) :: put
134+
${t1}$, intent(out) :: get
135+
integer(int64) :: tmp
136+
integer :: i
137+
138+
tmp = splitmix64(int(put, kind = int64))
139+
do i = 1, 10
140+
tmp = splitmix64( )
141+
end do
142+
do i = 1, 4
143+
tmp = splitmix64( )
144+
st(i) = tmp
145+
end do
146+
get = int(tmp, kind = ${k1}$)
147+
seed_initialized = .true.
148+
end subroutine random_distribution_seed_${t1[0]}$${k1}$
149+
150+
#:endfor
151+
end module stdlib_stats_distribution_PRNG

src/tests/stats/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ ADDTEST(moment)
55
ADDTEST(rawmoment)
66
ADDTEST(var)
77
ADDTEST(varn)
8+
ADDTEST(distribution_PRNG)
89

910
if(DEFINED CMAKE_MAXIMUM_RANK)
1011
if(${CMAKE_MAXIMUM_RANK} GREATER 7)

src/tests/stats/Makefile.manual

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
PROGS_SRC = test_mean.f90 test_moment.f90 test_var.f90
21

3-
include ../Makefile.manual.test.mk
2+
PROGS_SRC = test_mean.f90 test_moment.f90 test_var.f90 \
3+
test_distribution_PRNG.f90
4+
5+
include ../Makefile.manual.test.mk
+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
program test_distribution_PRNG
2+
use stdlib_error, only : check
3+
use stdlib_kinds, only: int8, int16, int32, int64
4+
use stdlib_stats_distribution_PRNG, only : random_seed, dist_rand
5+
6+
implicit none
7+
logical :: warn = .true.
8+
9+
call test_random_seed
10+
call test_random_rand_iint8
11+
call test_random_rand_iint16
12+
call test_random_rand_iint32
13+
call test_random_rand_iint64
14+
15+
16+
contains
17+
18+
subroutine test_random_seed
19+
integer :: put, get, res(5)
20+
integer :: ans(5) = [-1859553078, -1933696596, -642834430, &
21+
1711399314, 1548311463]
22+
integer :: i
23+
24+
print *, ""
25+
print *, "Test random_seed"
26+
put = 135792468
27+
do i = 1, 5
28+
call random_seed(put,get)
29+
res(i) = get
30+
put = get
31+
end do
32+
call check(all(res == ans), msg="random seed test failed.",warn=warn)
33+
end subroutine test_random_seed
34+
35+
subroutine test_random_rand_iint8
36+
integer :: put, get, i
37+
38+
integer(int8) :: res(5), ans(5)=[118, -15, -72, 101, 70]
39+
40+
41+
print *, ""
42+
print *, "Test random_rand with kind int8"
43+
put = 12345678
44+
call random_seed(put, get)
45+
do i = 1, 5
46+
res(i) = dist_rand(1_int8)
47+
end do
48+
call check(all(res == ans), msg="random_rand with kind int8 test" &
49+
//" failed.", warn=warn)
50+
end subroutine test_random_rand_iint8
51+
52+
subroutine test_random_rand_iint16
53+
integer :: put, get, i
54+
55+
integer(int16) :: res(5), ans(5)=[30286, -3799, -18204, 25947, 18148]
56+
57+
58+
print *, ""
59+
print *, "Test random_rand with kind int16"
60+
put = 12345678
61+
call random_seed(put, get)
62+
do i = 1, 5
63+
res(i) = dist_rand(1_int16)
64+
end do
65+
call check(all(res == ans), msg="random_rand with kind int16 test" &
66+
//" failed.", warn=warn)
67+
end subroutine test_random_rand_iint16
68+
69+
subroutine test_random_rand_iint32
70+
integer :: put, get, i
71+
72+
integer(int32) :: res(5), ans(5)=[1984865646, -248954393, -1192993267, &
73+
1700514835, 1189401802]
74+
75+
76+
print *, ""
77+
print *, "Test random_rand with kind int32"
78+
put = 12345678
79+
call random_seed(put, get)
80+
do i = 1, 5
81+
res(i) = dist_rand(1_int32)
82+
end do
83+
call check(all(res == ans), msg="random_rand with kind int32 test" &
84+
//" failed.", warn=warn)
85+
end subroutine test_random_rand_iint32
86+
87+
subroutine test_random_rand_iint64
88+
integer :: put, get, i
89+
90+
integer(int64) :: res(5), ans(5)=[8524933037632333570_int64, &
91+
-1069250973542918798_int64, &
92+
-5123867065024149335_int64, &
93+
7303655603304982073_int64, &
94+
5108441843522503546_int64]
95+
96+
97+
print *, ""
98+
print *, "Test random_rand with kind int64"
99+
put = 12345678
100+
call random_seed(put, get)
101+
do i = 1, 5
102+
res(i) = dist_rand(1_int64)
103+
end do
104+
call check(all(res == ans), msg="random_rand with kind int64 test" &
105+
//" failed.", warn=warn)
106+
end subroutine test_random_rand_iint64
107+
108+
end program test_distribution_PRNG

0 commit comments

Comments
 (0)