Skip to content

Commit 5644af6

Browse files
committed
Remove eltype definitions, add deprecation warning, and update documentation
1 parent 17154a2 commit 5644af6

25 files changed

+48
-96
lines changed

docs/src/extends.md

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ Whereas this package already provides a large collection of common distributions
44

55
Generally, you don't have to implement every API method listed in the documentation. This package provides a series of generic functions that turn a small number of internal methods into user-end API methods. What you need to do is to implement this small set of internal methods for your distributions.
66

7-
By default, `Discrete` sampleables have the support of type `Int` while `Continuous` sampleables have the support of type `Float64`. If this assumption does not hold for your new distribution or sampler, or its `ValueSupport` is neither `Discrete` nor `Continuous`, you should implement the `eltype` method in addition to the other methods listed below.
8-
97
**Note:** The methods that need to be implemented are different for distributions of different variate forms.
108

11-
129
## Create a Sampler
1310

1411
Unlike full-fledged distributions, a sampler, in general, only provides limited functionalities, mainly to support sampling.
@@ -18,60 +15,48 @@ Unlike full-fledged distributions, a sampler, in general, only provides limited
1815
To implement a univariate sampler, one can define a subtype (say `Spl`) of `Sampleable{Univariate,S}` (where `S` can be `Discrete` or `Continuous`), and provide a `rand` method, as
1916

2017
```julia
21-
function rand(rng::AbstractRNG, s::Spl)
18+
function Base.rand(rng::AbstractRNG, s::Spl)
2219
# ... generate a single sample from s
2320
end
2421
```
2522

26-
The package already implements a vectorized version of `rand!` and `rand` that repeatedly calls the scalar version to generate multiple samples; as wells as a one arg version that uses the default random number generator.
27-
28-
### Multivariate Sampler
23+
The package already implements vectorized versions `rand!(rng::AbstractRNG, s::Spl, dims::Int...)` and `rand(rng::AbstractRNG, s::Spl, dims::Int...)` that repeatedly call the scalar version to generate multiple samples.
24+
Additionally, the package implements versions of these functions without the `rng::AbstractRNG` argument that use the default random number generator.
2925

30-
To implement a multivariate sampler, one can define a subtype of `Sampleable{Multivariate,S}`, and provide both `length` and `_rand!` methods, as
26+
If there is a more efficient method to generate multiple samples, one should provide the following method
3127

3228
```julia
33-
Base.length(s::Spl) = ... # return the length of each sample
34-
35-
function _rand!(rng::AbstractRNG, s::Spl, x::AbstractVector{T}) where T<:Real
36-
# ... generate a single vector sample to x
29+
function Random.rand!(rng::AbstractRNG, s::Spl, x::AbstractArray{<:Real})
30+
# ... generate multiple samples from s in x
3731
end
3832
```
3933

40-
This function can assume that the dimension of `x` is correct, and doesn't need to perform dimension checking.
34+
### Multivariate Sampler
4135

42-
The package implements both `rand` and `rand!` as follows (which you don't need to implement in general):
36+
To implement a multivariate sampler, one can define a subtype of `Sampleable{Multivariate,S}`, and provide `length`, `rand`, and `rand!` methods, as
4337

4438
```julia
45-
function _rand!(rng::AbstractRNG, s::Sampleable{Multivariate}, A::DenseMatrix)
46-
for i = 1:size(A,2)
47-
_rand!(rng, s, view(A,:,i))
48-
end
49-
return A
50-
end
39+
Base.length(s::Spl) = ... # return the length of each sample
5140

52-
function rand!(rng::AbstractRNG, s::Sampleable{Multivariate}, A::AbstractVector)
53-
length(A) == length(s) ||
54-
throw(DimensionMismatch("Output size inconsistent with sample length."))
55-
_rand!(rng, s, A)
41+
function Base.rand(rng::AbstractRNG, s::Spl)
42+
# ... generate a single vector sample from s
5643
end
5744

58-
function rand!(rng::AbstractRNG, s::Sampleable{Multivariate}, A::DenseMatrix)
59-
size(A,1) == length(s) ||
60-
throw(DimensionMismatch("Output size inconsistent with sample length."))
61-
_rand!(rng, s, A)
45+
@inline function Random.rand!(rng::AbstractRNG, s::Spl, x::AbstractVector{<:Real})
46+
# `@inline` + `@boundscheck` allows users to skip bound checks by calling `@inbounds rand!(...)`
47+
# Ref https://docs.julialang.org/en/v1/devdocs/boundscheck/#Eliding-bounds-checks
48+
@boundscheck # ... check size (and possibly indices) of `x`
49+
# ... generate a single vector sample from s in x
6250
end
63-
64-
rand(rng::AbstractRNG, s::Sampleable{Multivariate,S}) where {S<:ValueSupport} =
65-
_rand!(rng, s, Vector{eltype(S)}(length(s)))
66-
67-
rand(rng::AbstractRNG, s::Sampleable{Multivariate,S}, n::Int) where {S<:ValueSupport} =
68-
_rand!(rng, s, Matrix{eltype(S)}(length(s), n))
6951
```
7052

7153
If there is a more efficient method to generate multiple vector samples in a batch, one should provide the following method
7254

7355
```julia
74-
function _rand!(rng::AbstractRNG, s::Spl, A::DenseMatrix{T}) where T<:Real
56+
@inline function Random.rand!(rng::AbstractRNG, s::Spl, A::AbstractMatrix{<:Real})
57+
# `@inline` + `@boundscheck` allows users to skip bound checks by calling `@inbounds rand!(...)`
58+
# Ref https://docs.julialang.org/en/v1/devdocs/boundscheck/#Eliding-bounds-checks
59+
@boundscheck # ... check size (and possibly indices) of `x`
7560
# ... generate multiple vector samples in batch
7661
end
7762
```
@@ -80,17 +65,22 @@ Remember that each *column* of A is a sample.
8065

8166
### Matrix-variate Sampler
8267

83-
To implement a multivariate sampler, one can define a subtype of `Sampleable{Multivariate,S}`, and provide both `size` and `_rand!` methods, as
68+
To implement a multivariate sampler, one can define a subtype of `Sampleable{Multivariate,S}`, and provide `size`, `rand`, and `rand!` methods, as
8469

8570
```julia
8671
Base.size(s::Spl) = ... # the size of each matrix sample
8772

88-
function _rand!(rng::AbstractRNG, s::Spl, x::DenseMatrix{T}) where T<:Real
89-
# ... generate a single matrix sample to x
73+
function Base.rand(rng::AbstractRNG, s::Spl)
74+
# ... generate a single matrix sample from s
9075
end
91-
```
9276

93-
Note that you can assume `x` has correct dimensions in `_rand!` and don't have to perform dimension checking, the generic `rand` and `rand!` will do dimension checking and array allocation for you.
77+
@inline function Random.rand!(rng::AbstractRNG, s::Spl, x::AbstractMatrix{<:Real})
78+
# `@inline` + `@boundscheck` allows users to skip bound checks by calling `@inbounds rand!(...)`
79+
# Ref https://docs.julialang.org/en/v1/devdocs/boundscheck/#Eliding-bounds-checks
80+
@boundscheck # ... check size (and possibly indices) of `x`
81+
# ... generate a single matrix sample from s in x
82+
end
83+
```
9484

9585
## Create a Distribution
9686

@@ -106,7 +96,7 @@ A univariate distribution type should be defined as a subtype of `DiscreteUnivar
10696

10797
The following methods need to be implemented for each univariate distribution type:
10898

109-
- [`rand(::AbstractRNG, d::UnivariateDistribution)`](@ref)
99+
- [`Base.rand(::AbstractRNG, d::UnivariateDistribution)`](@ref)
110100
- [`sampler(d::Distribution)`](@ref)
111101
- [`logpdf(d::UnivariateDistribution, x::Real)`](@ref)
112102
- [`cdf(d::UnivariateDistribution, x::Real)`](@ref)
@@ -138,8 +128,8 @@ The following methods need to be implemented for each multivariate distribution
138128

139129
- [`length(d::MultivariateDistribution)`](@ref)
140130
- [`sampler(d::Distribution)`](@ref)
141-
- [`eltype(d::Distribution)`](@ref)
142-
- [`Distributions._rand!(::AbstractRNG, d::MultivariateDistribution, x::AbstractArray)`](@ref)
131+
- [`Base.rand(::AbstractRNG, d::MultivariateDistribution)`](@ref)
132+
- [`Random.rand!(::AbstractRNG, d::MultivariateDistribution, x::AbstractVector{<:Real})`](@ref)
143133
- [`Distributions._logpdf(d::MultivariateDistribution, x::AbstractArray)`](@ref)
144134

145135
Note that if there exist faster methods for batch evaluation, one should override `_logpdf!` and `_pdf!`.
@@ -161,6 +151,7 @@ A matrix-variate distribution type should be defined as a subtype of `DiscreteMa
161151
The following methods need to be implemented for each matrix-variate distribution type:
162152

163153
- [`size(d::MatrixDistribution)`](@ref)
164-
- [`Distributions._rand!(rng::AbstractRNG, d::MatrixDistribution, A::AbstractMatrix)`](@ref)
154+
- [`Base.rand(rng::AbstractRNG, d::MatrixDistribution)`](@ref)
155+
- [`Random.rand!(rng::AbstractRNG, d::MatrixDistribution, A::AbstractMatrix{<:Real})`](@ref)
165156
- [`sampler(d::MatrixDistribution)`](@ref)
166157
- [`Distributions._logpdf(d::MatrixDistribution, x::AbstractArray)`](@ref)

docs/src/multivariate.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ The methods listed below are implemented for each multivariate distribution, whi
1818
```@docs
1919
length(::MultivariateDistribution)
2020
size(::MultivariateDistribution)
21-
eltype(::Type{MultivariateDistribution})
2221
mean(::MultivariateDistribution)
2322
var(::MultivariateDistribution)
2423
cov(::MultivariateDistribution)

docs/src/types.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ The basic functionalities that a sampleable object provides are to *retrieve inf
5757
length(::Sampleable)
5858
size(::Sampleable)
5959
nsamples(::Type{Sampleable}, ::Any)
60-
eltype(::Type{Sampleable})
6160
rand(::AbstractRNG, ::Sampleable)
6261
rand!(::AbstractRNG, ::Sampleable, ::AbstractArray)
6362
```

src/censored.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,6 @@ function partype(d::Censored{<:UnivariateDistribution,<:ValueSupport,T}) where {
112112
return promote_type(partype(d.uncensored), T)
113113
end
114114

115-
Base.eltype(::Type{<:Censored{D,S,T}}) where {D,S,T} = promote_type(T, eltype(D))
116-
117115
#### Range and Support
118116

119117
isupperbounded(d::LeftCensored) = isupperbounded(d.uncensored)

src/cholesky/lkjcholesky.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ end
8282
# Properties
8383
# -----------------------------------------------------------------------------
8484

85-
Base.eltype(::Type{LKJCholesky{T}}) where {T} = T
86-
8785
function Base.size(d::LKJCholesky)
8886
p = d.d
8987
return (p, p)

src/common.jl

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,21 @@ Base.size(s::Sampleable{Univariate}) = ()
9494
Base.size(s::Sampleable{Multivariate}) = (length(s),)
9595

9696
"""
97-
eltype(::Type{Sampleable})
97+
eltype(::Type{S}) where {S<:Distributions.Sampleable}
98+
99+
The default element type of a sample from a sampler of type `S`.
100+
101+
This is the type of elements of the samples generated by the `rand` method.
102+
However, one can provide an array of different element types to store the samples using `rand!`.
103+
104+
!!! warn
105+
This method is deprecated and will be removed in an upcoming breaking release.
98106
99-
The default element type of a sample. This is the type of elements of the samples generated
100-
by the `rand` method. However, one can provide an array of different element types to
101-
store the samples using `rand!`.
102107
"""
103-
Base.eltype(::Type{<:Sampleable{F,Discrete}}) where {F} = Int
104-
Base.eltype(::Type{<:Sampleable{F,Continuous}}) where {F} = Float64
108+
function Base.eltype(::Type{S}) where {S<:Sampleable}
109+
Base.depwarn("`eltype(::Type{<:Distributions.Sampleable})` is deprecated and will be removed", :eltype)
110+
return Base.promote_op(eltype rand, S)
111+
end
105112

106113
"""
107114
nsamples(s::Sampleable)

src/multivariate/dirichlet.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ end
5050

5151
length(d::DirichletCanon) = length(d.alpha)
5252

53-
Base.eltype(::Type{<:Dirichlet{T}}) where {T} = T
54-
5553
#### Conversions
5654
convert(::Type{Dirichlet{T}}, cf::DirichletCanon) where {T<:Real} =
5755
Dirichlet(convert(AbstractVector{T}, cf.alpha))

src/multivariate/jointorderstatistics.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ maximum(d::JointOrderStatistics) = Fill(maximum(d.dist), length(d))
8888

8989
params(d::JointOrderStatistics) = tuple(params(d.dist)..., d.n, d.ranks)
9090
partype(d::JointOrderStatistics) = partype(d.dist)
91-
Base.eltype(::Type{<:JointOrderStatistics{D}}) where {D} = Base.eltype(D)
92-
Base.eltype(d::JointOrderStatistics) = eltype(d.dist)
9391

9492
function logpdf(d::JointOrderStatistics, x::AbstractVector{<:Real})
9593
n = d.n

src/multivariate/mvlogitnormal.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ canonform(d::MvLogitNormal{<:MvNormal}) = MvLogitNormal(canonform(d.normal))
5252
# Properties
5353

5454
length(d::MvLogitNormal) = length(d.normal) + 1
55-
Base.eltype(::Type{<:MvLogitNormal{D}}) where {D} = eltype(D)
56-
Base.eltype(d::MvLogitNormal) = eltype(d.normal)
5755
params(d::MvLogitNormal) = params(d.normal)
5856
@inline partype(d::MvLogitNormal) = partype(d.normal)
5957

src/multivariate/mvlognormal.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,6 @@ MvLogNormal(μ::AbstractVector,s::Real) = MvLogNormal(MvNormal(μ,s))
176176
MvLogNormal::AbstractVector) = MvLogNormal(MvNormal(σ))
177177
MvLogNormal(d::Int,s::Real) = MvLogNormal(MvNormal(d,s))
178178

179-
Base.eltype(::Type{<:MvLogNormal{T}}) where {T} = T
180-
181179
### Conversion
182180
function convert(::Type{MvLogNormal{T}}, d::MvLogNormal) where T<:Real
183181
MvLogNormal(convert(MvNormal{T}, d.normal))

0 commit comments

Comments
 (0)