stdlib_stats_distribution_gamma.fypp Source File


Source Code

#:include "common.fypp"
#:set GAMMA_REAL_KINDS_TYPES = [item for item in REAL_KINDS_TYPES if item[0] in ('sp', 'dp', 'xdp')]
#:set GAMMA_CMPLX_KINDS_TYPES = [item for item in CMPLX_KINDS_TYPES if item[0] in ('sp', 'dp', 'xdp')]
#:set RC_KINDS_TYPES = GAMMA_REAL_KINDS_TYPES + GAMMA_CMPLX_KINDS_TYPES
Module stdlib_stats_distribution_gamma    
    use ieee_arithmetic, only: ieee_value, ieee_quiet_nan, ieee_is_nan
    use stdlib_kinds, only : sp, dp, xdp
    use stdlib_error, only : error_stop
    use stdlib_optval, only : optval
    use stdlib_stats_distribution_uniform, only : uni=>rvs_uniform
    use stdlib_stats_distribution_normal, only : rnor=>rvs_normal
    use stdlib_specialfunctions_gamma, only : regamma_p=>regularized_gamma_p

    implicit none
    intrinsic :: log_gamma
    private

    public :: rvs_gamma
    public :: pdf_gamma
    public :: cdf_gamma


    interface rvs_gamma
    !! Version experimental
    !!
    !! Gamma Distribution Random Variates
    !! ([Specification](../page/specs/stdlib_stats_distribution_gamma.html#
    !! rvs_gamma-gamma-distribution-random-variates))
    !!
        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_rvs_1_${t1[0]}$${k1}$     ! 1 argument
        #:endfor

        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_rvs_${t1[0]}$${k1}$       ! 2 arguments
        #:endfor

        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_rvs_array_${t1[0]}$${k1}$ ! 3 arguments
        #:endfor
    end interface rvs_gamma


    interface pdf_gamma
    !! Version experimental
    !!
    !! Gamma Distribution Probability Density Function
    !! ([Specification](../page/specs/stdlib_stats_distribution_gamma.html#
    !! pdf_gamma-gamma-distribution-probability-density-function))
    !!
        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_pdf_${t1[0]}$${k1}$
        #:endfor

        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_pdf_impure_${t1[0]}$${k1}$
        #:endfor
    end interface pdf_gamma


    interface cdf_gamma
    !! Version experimental
    !!
    !! Gamma Distribution Cumulative Distribution Function
    !! ([Specification](../page/specs/stdlib_stats_distribution_gamma.html#
    !! cdf_gamma-gamma-distribution-cumulative-distribution-function))
    !!
        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_cdf_${t1[0]}$${k1}$
        #:endfor

        #:for k1, t1 in RC_KINDS_TYPES
        module procedure gamma_dist_cdf_impure_${t1[0]}$${k1}$
        #:endfor
    end interface cdf_gamma




contains

    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    impure elemental function gamma_dist_rvs_1_${t1[0]}$${k1}$(shape) result(res)
    ! Gamma distribution random variate. "A Simple Method for Generating Gamma
    ! Variables", G. Marsaglia & W. W. Tsang, ACM Transactions on Mathematical
    ! Software, 26(3), 2000, p. 363
    !
        ${t1}$, intent(in) :: shape
        ${t1}$ :: res
        ${t1}$ :: x, v, u, zz, d, c
        ${t1}$, parameter :: sq = 0.0331_${k1}$

        if(shape <= 0.0_${k1}$) then
            res = ieee_value(1.0_${k1}$, ieee_quiet_nan)
            return
        end if

        zz = shape

        if(zz < 1._${k1}$) zz = 1._${k1}$ + zz
        !shift shape parameter > 1
        d = zz - 1._${k1}$ / 3._${k1}$
        c = 1._${k1}$ / (3._${k1}$ * sqrt(d))

        do
            do
                x = rnor(0.0_${k1}$, 1.0_${k1}$)
                v = 1._${k1}$ + c * x
                v = v * v * v

                if(v > 0._${k1}$) exit

            end do

            x = x * x
            u = uni(1.0_${k1}$)

            if(u < (1._${k1}$ - sq * x * x)) exit

            if(log(u) < 0.5_${k1}$ * x + d * (1._${k1}$ - v + log(v))) exit

        end do

        res = d * v

        if(shape < 1._${k1}$) then
        !restore shape parameter < 1
            u = uni(1.0_${k1}$)
            res = res * u ** (1._${k1}$ / shape)

        endif
    end function gamma_dist_rvs_1_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    impure elemental function gamma_dist_rvs_1_${t1[0]}$${k1}$(shape) result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are
    ! independent of each other.
    !
        ${t1}$, intent(in) :: shape
        ${t1}$ :: res

        res = cmplx(gamma_dist_rvs_1_r${k1}$(shape%re),                        &
                    gamma_dist_rvs_1_r${k1}$(shape%im), kind=${k1}$)
    end function gamma_dist_rvs_1_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    impure elemental function gamma_dist_rvs_${t1[0]}$${k1}$(shape, rate, loc)      &
        result(res)
    !
        ${t1}$, intent(in) :: shape, rate
        ${t1}$, intent(in), optional :: loc
        ${t1}$ :: res
        ${t1}$ :: loc_

        loc_ = optval(loc, 0.0_${k1}$)

        if(rate <= 0.0_${k1}$) call error_stop("Error(gamma_dist_rvs): Gamma" &
        //" distribution rate parameter must be greater than zero")

        res = gamma_dist_rvs_1_${t1[0]}$${k1}$(shape) / rate + loc_
    end function gamma_dist_rvs_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    impure elemental function gamma_dist_rvs_${t1[0]}$${k1}$(shape, rate, loc)      &
        result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are           &
    ! independent of each other.
    !
        ${t1}$, intent(in) :: shape, rate
        ${t1}$, intent(in), optional :: loc
        ${t1}$ :: res
        ${t1}$ :: loc_

        loc_ = optval(loc, cmplx(0.0_${k1}$, 0.0_${k1}$, kind=${k1}$))

        res = cmplx(gamma_dist_rvs_r${k1}$(shape%re, rate%re) + loc_%re,                 &
                    gamma_dist_rvs_r${k1}$(shape%im, rate%im) + loc_%im, kind=${k1}$)
    end function gamma_dist_rvs_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    function gamma_dist_rvs_array_${t1[0]}$${k1}$(shape, rate, array_size, loc)     &
        result(res)
    !
        ${t1}$, intent(in) :: shape, rate
        integer, intent(in) :: array_size
        ${t1}$, intent(in), optional :: loc
        ${t1}$ :: res(array_size)
        integer :: i
        ${t1}$ :: loc_

        loc_ = optval(loc, 0.0_${k1}$)

        do i = 1, array_size

            res(i) = gamma_dist_rvs_${t1[0]}$${k1}$(shape, rate, loc_)

        end do
    end function gamma_dist_rvs_array_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    function gamma_dist_rvs_array_${t1[0]}$${k1}$(shape, rate, array_size, loc)     &
        result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are           &
    ! independent of each other.
    !
        ${t1}$, intent(in) :: shape, rate
        integer, intent(in) :: array_size
        ${t1}$, intent(in), optional :: loc
        ${t1}$ :: res(array_size)
        integer :: i
        ${t1}$ :: loc_

        loc_ = optval(loc, cmplx(0.0_${k1}$, 0.0_${k1}$, kind=${k1}$))

        do i = 1, array_size

            res(i) = gamma_dist_rvs_${t1[0]}$${k1}$(shape, rate, loc_)

        end do
    end function gamma_dist_rvs_array_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    elemental function gamma_dist_pdf_${t1[0]}$${k1}$(x, shape, rate, loc)     &
        result(res)
    ! Gamma distribution probability density function
    !
        ${t1}$, intent(in) :: x, shape, rate
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res
        ${t1}$ :: xs
        ${t1}$ :: loc_

        loc_ = optval(loc, 0.0_${k1}$)

        xs = x - loc_

        if(rate <= 0.0_${k1}$ .or. shape <= 0.0_${k1}$) then
            res = ieee_value(1.0_${k1}$, ieee_quiet_nan)
            return
        end if

        if(xs == 0.0_${k1}$) then

            if(shape == 1.0_${k1}$) then

                res = rate

            else if(shape > 1.0_${k1}$) then

                res = 0.0_${k1}$

            else

                res = huge(res)

            endif

        else if(xs < 0.0_${k1}$) then

            res = 0.0_${k1}$

        else

            res = exp((shape - 1._${k1}$) * log(xs) - xs * rate + shape *      &
                log(rate) - log_gamma(shape))

        endif
    end function gamma_dist_pdf_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    impure elemental function gamma_dist_pdf_impure_${t1[0]}$${k1}$(x, shape, rate, err, loc) &
        result(res)
    ! Gamma distribution probability density function (impure wrapper)
    !
        ${t1}$, intent(in) :: x, shape, rate
        integer, intent(out) :: err
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res

        res = gamma_dist_pdf_${t1[0]}$${k1}$(x, shape, rate, loc)
        err = 0
        if(ieee_is_nan(res)) err = 1
    end function gamma_dist_pdf_impure_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    elemental function gamma_dist_pdf_${t1[0]}$${k1}$(x, shape, rate, loc)     &
        result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are &
    ! independent of each other.
    !
        ${t1}$, intent(in) :: x, shape, rate
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res
        ${t1}$ :: loc_

        loc_ = optval(loc, cmplx(0.0_${k1}$, 0.0_${k1}$, kind=${k1}$))

        res = gamma_dist_pdf_r${k1}$(x%re, shape%re, rate%re, loc_%re)
        res = res * gamma_dist_pdf_r${k1}$(x%im, shape%im, rate%im, loc_%im)
    end function gamma_dist_pdf_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    impure elemental function gamma_dist_pdf_impure_${t1[0]}$${k1}$(x, shape, rate, err, loc) &
        result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are           &
    ! independent of each other.
    !
        ${t1}$, intent(in) :: x, shape, rate
        integer, intent(out) :: err
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res
        ${t1}$ :: loc_

        loc_ = optval(loc, cmplx(0.0_${k1}$, 0.0_${k1}$, kind=${k1}$))

        res = gamma_dist_pdf_r${k1}$(x%re, shape%re, rate%re, loc_%re)
        res = res * gamma_dist_pdf_r${k1}$(x%im, shape%im, rate%im, loc_%im)
        err = 0
        if(ieee_is_nan(res)) err = 1
    end function gamma_dist_pdf_impure_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    impure elemental function gamma_dist_cdf_${t1[0]}$${k1}$(x, shape, rate, loc)   &
        result(res)
    ! Gamma distribution cumulative distribution function
    !
        ${t1}$, intent(in) :: x, shape, rate
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res, xs
        ${t1}$ :: loc_

        loc_ = optval(loc, 0.0_${k1}$)

        if(rate <= 0.0_${k1}$ .or. shape <= 0.0_${k1}$) then
            res = ieee_value(1.0_${k1}$, ieee_quiet_nan)
            return
        end if

        xs = x - loc_
        if(xs <= 0.0_${k1}$) then
            res = 0.0_${k1}$
            return
        end if

        res = real(regamma_p(shape, xs * rate), kind=${k1}$)
    end function gamma_dist_cdf_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_REAL_KINDS_TYPES
    impure elemental function gamma_dist_cdf_impure_${t1[0]}$${k1}$(x, shape, rate, err, loc) &
        result(res)
    ! Gamma distribution cumulative distribution function (impure wrapper)
    !
        ${t1}$, intent(in) :: x, shape, rate
        integer, intent(out) :: err
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res

        res = gamma_dist_cdf_${t1[0]}$${k1}$(x, shape, rate, loc)
        err = 0
        if(ieee_is_nan(res)) err = 1
    end function gamma_dist_cdf_impure_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    impure elemental function gamma_dist_cdf_${t1[0]}$${k1}$(x, shape, rate, loc)   &
        result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are           &
    ! independent of each other.
    !
        ${t1}$, intent(in) :: x, shape, rate
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res
        ${t1}$ :: loc_

        loc_ = optval(loc, cmplx(0.0_${k1}$, 0.0_${k1}$, kind=${k1}$))

        res = gamma_dist_cdf_r${k1}$(x%re, shape%re, rate%re, loc_%re)

        res = res * gamma_dist_cdf_r${k1}$(x%im, shape%im, rate%im, loc_%im)
    end function gamma_dist_cdf_${t1[0]}$${k1}$

    #:endfor


    #:for k1, t1 in GAMMA_CMPLX_KINDS_TYPES
    impure elemental function gamma_dist_cdf_impure_${t1[0]}$${k1}$(x, shape, rate, err, loc) &
        result(res)
    ! Complex parameter gamma distributed. The real part and imaginary part are           &
    ! independent of each other.
    !
        ${t1}$, intent(in) :: x, shape, rate
        integer, intent(out) :: err
        ${t1}$, intent(in), optional :: loc
        real(${k1}$) :: res
        ${t1}$ :: loc_

        loc_ = optval(loc, cmplx(0.0_${k1}$, 0.0_${k1}$, kind=${k1}$))

        res = gamma_dist_cdf_${t1[0]}$${k1}$(x, shape, rate, loc_)
        err = 0
        if(ieee_is_nan(res)) err = 1
    end function gamma_dist_cdf_impure_${t1[0]}$${k1}$

    #:endfor


end module stdlib_stats_distribution_gamma