|
1 | | -import Random: AbstractRNG |
2 | | - |
3 | | -# No info |
4 | 1 | """ |
5 | | - Flat <: ContinuousUnivariateDistribution |
| 2 | + Flat() |
| 3 | +
|
| 4 | +The *flat distribution* is the improper distribution of real numbers that has the improper |
| 5 | +probability density function |
6 | 6 |
|
7 | | -A distribution with support and density of one |
8 | | -everywhere. |
| 7 | +```math |
| 8 | +f(x) = 1. |
| 9 | +``` |
9 | 10 | """ |
10 | 11 | struct Flat <: ContinuousUnivariateDistribution end |
11 | 12 |
|
12 | | -Distributions.rand(rng::AbstractRNG, d::Flat) = rand(rng) |
13 | | -Distributions.logpdf(d::Flat, x::Real) = zero(x) |
14 | | -Distributions.minimum(d::Flat) = -Inf |
15 | | -Distributions.maximum(d::Flat) = +Inf |
| 13 | +Base.minimum(::Flat) = -Inf |
| 14 | +Base.maximum(::Flat) = Inf |
| 15 | + |
| 16 | +Base.rand(rng::Random.AbstractRNG, d::Flat) = rand(rng) |
| 17 | +Distributions.logpdf(::Flat, x::Real) = zero(x) |
| 18 | + |
| 19 | +# TODO: only implement `logpdf(d, ::Real)` if support for Distributions < 0.24 is dropped |
| 20 | +Distributions.pdf(d::Flat, x::Real) = exp(logpdf(d, x)) |
16 | 21 |
|
17 | 22 | # For vec support |
18 | | -Distributions.logpdf(d::Flat, x::AbstractVector{<:Real}) = zero(x) |
| 23 | +Distributions.logpdf(::Flat, x::AbstractVector{<:Real}) = zero(x) |
| 24 | +Distributions.loglikelihood(::Flat, x::AbstractVector{<:Real}) = zero(eltype(x)) |
19 | 25 |
|
20 | 26 | """ |
21 | 27 | FlatPos(l::Real) |
22 | 28 |
|
23 | | -A distribution with a lower bound of `l` and a density |
24 | | -of one at every `x` above `l`. |
| 29 | +The *positive flat distribution* with real-valued parameter `l` is the improper distribution |
| 30 | +of real numbers that has the improper probability density function |
| 31 | +
|
| 32 | +```math |
| 33 | +f(x) = \\begin{cases} |
| 34 | +0 & \\text{if } x \\leq l, \\\\ |
| 35 | +1 & \\text{otherwise}. |
| 36 | +\\end{cases} |
| 37 | +``` |
25 | 38 | """ |
26 | 39 | struct FlatPos{T<:Real} <: ContinuousUnivariateDistribution |
27 | 40 | l::T |
28 | 41 | end |
29 | 42 |
|
30 | | -Distributions.rand(rng::AbstractRNG, d::FlatPos) = rand(rng) + d.l |
31 | | -Distributions.logpdf(d::FlatPos, x::Real) = x <= d.l ? -Inf : zero(x) |
32 | | -Distributions.minimum(d::FlatPos) = d.l |
33 | | -Distributions.maximum(d::FlatPos) = Inf |
| 43 | +Base.minimum(d::FlatPos) = d.l |
| 44 | +Base.maximum(d::FlatPos) = Inf |
| 45 | + |
| 46 | +Base.rand(rng::Random.AbstractRNG, d::FlatPos) = rand(rng) + d.l |
| 47 | +function Distributions.logpdf(d::FlatPos, x::Real) |
| 48 | + z = float(zero(x)) |
| 49 | + return x <= d.l ? oftype(z, -Inf) : z |
| 50 | +end |
| 51 | + |
| 52 | +# TODO: only implement `logpdf(d, ::Real)` if support for Distributions < 0.24 is dropped |
| 53 | +Distributions.pdf(d::FlatPos, x::Real) = exp(logpdf(d, x)) |
34 | 54 |
|
35 | 55 | # For vec support |
36 | | -function Distributions.logpdf(d::FlatPos, x::AbstractVector{<:Real}) |
37 | | - return any(x .<= d.l) ? -Inf : zero(x) |
| 56 | +function Distributions.loglikelihood(d::FlatPos, x::AbstractVector{<:Real}) |
| 57 | + lower = d.l |
| 58 | + T = float(eltype(x)) |
| 59 | + return any(xi <= lower for xi in x) ? T(-Inf) : zero(T) |
38 | 60 | end |
39 | 61 |
|
40 | 62 | """ |
41 | | - BinomialLogit(n<:Real, I<:Integer) |
| 63 | + BinomialLogit(n, logitp) |
| 64 | +
|
| 65 | +The *Binomial distribution* with logit parameterization characterizes the number of |
| 66 | +successes in a sequence of independent trials. |
| 67 | +
|
| 68 | +It has two parameters: `n`, the number of trials, and `logitp`, the logit of the probability |
| 69 | +of success in an individual trial, with the distribution |
| 70 | +
|
| 71 | +```math |
| 72 | +P(X = k) = {n \\choose k}{(\\text{logistic}(logitp))}^k (1 - \\text{logistic}(logitp))^{n-k}, \\quad \\text{ for } k = 0,1,2, \\ldots, n. |
| 73 | +``` |
42 | 74 |
|
43 | | -A univariate binomial logit distribution. |
| 75 | +See also: [`Binomial`](@ref) |
44 | 76 | """ |
45 | | -struct BinomialLogit{T<:Real, I<:Integer} <: DiscreteUnivariateDistribution |
46 | | - n::I |
| 77 | +struct BinomialLogit{T<:Real,S<:Real} <: DiscreteUnivariateDistribution |
| 78 | + n::Int |
47 | 79 | logitp::T |
48 | | -end |
| 80 | + logconstant::S |
49 | 81 |
|
50 | | -function logpdf_binomial_logit(n, logitp, k) |
51 | | - logcomb = -StatsFuns.log1p(n) - SpecialFunctions.logbeta(n - k + 1, k + 1) |
52 | | - return logcomb + k * logitp - n * StatsFuns.log1pexp(logitp) |
| 82 | + function BinomialLogit{T}(n::Int, logitp::T) where T |
| 83 | + n >= 0 || error("parameter `n` has to be non-negative") |
| 84 | + logconstant = - (log1p(n) + n * StatsFuns.log1pexp(logitp)) |
| 85 | + return new{T,typeof(logconstant)}(n, logitp, logconstant) |
| 86 | + end |
53 | 87 | end |
54 | 88 |
|
55 | | -function Distributions.logpdf(d::BinomialLogit{<:Real}, k::Int) |
56 | | - return logpdf_binomial_logit(d.n, d.logitp, k) |
| 89 | +BinomialLogit(n::Int, logitp::Real) = BinomialLogit{typeof(logitp)}(n, logitp) |
| 90 | + |
| 91 | +Base.minimum(::BinomialLogit) = 0 |
| 92 | +Base.maximum(d::BinomialLogit) = d.n |
| 93 | + |
| 94 | +# TODO: only implement `logpdf(d, k::Real)` if support for Distributions < 0.24 is dropped |
| 95 | +Distributions.pdf(d::BinomialLogit, k::Real) = exp(logpdf(d, k)) |
| 96 | +Distributions.logpdf(d::BinomialLogit, k::Real) = _logpdf(d, k) |
| 97 | +Distributions.logpdf(d::BinomialLogit, k::Integer) = _logpdf(d, k) |
| 98 | + |
| 99 | +function _logpdf(d::BinomialLogit, k::Real) |
| 100 | + n, logitp, logconstant = d.n, d.logitp, d.logconstant |
| 101 | + _insupport = insupport(d, k) |
| 102 | + _k = _insupport ? round(Int, k) : 0 |
| 103 | + result = logconstant + _k * logitp - SpecialFunctions.logbeta(n - _k + 1, _k + 1) |
| 104 | + return _insupport ? result : oftype(result, -Inf) |
57 | 105 | end |
58 | 106 |
|
59 | | -function Distributions.pdf(d::BinomialLogit{<:Real}, k::Int) |
60 | | - return exp(logpdf_binomial_logit(d.n, d.logitp, k)) |
| 107 | +function Base.rand(rng::Random.AbstractRNG, d::BinomialLogit) |
| 108 | + return rand(rng, Binomial(d.n, logistic(d.logitp))) |
61 | 109 | end |
| 110 | +Distributions.sampler(d::BinomialLogit) = sampler(Binomial(d.n, logistic(d.logitp))) |
62 | 111 |
|
63 | 112 | """ |
64 | | - BernoulliLogit(p<:Real) |
| 113 | + BernoulliLogit(logitp::Real) |
65 | 114 |
|
66 | | -A univariate logit-parameterised Bernoulli distribution. |
| 115 | +Create a univariate logit-parameterised Bernoulli distribution. |
67 | 116 | """ |
68 | | -function BernoulliLogit(logitp::Real) |
69 | | - return BinomialLogit(1, logitp) |
70 | | -end |
| 117 | +BernoulliLogit(logitp::Real) = BinomialLogit(1, logitp) |
71 | 118 |
|
72 | 119 | """ |
73 | | - OrderedLogistic(η::Any, cutpoints<:AbstractVector) |
74 | | -
|
75 | | -An ordered logistic distribution. |
| 120 | + OrderedLogistic(η, c::AbstractVector) |
| 121 | +
|
| 122 | +The *ordered logistic distribution* with real-valued parameter `η` and cutpoints `c` has the |
| 123 | +probability mass function |
| 124 | +
|
| 125 | +```math |
| 126 | +P(X = k) = \\begin{cases} |
| 127 | + 1 - \\text{logistic}(\\eta - c_1) & \\text{if } k = 1, \\\\ |
| 128 | + \\text{logistic}(\\eta - c_{k-1}) - \\text{logistic}(\\eta - c_k) & \\text{if } 1 < k < K, \\\\ |
| 129 | + \\text{logistic}(\\eta - c_{K-1}) & \\text{if } k = K, |
| 130 | +\\end{cases} |
| 131 | +``` |
| 132 | +where `K = length(c) + 1`. |
76 | 133 | """ |
77 | 134 | struct OrderedLogistic{T1, T2<:AbstractVector} <: DiscreteUnivariateDistribution |
78 | | - η::T1 |
79 | | - cutpoints::T2 |
| 135 | + η::T1 |
| 136 | + cutpoints::T2 |
80 | 137 |
|
81 | | - function OrderedLogistic(η, cutpoints) |
82 | | - if !issorted(cutpoints) |
83 | | - error("cutpoints are not sorted") |
84 | | - end |
| 138 | + function OrderedLogistic{T1,T2}(η::T1, cutpoints::T2) where {T1,T2} |
| 139 | + issorted(cutpoints) || error("cutpoints are not sorted") |
85 | 140 | return new{typeof(η), typeof(cutpoints)}(η, cutpoints) |
86 | | - end |
87 | | - |
88 | | -end |
89 | | - |
90 | | -function Distributions.logpdf(d::OrderedLogistic, k::Int) |
91 | | - K = length(d.cutpoints)+1 |
92 | | - c = d.cutpoints |
93 | | - |
94 | | - if k==1 |
95 | | - logp= -softplus(-(c[k]-d.η)) #logp= log(logistic(c[k]-d.η)) |
96 | | - elseif k<K |
97 | | - logp= log(logistic(c[k]-d.η) - logistic(c[k-1]-d.η)) |
98 | | - else |
99 | | - logp= -softplus(c[k-1]-d.η) #logp= log(1-logistic(c[k-1]-d.η)) |
100 | 141 | end |
| 142 | +end |
101 | 143 |
|
102 | | - return logp |
| 144 | +function OrderedLogistic(η, cutpoints::AbstractVector) |
| 145 | + return OrderedLogistic{typeof(η),typeof(cutpoints)}(η, cutpoints) |
103 | 146 | end |
104 | 147 |
|
105 | | -Distributions.pdf(d::OrderedLogistic, k::Int) = exp(logpdf(d,k)) |
| 148 | +Base.minimum(d::OrderedLogistic) = 0 |
| 149 | +Base.maximum(d::OrderedLogistic) = length(d.cutpoints) + 1 |
106 | 150 |
|
107 | | -function Distributions.rand(rng::AbstractRNG, d::OrderedLogistic) |
108 | | - cutpoints = d.cutpoints |
109 | | - η = d.η |
| 151 | +# TODO: only implement `logpdf(d, k::Real)` if support for Distributions < 0.24 is dropped |
| 152 | +Distributions.pdf(d::OrderedLogistic, k::Real) = exp(logpdf(d, k)) |
| 153 | +Distributions.logpdf(d::OrderedLogistic, k::Real) = _logpdf(d, k) |
| 154 | +Distributions.logpdf(d::OrderedLogistic, k::Integer) = _logpdf(d, k) |
110 | 155 |
|
111 | | - K = length(cutpoints)+1 |
112 | | - c = vcat(-Inf, cutpoints, Inf) |
| 156 | +function _logpdf(d::OrderedLogistic, k::Real) |
| 157 | + η, cutpoints = d.η, d.cutpoints |
| 158 | + K = length(cutpoints) + 1 |
113 | 159 |
|
114 | | - ps = [logistic(η - i[1]) - logistic(η - i[2]) for i in zip(c[1:(end-1)],c[2:end])] |
| 160 | + _insupport = insupport(d, k) |
| 161 | + _k = _insupport ? round(Int, k) : 1 |
| 162 | + logp = unsafe_logpdf_ordered_logistic(η, cutpoints, K, _k) |
115 | 163 |
|
| 164 | + return _insupport ? logp : oftype(logp, -Inf) |
| 165 | +end |
| 166 | + |
| 167 | +function Base.rand(rng::Random.AbstractRNG, d::OrderedLogistic) |
| 168 | + η, cutpoints = d.η, d.cutpoints |
| 169 | + K = length(cutpoints) + 1 |
| 170 | + # evaluate probability mass function |
| 171 | + ps = map(1:K) do k |
| 172 | + exp(unsafe_logpdf_ordered_logistic(η, cutpoints, K, k)) |
| 173 | + end |
116 | 174 | k = rand(rng, Categorical(ps)) |
| 175 | + return k |
| 176 | +end |
| 177 | +function Distributions.sampler(d::OrderedLogistic) |
| 178 | + η, cutpoints = d.η, d.cutpoints |
| 179 | + K = length(cutpoints) + 1 |
| 180 | + # evaluate probability mass function |
| 181 | + ps = map(1:K) do k |
| 182 | + exp(unsafe_logpdf_ordered_logistic(η, cutpoints, K, k)) |
| 183 | + end |
| 184 | + return sampler(Categorical(ps)) |
| 185 | +end |
117 | 186 |
|
118 | | - if all(x -> x > zero(x), ps) |
119 | | - return(k) |
120 | | - else |
121 | | - return(-Inf) |
| 187 | +# unsafe version without bounds checking |
| 188 | +function unsafe_logpdf_ordered_logistic(η, cutpoints, K, k::Int) |
| 189 | + @inbounds begin |
| 190 | + logp = if k == 1 |
| 191 | + -StatsFuns.log1pexp(η - cutpoints[k]) |
| 192 | + elseif k < K |
| 193 | + tmp = StatsFuns.log1pexp(cutpoints[k-1] - η) |
| 194 | + -tmp + StatsFuns.log1mexp(tmp - StatsFuns.log1pexp(cutpoints[k] - η)) |
| 195 | + else |
| 196 | + -StatsFuns.log1pexp(cutpoints[k-1] - η) |
| 197 | + end |
122 | 198 | end |
| 199 | + return logp |
123 | 200 | end |
124 | 201 |
|
125 | 202 | """ |
126 | | -Numerically stable Poisson log likelihood. |
127 | | -* `logλ`: log of rate parameter |
| 203 | + LogPoisson(logλ) |
| 204 | +
|
| 205 | +The *Poisson distribution* with logarithmic parameterization of the rate parameter |
| 206 | +descibes the number of independent events occurring within a unit time interval, given the |
| 207 | +average rate of occurrence ``exp(logλ)``. |
| 208 | +
|
| 209 | +The distribution has the probability mass function |
| 210 | +
|
| 211 | +```math |
| 212 | +P(X = k) = \\frac{e^{k \\cdot logλ}{k!} e^{-e^{logλ}}, \\quad \\text{ for } k = 0,1,2,\\ldots. |
| 213 | +``` |
| 214 | +
|
| 215 | +See also: [`Poisson`](@ref) |
128 | 216 | """ |
129 | | -struct LogPoisson{T<:Real} <: DiscreteUnivariateDistribution |
| 217 | +struct LogPoisson{T<:Real,S} <: DiscreteUnivariateDistribution |
130 | 218 | logλ::T |
| 219 | + λ::S |
| 220 | + |
| 221 | + function LogPoisson{T}(logλ::T) where T |
| 222 | + λ = exp(logλ) |
| 223 | + return new{T,typeof(λ)}(logλ, λ) |
| 224 | + end |
131 | 225 | end |
132 | 226 |
|
133 | | -function Distributions.logpdf(lp::LogPoisson, k::Int) |
134 | | - return k * lp.logλ - exp(lp.logλ) - SpecialFunctions.loggamma(k + 1) |
| 227 | +LogPoisson(logλ::Real) = LogPoisson{typeof(logλ)}(logλ) |
| 228 | + |
| 229 | +Base.minimum(d::LogPoisson) = 0 |
| 230 | +Base.maximum(d::LogPoisson) = Inf |
| 231 | + |
| 232 | +function _logpdf(d::LogPoisson, k::Real) |
| 233 | + _insupport = insupport(d, k) |
| 234 | + _k = _insupport ? round(Int, k) : 0 |
| 235 | + logp = _k * d.logλ - d.λ - SpecialFunctions.loggamma(_k + 1) |
| 236 | + |
| 237 | + return _insupport ? logp : oftype(logp, -Inf) |
135 | 238 | end |
136 | 239 |
|
| 240 | +# TODO: only implement `logpdf(d, ::Real)` if support for Distributions < 0.24 is dropped |
| 241 | +Distributions.pdf(d::LogPoisson, k::Real) = exp(logpdf(d, k)) |
| 242 | +Distributions.logpdf(d::LogPoisson, k::Integer) = _logpdf(d, k) |
| 243 | +Distributions.logpdf(d::LogPoisson, k::Real) = _logpdf(d, k) |
| 244 | + |
| 245 | +Base.rand(rng::Random.AbstractRNG, d::LogPoisson) = rand(rng, Poisson(d.λ)) |
| 246 | +Distributions.sampler(d::LogPoisson) = sampler(Poisson(d.λ)) |
| 247 | + |
137 | 248 | Bijectors.logpdf_with_trans(d::NoDist{<:Univariate}, ::Real, ::Bool) = 0 |
138 | 249 | Bijectors.logpdf_with_trans(d::NoDist{<:Multivariate}, ::AbstractVector{<:Real}, ::Bool) = 0 |
139 | 250 | function Bijectors.logpdf_with_trans(d::NoDist{<:Multivariate}, x::AbstractMatrix{<:Real}, ::Bool) |
|
0 commit comments