|
|
|
|
|
|
|
|
|
|
| module bob_rng
|
| use bob_kinds
|
| use bob_errors
|
| implicit none
|
| private
|
|
|
|
|
| type, public :: bob_rng_state
|
| integer(i8) :: seed = 0_i8
|
| integer(i8) :: state(4) = 0_i8
|
| integer(i8) :: call_count = 0_i8
|
| logical(lk) :: is_initialized = .false.
|
| contains
|
| procedure :: init => rng_init
|
| procedure :: uniform => rng_uniform
|
| procedure :: normal => rng_normal
|
| procedure :: integer_range => rng_integer_range
|
| procedure :: choice => rng_choice
|
| end type bob_rng_state
|
|
|
| public :: bob_rng_create
|
| public :: bob_rng_destroy
|
| public :: bob_rng_seed
|
|
|
| contains
|
|
|
|
|
| subroutine rng_init(this, seed)
|
| class(bob_rng_state), intent(inout) :: this
|
| integer(i8), intent(in) :: seed
|
| integer :: i
|
|
|
| this%seed = seed
|
| this%call_count = 0_i8
|
|
|
|
|
| this%state(1) = splitmix64(seed)
|
| this%state(2) = splitmix64(this%state(1))
|
| this%state(3) = splitmix64(this%state(2))
|
| this%state(4) = splitmix64(this%state(3))
|
|
|
| this%is_initialized = .true.
|
| end subroutine rng_init
|
|
|
|
|
| function rng_uniform(this) result(r)
|
| class(bob_rng_state), intent(inout) :: this
|
| real(wp) :: r
|
| integer(i8) :: bits
|
|
|
| if (.not. this%is_initialized) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "RNG not initialized", "rng_uniform")
|
| r = ZERO
|
| return
|
| end if
|
|
|
| bits = xoshiro256ss(this%state)
|
| this%call_count = this%call_count + 1
|
|
|
|
|
| r = real(ishft(bits, -11), wp) * (ONE / real(ishft(1_i8, 53), wp))
|
|
|
| call bob_clear_error()
|
| end function rng_uniform
|
|
|
|
|
| function rng_normal(this, mean, stddev) result(r)
|
| class(bob_rng_state), intent(inout) :: this
|
| real(wp), intent(in), optional :: mean, stddev
|
| real(wp) :: r
|
| real(wp) :: u1, u2, z0
|
| real(wp) :: mu, sigma
|
|
|
| mu = ZERO
|
| sigma = ONE
|
| if (present(mean)) mu = mean
|
| if (present(stddev)) sigma = stddev
|
|
|
|
|
| u1 = this%uniform()
|
| u2 = this%uniform()
|
|
|
| z0 = sqrt(-TWO * log(u1)) * cos(TWO * PI * u2)
|
| r = mu + sigma * z0
|
| end function rng_normal
|
|
|
|
|
| function rng_integer_range(this, min_val, max_val) result(r)
|
| class(bob_rng_state), intent(inout) :: this
|
| integer(i8), intent(in) :: min_val, max_val
|
| integer(i8) :: r
|
| real(wp) :: u
|
|
|
| if (min_val > max_val) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "min_val > max_val", "rng_integer_range")
|
| r = min_val
|
| return
|
| end if
|
|
|
| u = this%uniform()
|
| r = min_val + int(u * real(max_val - min_val + 1, wp), i8)
|
|
|
|
|
| if (r > max_val) r = max_val
|
|
|
| call bob_clear_error()
|
| end function rng_integer_range
|
|
|
|
|
| function rng_choice(this, array, n) result(idx)
|
| class(bob_rng_state), intent(inout) :: this
|
| integer(i8), intent(in) :: n
|
| integer(i8), intent(in) :: array(n)
|
| integer(i8) :: idx
|
|
|
| if (n <= 0) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Array size must be positive", "rng_choice")
|
| idx = 0
|
| return
|
| end if
|
|
|
| idx = this%integer_range(1_i8, n)
|
| call bob_clear_error()
|
| end function rng_choice
|
|
|
|
|
| function xoshiro256ss(state) result(r)
|
| integer(i8), intent(inout) :: state(4)
|
| integer(i8) :: r, t
|
|
|
|
|
| r = rotl64(state(2) * 5_i8, 7) * 9_i8
|
|
|
| t = ishft(state(2), 17)
|
|
|
| state(3) = ieor(state(3), state(1))
|
| state(4) = ieor(state(4), state(2))
|
| state(2) = ieor(state(2), state(3))
|
| state(1) = ieor(state(1), state(4))
|
|
|
| state(3) = ieor(state(3), t)
|
| state(4) = rotl64(state(4), 45)
|
| end function xoshiro256ss
|
|
|
|
|
| function rotl64(x, k) result(r)
|
| integer(i8), intent(in) :: x
|
| integer, intent(in) :: k
|
| integer(i8) :: r
|
|
|
| r = ior(ishft(x, k), ishft(x, k - 64))
|
| end function rotl64
|
|
|
|
|
| function splitmix64(x) result(r)
|
| integer(i8), intent(in) :: x
|
| integer(i8) :: r, z
|
|
|
| z = x + int(z'9e3779b97f4a7c15', i8)
|
| z = ieor(z, ishft(z, -30)) * int(z'bf58476d1ce4e5b9', i8)
|
| z = ieor(z, ishft(z, -27)) * int(z'94d049bb133111eb', i8)
|
| r = ieor(z, ishft(z, -31))
|
| end function splitmix64
|
|
|
|
|
| function bob_rng_create(seed) result(rng_ptr) bind(C, name="bob_rng_create")
|
| use, intrinsic :: iso_c_binding
|
| integer(c_int64_t), value :: seed
|
| type(c_ptr) :: rng_ptr
|
|
|
| type(bob_rng_state), pointer :: rng
|
|
|
| allocate(rng)
|
| call rng%init(seed)
|
| rng_ptr = c_loc(rng)
|
| end function bob_rng_create
|
|
|
|
|
| subroutine bob_rng_destroy(rng_ptr) bind(C, name="bob_rng_destroy")
|
| use, intrinsic :: iso_c_binding
|
| type(c_ptr), value :: rng_ptr
|
| type(bob_rng_state), pointer :: rng
|
|
|
| if (.not. c_associated(rng_ptr)) return
|
|
|
| call c_f_pointer(rng_ptr, rng)
|
| deallocate(rng)
|
| end subroutine bob_rng_destroy
|
|
|
|
|
| function bob_rng_seed(rng_ptr, seed) result(status) bind(C, name="bob_rng_seed")
|
| use, intrinsic :: iso_c_binding
|
| type(c_ptr), value :: rng_ptr
|
| integer(c_int64_t), value :: seed
|
| integer(c_int) :: status
|
|
|
| type(bob_rng_state), pointer :: rng
|
|
|
| if (.not. c_associated(rng_ptr)) then
|
| status = BOB_ERROR_INVALID_ARGUMENT
|
| return
|
| end if
|
|
|
| call c_f_pointer(rng_ptr, rng)
|
| call rng%init(seed)
|
| status = BOB_SUCCESS
|
| end function bob_rng_seed
|
|
|
| end module bob_rng
|
|
|
|
|
|
|