From c82cf7c1f750e320f02017c56874a41d1c790e7d Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Fri, 8 Sep 2023 11:43:09 +0200 Subject: [PATCH 01/11] add partype method to lognormal and semicircle --- src/univariate/continuous/lognormal.jl | 1 + src/univariate/continuous/semicircle.jl | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/univariate/continuous/lognormal.jl b/src/univariate/continuous/lognormal.jl index 666b14248a..9f3675c1d7 100644 --- a/src/univariate/continuous/lognormal.jl +++ b/src/univariate/continuous/lognormal.jl @@ -62,6 +62,7 @@ stdlogx(d::LogNormal) = d.σ mean(d::LogNormal) = ((μ, σ) = params(d); exp(μ + σ^2/2)) median(d::LogNormal) = exp(d.μ) mode(d::LogNormal) = ((μ, σ) = params(d); exp(μ - σ^2)) +partype(d::LogNormal{T}) where {T<:Real} = T function var(d::LogNormal) (μ, σ) = params(d) diff --git a/src/univariate/continuous/semicircle.jl b/src/univariate/continuous/semicircle.jl index f04d4c9ed2..32529ca708 100644 --- a/src/univariate/continuous/semicircle.jl +++ b/src/univariate/continuous/semicircle.jl @@ -34,6 +34,8 @@ Semicircle(r::Integer; check_args::Bool=true) = Semicircle(float(r); check_args= @distr_support Semicircle -d.r +d.r params(d::Semicircle) = (d.r,) +partype(d::Semicircle{T}) where {T<:Real} = T + mean(d::Semicircle) = zero(d.r) var(d::Semicircle) = d.r^2 / 4 From 4a19bc79e9885fbe24467081391a303098ea6f1b Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Fri, 8 Sep 2023 13:48:12 +0200 Subject: [PATCH 02/11] Update src/univariate/continuous/lognormal.jl Co-authored-by: David Widmann --- src/univariate/continuous/lognormal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/univariate/continuous/lognormal.jl b/src/univariate/continuous/lognormal.jl index 9f3675c1d7..e5f29b4d7f 100644 --- a/src/univariate/continuous/lognormal.jl +++ b/src/univariate/continuous/lognormal.jl @@ -62,7 +62,7 @@ stdlogx(d::LogNormal) = d.σ mean(d::LogNormal) = ((μ, σ) = params(d); exp(μ + σ^2/2)) median(d::LogNormal) = exp(d.μ) mode(d::LogNormal) = ((μ, σ) = params(d); exp(μ - σ^2)) -partype(d::LogNormal{T}) where {T<:Real} = T +partype(::LogNormal{T}) where {T<:Real} = T function var(d::LogNormal) (μ, σ) = params(d) From 72725462310cf68091624daa4c21acbeb664029d Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Fri, 8 Sep 2023 13:48:22 +0200 Subject: [PATCH 03/11] Update src/univariate/continuous/semicircle.jl Co-authored-by: David Widmann --- src/univariate/continuous/semicircle.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/univariate/continuous/semicircle.jl b/src/univariate/continuous/semicircle.jl index 32529ca708..48fc1c9746 100644 --- a/src/univariate/continuous/semicircle.jl +++ b/src/univariate/continuous/semicircle.jl @@ -34,7 +34,7 @@ Semicircle(r::Integer; check_args::Bool=true) = Semicircle(float(r); check_args= @distr_support Semicircle -d.r +d.r params(d::Semicircle) = (d.r,) -partype(d::Semicircle{T}) where {T<:Real} = T +partype(::Semicircle{T}) where {T<:Real} = T mean(d::Semicircle) = zero(d.r) From 6ae492c3a4eba425c87b8ae3a427805e6fd20291 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Fri, 8 Sep 2023 15:48:49 +0200 Subject: [PATCH 04/11] add tests on par_type and Float32 --- .gitignore | 1 + src/univariate/discrete/discreteuniform.jl | 33 ++++++++++++++++------ src/univariate/discrete/hypergeometric.jl | 1 + test/univariates.jl | 15 ++++++++-- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 51e163194d..e2ac715357 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ docs/site .Rhistory Manifest.toml .vscode +tmp/ diff --git a/src/univariate/discrete/discreteuniform.jl b/src/univariate/discrete/discreteuniform.jl index 2638188287..28c8ffe584 100644 --- a/src/univariate/discrete/discreteuniform.jl +++ b/src/univariate/discrete/discreteuniform.jl @@ -21,21 +21,35 @@ External links * [Discrete uniform distribution on Wikipedia](http://en.wikipedia.org/wiki/Uniform_distribution_(discrete)) """ -struct DiscreteUniform <: DiscreteUnivariateDistribution - a::Int - b::Int +struct DiscreteUniform{I} <: DiscreteUnivariateDistribution where I + a::I + b::I pv::Float64 # individual probabilities - function DiscreteUniform(a::Real, b::Real; check_args::Bool=true) + function DiscreteUniform{I}(a::I, b::I; check_args::Bool=true) where I <: Integer @check_args DiscreteUniform (a <= b) - new(a, b, 1 / (b - a + 1)) + new{I}(a, b, one(I) / (b - a + one(I))) + end + function DiscreteUniform(a::Real, b::Real; kwargs...) + a_int = a isa Integer ? a : ceil(Int,a) + b_int = b isa Integer ? b : floor(Int, b) + aI, bI = promote(a_int,b_int) + I = typeof(aI) + DiscreteUniform{I}(aI,bI; kwargs...) end DiscreteUniform(b::Real; check_args::Bool=true) = DiscreteUniform(0, b; check_args=check_args) - DiscreteUniform() = new(0, 1, 0.5) + DiscreteUniform() = new{Int}(0, 1, 0.5) end @distr_support DiscreteUniform d.a d.b +partype(::DiscreteUniform{T}) where {T<:Integer} = T + +#### Conversions +convert(::Type{DiscreteUniform{T}}, a::S, b::S) where {T <: Integer, S <: Real} = DiscreteUniform(T(a), T(b)) +Base.convert(::Type{DiscreteUniform{T}}, d::DiscreteUniform) where {T<:Integer} = DiscreteUniform{T}(T(d.a), T(d.b)) +Base.convert(::Type{DiscreteUniform{T}}, d::DiscreteUniform{T}) where {T<:Integer} = d + ### Parameters span(d::DiscreteUniform) = d.b - d.a + 1 @@ -73,7 +87,7 @@ modes(d::DiscreteUniform) = [d.a:d.b] pdf(d::DiscreteUniform, x::Real) = insupport(d, x) ? d.pv : zero(d.pv) logpdf(d::DiscreteUniform, x::Real) = log(pdf(d, x)) -function cdf(d::DiscreteUniform, x::Int) +function cdf(d::DiscreteUniform, x::Integer) a = d.a result = (x - a + 1) * d.pv return if x < a @@ -85,7 +99,10 @@ function cdf(d::DiscreteUniform, x::Int) end end -quantile(d::DiscreteUniform, p::Real) = iszero(p) ? d.a : d.a - 1 + ceil(Int, p * span(d)) +function quantile(d::DiscreteUniform, p::Real) + T = partype(d) + iszero(p) ? d.a : d.a - one(T) + ceil(T, p * span(d)) +end function mgf(d::DiscreteUniform, t::Real) a, b = d.a, d.b diff --git a/src/univariate/discrete/hypergeometric.jl b/src/univariate/discrete/hypergeometric.jl index 9f72c2fea4..0ee560f2d5 100644 --- a/src/univariate/discrete/hypergeometric.jl +++ b/src/univariate/discrete/hypergeometric.jl @@ -38,6 +38,7 @@ end @distr_support Hypergeometric max(d.n - d.nf, 0) min(d.ns, d.n) +partype(::Hypergeometric) = Int ### Parameters diff --git a/test/univariates.jl b/test/univariates.jl index e16c52f59b..1aa2f3a836 100644 --- a/test/univariates.jl +++ b/test/univariates.jl @@ -62,6 +62,9 @@ function verify_and_test(D::Union{Type,Function}, d::UnivariateDistribution, dct # test various constructors for promotion, all-Integer args, etc. pars = params(d) + # verify parameter type + @test partype(d) === promote_type(typeof.(pars)...) + # promotion constructor: float_pars = map(x -> isa(x, AbstractFloat), pars) if length(pars) > 1 && sum(float_pars) > 1 && !isa(D, typeof(truncated)) @@ -75,10 +78,18 @@ function verify_and_test(D::Union{Type,Function}, d::UnivariateDistribution, dct # conversions if D isa Type && !isconcretetype(D) @test convert(D{partype(d)}, d) === d - d32 = convert(D{Float32}, d) - @test d32 isa D{Float32} + if D <: DiscreteUniform #|| D <: Hypergeometric + d32 = convert(D{Int32}, d) + @test d32 isa D{Int32} + @test partype(d32) == Int32 + else + d32 = convert(D{Float32}, d) + @test d32 isa D{Float32} + @test partype(d32) == Float32 + end end + # verify properties (params & stats) pdct = dct["properties"] for (fname, val) in pdct From ae09da6a32039c5e52e993bb515f3f8283e65f48 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 21 Sep 2023 18:41:53 +0200 Subject: [PATCH 05/11] adapt partype testing to special case of nothing parameters and fix warning on ambiguous global variable --- test/univariate/continuous/semicircle.jl | 2 +- test/univariates.jl | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/univariate/continuous/semicircle.jl b/test/univariate/continuous/semicircle.jl index 8dbf2ff2c4..2b5dfa57ed 100644 --- a/test/univariate/continuous/semicircle.jl +++ b/test/univariate/continuous/semicircle.jl @@ -41,7 +41,7 @@ d = Semicircle(2.0) rng = MersenneTwister(0) for r in rand(rng, Uniform(0,10), 5) - N = 10^4 + local N = 10^4 semi = Semicircle(r) sample = rand(rng, semi, N) mi, ma = extrema(sample) diff --git a/test/univariates.jl b/test/univariates.jl index 1aa2f3a836..0eaad8e846 100644 --- a/test/univariates.jl +++ b/test/univariates.jl @@ -63,7 +63,11 @@ function verify_and_test(D::Union{Type,Function}, d::UnivariateDistribution, dct pars = params(d) # verify parameter type - @test partype(d) === promote_type(typeof.(pars)...) + # truncated parameters may be nothing: Union{Nothing, promote_type()} + # but partype should still be type of the non-nothing ones + #@test partype(d) === promote_type(typeof.(pars)...) + @test partype(d) === promote_type(typeof.(pars)...) || + Union{Nothing,partype(d)} === promote_type(typeof.(pars)...) # promotion constructor: float_pars = map(x -> isa(x, AbstractFloat), pars) From b8511e9e53055422a50df9ccf57f829a1cf96a66 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Thu, 21 Sep 2023 18:42:42 +0200 Subject: [PATCH 06/11] breaking adapt tests of special cases: Int partype --- test/utils.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 5373fef30d..c7e2aab8b9 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -15,8 +15,14 @@ r = RealInterval(1.5, 4.0) # special cases @test partype(Kolmogorov()) == Float64 -@test partype(Hypergeometric(2, 2, 2)) == Float64 -@test partype(DiscreteUniform(0, 4)) == Float64 +# twutz: Hypergeometric needs integer parameters +#@test partype(Hypergeometric(2, 2, 2)) == Float64 +@test partype(Hypergeometric(2, 2, 2)) == Int +@test partype(Hypergeometric(2.0, 2, 2)) == Int +# twutz: should DiscreteUniform needs a partype of Int +#@test partype(DiscreteUniform(0, 4)) == Float64 +@test partype(DiscreteUniform(0, 4)) == Int +@test partype(DiscreteUniform(0.0, 4)) == Int A = rand(1:10, 5, 5) B = rand(Float32, 4) From f5237602a9aa027dcf719fc1bd910f74d2a10c17 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Fri, 22 Sep 2023 21:18:36 +0200 Subject: [PATCH 07/11] LocationScale: do not promote T but promote eltype T with eltype(inner) but also promote partype T with partype(inner) --- src/univariate/locationscale.jl | 15 ++++++++++----- test/univariate/locationscale.jl | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/univariate/locationscale.jl b/src/univariate/locationscale.jl index 97e47d6f74..56bd39d635 100644 --- a/src/univariate/locationscale.jl +++ b/src/univariate/locationscale.jl @@ -49,10 +49,15 @@ struct AffineDistribution{T<:Real, S<:ValueSupport, D<:UnivariateDistribution{S} end end -function AffineDistribution(μ::T, σ::T, ρ::UnivariateDistribution; check_args::Bool=true) where {T<:Real} + +function AffineDistribution(μ::T, σ::T, ρ::D; check_args::Bool=true) where {T<:Real,D<:UnivariateDistribution} @check_args AffineDistribution (σ, !iszero(σ)) - _T = promote_type(eltype(ρ), T) - return AffineDistribution{_T}(_T(μ), _T(σ), ρ) + # μ and σ act on both random numbers and parameter-like quantities like mean + # hence do not promote: but take care in eltype and partype + #_T = promote_type(eltype(ρ), T) + # _T = typeof(one(eltype(D))*one(T) + one(T)) + # return AffineDistribution{_T}(_T(μ), _T(σ), ρ) + return AffineDistribution{T}(μ, σ, ρ) end function AffineDistribution(μ::Real, σ::Real, ρ::UnivariateDistribution; check_args::Bool=true) @@ -71,7 +76,7 @@ end const ContinuousAffineDistribution{T<:Real,D<:ContinuousUnivariateDistribution} = AffineDistribution{T,Continuous,D} const DiscreteAffineDistribution{T<:Real,D<:DiscreteUnivariateDistribution} = AffineDistribution{T,Discrete,D} -Base.eltype(::Type{<:AffineDistribution{T}}) where T = T +Base.eltype(::Type{<:AffineDistribution{T,S,D}}) where {T,S,D} = promote_type(eltype(D), T) minimum(d::AffineDistribution) = d.σ > 0 ? d.μ + d.σ * minimum(d.ρ) : d.μ + d.σ * maximum(d.ρ) @@ -102,7 +107,7 @@ Base.convert(::Type{AffineDistribution{T}}, d::AffineDistribution{T}) where {T<: location(d::AffineDistribution) = d.μ scale(d::AffineDistribution) = d.σ params(d::AffineDistribution) = (d.μ,d.σ,d.ρ) -partype(::AffineDistribution{T}) where {T} = T +partype(d::AffineDistribution{T}) where {T} = promote_type(partype(d.ρ), T) #### Statistics diff --git a/test/univariate/locationscale.jl b/test/univariate/locationscale.jl index 761ce55bbf..6664b7c77b 100644 --- a/test/univariate/locationscale.jl +++ b/test/univariate/locationscale.jl @@ -117,7 +117,7 @@ function test_location_scale( rand!(rng, dtest, r) end @test mean(r) ≈ mean(dref) atol=0.02 - @test std(r) ≈ std(dref) atol=0.01 + @test std(r) ≈ std(dref) atol=0.02 @test cf(dtest, -0.1) ≈ cf(dref,-0.1) if dref isa ContinuousDistribution From 9219995f62f47dfa41635a5c083fe67505cc150d Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 25 Sep 2023 09:19:00 +0200 Subject: [PATCH 08/11] Update .gitignore Co-authored-by: David Widmann --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index e2ac715357..51e163194d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ docs/site .Rhistory Manifest.toml .vscode -tmp/ From 7392e9bde3eda7d09ed50a45051b069bb22c9ed3 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 25 Sep 2023 10:07:16 +0200 Subject: [PATCH 09/11] revert DiscreteUniform to non-parametric will be moved to its own pull-request --- src/univariate/discrete/discreteuniform.jl | 40 ++++++++-------------- test/univariates.jl | 12 ++----- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/src/univariate/discrete/discreteuniform.jl b/src/univariate/discrete/discreteuniform.jl index 28c8ffe584..567a860aa1 100644 --- a/src/univariate/discrete/discreteuniform.jl +++ b/src/univariate/discrete/discreteuniform.jl @@ -21,41 +21,30 @@ External links * [Discrete uniform distribution on Wikipedia](http://en.wikipedia.org/wiki/Uniform_distribution_(discrete)) """ -struct DiscreteUniform{I} <: DiscreteUnivariateDistribution where I - a::I - b::I +struct DiscreteUniform <: DiscreteUnivariateDistribution + a::Int + b::Int pv::Float64 # individual probabilities - function DiscreteUniform{I}(a::I, b::I; check_args::Bool=true) where I <: Integer + function DiscreteUniform(a::Real, b::Real; check_args::Bool=true) @check_args DiscreteUniform (a <= b) - new{I}(a, b, one(I) / (b - a + one(I))) - end - function DiscreteUniform(a::Real, b::Real; kwargs...) - a_int = a isa Integer ? a : ceil(Int,a) - b_int = b isa Integer ? b : floor(Int, b) - aI, bI = promote(a_int,b_int) - I = typeof(aI) - DiscreteUniform{I}(aI,bI; kwargs...) + new(a, b, 1 / (b - a + 1)) end DiscreteUniform(b::Real; check_args::Bool=true) = DiscreteUniform(0, b; check_args=check_args) - DiscreteUniform() = new{Int}(0, 1, 0.5) + DiscreteUniform() = new(0, 1, 0.5) end @distr_support DiscreteUniform d.a d.b -partype(::DiscreteUniform{T}) where {T<:Integer} = T - -#### Conversions -convert(::Type{DiscreteUniform{T}}, a::S, b::S) where {T <: Integer, S <: Real} = DiscreteUniform(T(a), T(b)) -Base.convert(::Type{DiscreteUniform{T}}, d::DiscreteUniform) where {T<:Integer} = DiscreteUniform{T}(T(d.a), T(d.b)) -Base.convert(::Type{DiscreteUniform{T}}, d::DiscreteUniform{T}) where {T<:Integer} = d - ### Parameters span(d::DiscreteUniform) = d.b - d.a + 1 probval(d::DiscreteUniform) = d.pv params(d::DiscreteUniform) = (d.a, d.b) +partype(::DiscreteUniform) = Int + + ### Show show(io::IO, d::DiscreteUniform) = show(io, d, (:a, :b)) @@ -87,7 +76,7 @@ modes(d::DiscreteUniform) = [d.a:d.b] pdf(d::DiscreteUniform, x::Real) = insupport(d, x) ? d.pv : zero(d.pv) logpdf(d::DiscreteUniform, x::Real) = log(pdf(d, x)) -function cdf(d::DiscreteUniform, x::Integer) +function cdf(d::DiscreteUniform, x::Int) a = d.a result = (x - a + 1) * d.pv return if x < a @@ -99,10 +88,7 @@ function cdf(d::DiscreteUniform, x::Integer) end end -function quantile(d::DiscreteUniform, p::Real) - T = partype(d) - iszero(p) ? d.a : d.a - one(T) + ceil(T, p * span(d)) -end +quantile(d::DiscreteUniform, p::Real) = iszero(p) ? d.a : d.a - 1 + ceil(Int, p * span(d)) function mgf(d::DiscreteUniform, t::Real) a, b = d.a, d.b @@ -131,3 +117,7 @@ function fit_mle(::Type{DiscreteUniform}, x::AbstractArray{<:Real}) end return DiscreteUniform(extrema(x)...) end + + + + diff --git a/test/univariates.jl b/test/univariates.jl index 0eaad8e846..2da63b24d5 100644 --- a/test/univariates.jl +++ b/test/univariates.jl @@ -82,18 +82,10 @@ function verify_and_test(D::Union{Type,Function}, d::UnivariateDistribution, dct # conversions if D isa Type && !isconcretetype(D) @test convert(D{partype(d)}, d) === d - if D <: DiscreteUniform #|| D <: Hypergeometric - d32 = convert(D{Int32}, d) - @test d32 isa D{Int32} - @test partype(d32) == Int32 - else - d32 = convert(D{Float32}, d) - @test d32 isa D{Float32} - @test partype(d32) == Float32 - end + d32 = convert(D{Float32}, d) + @test d32 isa D{Float32} end - # verify properties (params & stats) pdct = dct["properties"] for (fname, val) in pdct From 3021725ea03ee7f0d2dcb48cf646ea3b8723ad98 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 25 Sep 2023 10:08:59 +0200 Subject: [PATCH 10/11] generalize check of partype for non-Real parameters e.g. Nothing in Truncated distribution --- test/univariates.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/univariates.jl b/test/univariates.jl index 2da63b24d5..b994b747ba 100644 --- a/test/univariates.jl +++ b/test/univariates.jl @@ -64,11 +64,10 @@ function verify_and_test(D::Union{Type,Function}, d::UnivariateDistribution, dct # verify parameter type # truncated parameters may be nothing: Union{Nothing, promote_type()} - # but partype should still be type of the non-nothing ones - #@test partype(d) === promote_type(typeof.(pars)...) - @test partype(d) === promote_type(typeof.(pars)...) || - Union{Nothing,partype(d)} === promote_type(typeof.(pars)...) - + # in the following exclude all non-Real from creating the promoted type + @test partype(d) === mapfoldl( + typeof, (S, T) -> T <: Real ? promote_type(S, T) : S, pars; init = Bool) + # promotion constructor: float_pars = map(x -> isa(x, AbstractFloat), pars) if length(pars) > 1 && sum(float_pars) > 1 && !isa(D, typeof(truncated)) From b614da305eadc365bbadba25720ed35d5d6a9319 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Mon, 25 Sep 2023 10:54:55 +0200 Subject: [PATCH 11/11] Update src/univariate/locationscale.jl D is not used inside the function any more -> can simpify Co-authored-by: David Widmann --- src/univariate/locationscale.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/univariate/locationscale.jl b/src/univariate/locationscale.jl index 56bd39d635..8791510a64 100644 --- a/src/univariate/locationscale.jl +++ b/src/univariate/locationscale.jl @@ -50,7 +50,7 @@ struct AffineDistribution{T<:Real, S<:ValueSupport, D<:UnivariateDistribution{S} end -function AffineDistribution(μ::T, σ::T, ρ::D; check_args::Bool=true) where {T<:Real,D<:UnivariateDistribution} +function AffineDistribution(μ::T, σ::T, ρ::UnivariateDistribution; check_args::Bool=true) where {T<:Real} @check_args AffineDistribution (σ, !iszero(σ)) # μ and σ act on both random numbers and parameter-like quantities like mean # hence do not promote: but take care in eltype and partype