Skip to content

Commit a021af1

Browse files
authored
Merge pull request org-arl#33 from org-arl/patch-1
fix: rand! for AlphaSubGaussian
2 parents 45fe871 + 63c98a8 commit a021af1

File tree

2 files changed

+61
-50
lines changed

2 files changed

+61
-50
lines changed

src/AlphaStableDistributions.jl

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Base.@kwdef struct AlphaStable{T} <: Distributions.ContinuousUnivariateDistribut
1414
location::T = zero(α)
1515
end
1616

17+
AlphaStable::Integer, β::Integer, scale::Integer, location::Integer) = AlphaStable(float(α), float(β), float(scale), float(location))
18+
1719

1820
# sampler(d::AlphaStable) = error("Not implemented")
1921
# pdf(d::AlphaStable, x::Real) = error("Not implemented")
@@ -186,7 +188,7 @@ skewness parameter, scale parameter (dispersion^1/α) and location parameter res
186188
187189
α, β, c and δ are computed based on McCulloch (1986) fractile.
188190
"""
189-
function Distributions.fit(::Type{<:AlphaStable}, x, alg=QuickSort)
191+
function Distributions.fit(::Type{<:AlphaStable}, x::AbstractArray{T}, alg=QuickSort) where {T<:AbstractFloat}
190192
sx = sort(x, alg=alg)
191193
p = quantile.(Ref(sx), (0.05, 0.25, 0.28, 0.5, 0.72, 0.75, 0.95), sorted=true)
192194
να = (p[7]-p[1]) / (p[6]-p[2])
@@ -210,7 +212,7 @@ function Distributions.fit(::Type{<:AlphaStable}, x, alg=QuickSort)
210212
else
211213
δ = ζ - β * c * tan*α/2)
212214
end
213-
return AlphaStable=α, β=β, scale=c, location=oftype(α, δ))
215+
return AlphaStable=T(α), β=T(β), scale=T(c), location=T(δ))
214216
end
215217

216218
Base.@kwdef struct SymmetricAlphaStable{T} <: Distributions.ContinuousUnivariateDistribution
@@ -233,12 +235,12 @@ returns `SymmetricAlphaStable`
233235
scale is computed based on Fama & Roll (1971) fractile.
234236
location is the 50% trimmed mean of the sample.
235237
"""
236-
function Distributions.fit(::Type{<:SymmetricAlphaStable}, x, alg=QuickSort)
238+
function Distributions.fit(::Type{<:SymmetricAlphaStable}, x::AbstractArray{T}, alg=QuickSort) where {T<:AbstractFloat}
237239
sx = sort(x, alg=alg)
238240
δ = mean(@view(sx[end÷4:(3*end)÷4]))
239241
p = quantile.(Ref(sx), (0.05, 0.25, 0.28, 0.72, 0.75, 0.95), sorted=true)
240-
c = (p[4]-p[3])/1.654
241-
an = (p[6]-p[1])/(p[5]-p[2])
242+
c = (p[4]-p[3]) / 1.654
243+
an = (p[6]-p[1]) / (p[5]-p[2])
242244
if an < 2.4388
243245
α = 2.
244246
else
@@ -251,7 +253,7 @@ function Distributions.fit(::Type{<:SymmetricAlphaStable}, x, alg=QuickSort)
251253
if α < 0.5
252254
α = 0.5
253255
end
254-
return SymmetricAlphaStable=α, scale=c, location=oftype(α, δ))
256+
return SymmetricAlphaStable=T(α), scale=T(c), location=T(δ))
255257
end
256258

257259
"""
@@ -267,31 +269,33 @@ This implementation is based on the method in J.M. Chambers, C.L. Mallows
267269
and B.W. Stuck, "A Method for Simulating Stable Random Variables," JASA 71 (1976): 340-4.
268270
McCulloch's MATLAB implementation (1996) served as a reference in developing this code.
269271
"""
270-
function Base.rand(rng::AbstractRNG, d::AlphaStable)
272+
function Base.rand(rng::AbstractRNG, d::AlphaStable{T}) where {T<:AbstractFloat}
271273
α=d.α; β=d.β; scale=d.scale; loc=d.location
272274
< 0.1 || α > 2) && throw(DomainError(α, "α must be in the range 0.1 to 2"))
273275
abs(β) > 1 && throw(DomainError(β, "β must be in the range -1 to 1"))
274-
ϕ = (rand(rng) - 0.5) * π
275-
if α == 1 && β == 0
276-
return loc + scale*tan(ϕ)
276+
ϕ = (rand(rng, T) - 0.5) * π
277+
if α == one(T) && β == zero(T)
278+
return loc + scale * tan(ϕ)
277279
end
278-
w = -log(rand(rng))
280+
w = -log(rand(rng, T))
279281
α == 2 && (return loc + 2*scale*sqrt(w)*sin(ϕ))
280-
β == 0 && (return loc + scale * ((cos((1-α)*ϕ) / w)^(1.0/α - 1) * sin* ϕ) / cos(ϕ)^(1.0/α)))
282+
β == zero(T) && (return loc + scale * ((cos((1-α)*ϕ) / w)^(one(T)/α - one(T)) * sin* ϕ) / cos(ϕ)^(one(T)/α)))
281283
cosϕ = cos(ϕ)
282-
if abs-1) > 1e-8
283-
ζ = β * tan*α/2)
284+
if abs - one(T)) > 1e-8
285+
ζ = β * tan * α / 2)
284286
= α * ϕ
285-
a1ϕ = (1-α) * ϕ
286-
return loc + scale * (( (sin(aϕ)+ζ*cos(aϕ))/cosϕ * ((cos(a1ϕ)+ζ*sin(a1ϕ))) / ((w*cosϕ)^((1-α)/α)) ))
287+
a1ϕ = (one(T) - α) * ϕ
288+
return loc + scale * (( (sin(aϕ) + ζ * cos(aϕ))/cosϕ * ((cos(a1ϕ) + ζ*sin(a1ϕ))) / ((w*cosϕ)^((1-α)/α)) ))
287289
end
288290
= π/2 + β*ϕ
289-
x = 2/π * (bϕ*tan(ϕ) - β*log/2*w*cosϕ/bϕ))
290-
α == 1 || (x += β * tan*α/2))
291+
x = 2/π * (bϕ * tan(ϕ) - β * log/2*w*cosϕ/bϕ))
292+
α == one(T) || (x += β * tan*α/2))
291293

292-
return loc + scale*x
294+
return loc + scale * x
293295
end
294296

297+
Base.eltype(::Type{<:AlphaStable{T}}) where {T<:AbstractFloat} = T
298+
295299

296300
"""
297301
@@ -318,7 +322,7 @@ The maximum acceptable size of `R` is `10x10`
318322
julia> x = rand(AlphaSubGaussian(n=1000))
319323
```
320324
"""
321-
Base.@kwdef struct AlphaSubGaussian{T,M<:AbstractMatrix} <: Distributions.ContinuousUnivariateDistribution
325+
Base.@kwdef struct AlphaSubGaussian{T<:AbstractFloat,M<:AbstractMatrix} <: Distributions.ContinuousUnivariateDistribution
322326
α::T = 1.50
323327
R::M = SMatrix{5,5}(collect(SymmetricToeplitz([1.0000, 0.5804, 0.2140, 0.1444, -0.0135])))
324328
n::Int
@@ -366,7 +370,7 @@ function subgausscondprobtabulate(α, x1, x2_ind, invRx1, invR, vjoint, nmin, nm
366370
end
367371

368372

369-
function Random.rand!(rng::AbstractRNG, d::AlphaSubGaussian, x::AbstractArray)
373+
function Random.rand!(rng::AbstractRNG, d::AlphaSubGaussian{T}, x::AbstractArray{T}) where {T<:AbstractFloat}
370374
α=d.α; R=d.R; n=d.n
371375
length(x) >= n || throw(ArgumentError("length of x must be at least n"))
372376
α 1.10:0.01:1.98 || throw(DomainError(α, "α must lie within `1.10:0.01:1.98`"))
@@ -388,11 +392,11 @@ function Random.rand!(rng::AbstractRNG, d::AlphaSubGaussian, x::AbstractArray)
388392
nmax, nmin, res, rind, vjoint = matdict["Nmax"]::Float64, matdict["Nmin"]::Float64, matdict["res"]::Float64, vec(matdict["rind"])::Vector{Float64}, matdict["vJoint"]::Matrix{Float64}
389393
step = (log10(nmax)-log10(nmin))/res
390394
m>size(vjoint, 1)-1 && throw(DomainError(R, "The dimensions of `R` exceed the maximum possible 10x10"))
391-
A = rand(AlphaStable/2, 1.0, 2*cos*α/4)^(2.0/α), 0.0))
392-
T = rand(Chisq(m))
395+
A = rand(AlphaStable(T(α/2), one(T), T(2*cos*α/4)^(2.0/α)), zero(T)))
396+
CT = rand(Chisq(m))
393397
S = randn(m)
394398
S = S/sqrt(sum(abs2,S))
395-
xtmp = ((sigrootx1*sqrt(A*T))*S)'
399+
xtmp = ((sigrootx1*sqrt(A*CT))*S)'
396400
if n<=m
397401
copyto!(x, @view(xtmp[1:n]))
398402
else
@@ -421,7 +425,8 @@ function Random.rand!(rng::AbstractRNG, d::AlphaSubGaussian, x::AbstractArray)
421425
end
422426

423427

424-
Base.rand(rng::AbstractRNG, d::AlphaSubGaussian) = rand!(rng, d, zeros(d.n))
428+
Base.rand(rng::AbstractRNG, d::AlphaSubGaussian) = rand!(rng, d, zeros(eltype(d), d.n))
429+
Base.eltype(::Type{<:AlphaSubGaussian}) = Float64
425430

426431
"""
427432
fit(d::Type{<:AlphaSubGaussian}, x, m; p=1.0)

test/runtests.jl

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,43 @@ using Test, Random, Distributions
33

44
@testset "AlphaStableDistributions.jl" begin
55

6+
sampletypes = [Float32,Float64]
67
stabletypes = [AlphaStable,SymmetricAlphaStable]
78
αs = [0.6:0.1:2,1:0.1:2]
8-
for (i, stabletype) in enumerate(stabletypes)
9-
for α in αs[i]
10-
d1 = AlphaStable=α)
11-
s = rand(d1, 100000)
9+
for sampletype sampletypes
10+
for (i, stabletype) in enumerate(stabletypes)
11+
for α in αs[i]
12+
d1 = AlphaStable=sampletype(α))
13+
s = rand(d1, 200000)
14+
@test eltype(s) == sampletype
1215

13-
d2 = fit(stabletype, s)
16+
d2 = fit(stabletype, s)
17+
@test typeof(d2.α) == sampletype
1418

15-
@test d1.α d2.α rtol=0.1
16-
stabletype != SymmetricAlphaStable && @test d1.β d2.β atol=0.2
17-
@test d1.scale d2.scale rtol=0.1
18-
@test d1.location d2.location atol=0.1
19-
end
19+
@test d1.α d2.α rtol=0.1
20+
stabletype != SymmetricAlphaStable && @test d1.β d2.β atol=0.2
21+
@test d1.scale d2.scale rtol=0.1
22+
@test d1.location d2.location atol=0.1
23+
end
2024

21-
xnormal = rand(Normal(3.0, 4.0), 96000)
22-
d = fit(stabletype, xnormal)
23-
@test d.α 2 rtol=0.2
24-
stabletype != SymmetricAlphaStable && @test d.β 0 atol=0.2
25-
@test d.scale 4/√2 rtol=0.2
26-
@test d.location 3 rtol=0.1
25+
xnormal = rand(Normal(3.0, 4.0), 96000)
26+
d = fit(stabletype, xnormal)
27+
@test d.α 2 rtol=0.2
28+
stabletype != SymmetricAlphaStable && @test d.β 0 atol=0.2
29+
@test d.scale 4/√2 rtol=0.2
30+
@test d.location 3 rtol=0.1
2731

28-
xcauchy = rand(Cauchy(3.0, 4.0), 96000)
29-
d = fit(stabletype, xcauchy)
30-
@test d.α 1 rtol=0.2
31-
stabletype != SymmetricAlphaStable && @test d.β 0 atol=0.2
32-
@test d.scale 4 rtol=0.2
33-
@test d.location 3 rtol=0.1
32+
xcauchy = rand(Cauchy(3.0, 4.0), 96000)
33+
d = fit(stabletype, xcauchy)
34+
@test d.α 1 rtol=0.2
35+
stabletype != SymmetricAlphaStable && @test d.β 0 atol=0.2
36+
@test d.scale 4 rtol=0.2
37+
@test d.location 3 rtol=0.1
38+
end
3439
end
3540

3641
for α in 1.1:0.1:1.9
37-
d = AlphaSubGaussian(n=96000, α=α)
42+
d = AlphaSubGaussian(α=α, n=96000)
3843
x = rand(d)
3944
x2 = copy(x)
4045
rand!(d, x2)
@@ -47,14 +52,15 @@ using Test, Random, Distributions
4752
@test d3.location 0 atol=0.1
4853
end
4954

50-
d4 = AlphaSubGaussian(n=96000)
51-
m = size(d4.R, 1)-1
55+
d4 = AlphaSubGaussian(α=1.5, n=96000)
56+
m = size(d4.R, 1) - 1
5257
x = rand(d4)
5358
d5 = fit(AlphaSubGaussian, x, m, p=1.0)
5459
@test d4.α d5.α rtol=0.1
5560
@test d4.R d5.R rtol=0.1
5661

5762
end
63+
5864
# 362.499 ms (4620903 allocations: 227.64 MiB)
5965
# 346.520 ms (4621052 allocations: 209.62 MiB) # StaticArrays in outer fun
6066
# 345.925 ms (4225524 allocations: 167.66 MiB) # tempind to tuple

0 commit comments

Comments
 (0)