Skip to content

Commit cfae382

Browse files
committed
Make BilinearExponential into a Distribution
1 parent f0dc9b4 commit cfae382

File tree

5 files changed

+66
-26
lines changed

5 files changed

+66
-26
lines changed

src/StratMetropolis.jl

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -446,19 +446,6 @@
446446
end
447447

448448
adjust!(ages::AbstractVector, chronometer, systematic::Nothing) = ages
449-
function adjust!(ages::AbstractVector{BilinearExponential{T}}, chronometer, systematic::SystematicUncertainty) where T
450-
systUPb = randn()*systematic.UPb
451-
systArAr = randn()*systematic.ArAr
452-
@assert eachindex(ages)==eachindex(chronometer)
453-
@inbounds for i eachindex(ages)
454-
age = ages[i]
455-
μ = age.μ
456-
chronometer[i] === :UPb &&+= systUPb)
457-
chronometer[i] === :ArAr &&+= systArAr)
458-
ages[i] = BilinearExponential{T}(age.A, μ, age.σ, age.shp, age.skw)
459-
end
460-
return ages
461-
end
462449
function adjust!(ages::AbstractVector, chronometer, systematic::SystematicUncertainty) where T
463450
systUPb = randn()*systematic.UPb
464451
systArAr = randn()*systematic.ArAr

src/Utilities.jl

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,52 @@
11
## --- Fitting non-Gaussian distributions
22

3-
struct BilinearExponential{T}
3+
struct BilinearExponential{T<:Real} <: ContinuousUnivariateDistribution
44
A::T
55
μ::T
66
σ::T
7-
sharpness::T
8-
skew::T
7+
shp::T
8+
skw::T
99
end
10-
function BilinearExponential(p::AbstractVector)
10+
function BilinearExponential(p::AbstractVector{T}) where {T}
1111
@assert length(p) == 5
1212
@assert all(x->!(x<0), Iterators.drop(p,1))
13-
BilinearExponential(p...)
13+
BilinearExponential{T}(p...)
14+
end
15+
function BilinearExponential(p::NTuple{5,T}) where {T}
16+
@assert all(x->!(x<0), Iterators.drop(p,1))
17+
BilinearExponential{T}(p...)
18+
end
19+
20+
## Conversions
21+
Base.convert(::Type{BilinearExponential{T}}, d::Normal) where {T<:Real} = BilinearExponential{T}(T(d.A), T(d.μ), T(d.σ), T(d.shp), T(d.skw))
22+
Base.convert(::Type{BilinearExponential{T}}, d::Normal{T}) where {T<:Real} = d
23+
24+
## Parameters
25+
Distributions.params(d::BilinearExponential) = (d.A, d.μ, d.σ, d.shp, d.skw)
26+
@inline Distributions.partype(d::BilinearExponential{T}) where {T<:Real} = T
27+
28+
Distributions.location(d::BilinearExponential) = d.μ
29+
Distributions.scale(d::BilinearExponential) = d.σ
30+
31+
Base.eltype(::Type{BilinearExponential{T}}) where {T} = T
32+
33+
## Evaluation
34+
function Distributions.pdf(d::BilinearExponential, x::Real)
35+
xs = (x - d.μ)/d.σ # X scaled by mean and variance
36+
v = 1/2 - atan(xs)/3.141592653589793 # Sigmoid (positive on LHS)
37+
return exp(d.A + d.shp*d.skw*xs*v - d.shp/d.skw*xs*(1-v))
1438
end
1539

40+
function Distributions.logpdf(d::BilinearExponential, x::Real)
41+
xs = (x - d.μ)/d.σ # X scaled by mean and variance
42+
v = 1/2 - atan(xs)/3.141592653589793 # Sigmoid (positive on LHS)
43+
return d.A + d.shp*d.skw*xs*v - d.shp/d.skw*xs*(1-v)
44+
end
45+
46+
## Affine transformations
47+
Base.:+(d::BilinearExponential{T}, c::Real) where {T} = BilinearExponential{T}(d.A, d.μ + c, d.σ, d.shp, d.skw)
48+
Base.:*(d::BilinearExponential{T}, c::Real) where {T} = BilinearExponential{T}(d.A, d.μ * c, d.σ * abs(c), d.shp, d.skw)
49+
1650
"""
1751
```Julia
1852
bilinear_exponential(x::Number, p::AbstractVector)
@@ -95,7 +129,7 @@
95129
skw = abs(p[5,i])
96130
ll += shp*skw*xs*v - shp/skw*xs*(1-v)
97131
end
98-
return ll
132+
return lles
99133
end
100134
function bilinear_exponential_ll(x::AbstractVector, ages::AbstractVector{BilinearExponential{T}}) where T
101135
ll = zero(T)
@@ -104,7 +138,7 @@
104138
isnan(pᵢ.μ) && continue
105139
xs = (x[i]-pᵢ.μ)/(pᵢ.σ) # X scaled by mean and variance
106140
v = 1/2 - atan(xs)/3.141592653589793 # Sigmoid (positive on LHS)
107-
shp, skw = pᵢ.sharpness, pᵢ.skew
141+
shp, skw = pᵢ.shp, pᵢ.skw
108142
ll += shp*skw*xs*v - shp/skw*xs*(1-v)
109143
end
110144
return ll
@@ -146,12 +180,12 @@
146180
## --- Biquadratic distribution (like bilinear, with quadratic sides)
147181

148182

149-
struct BiquadraticExponential{T}
183+
struct BiquadraticExponential{T<:Real}
150184
A::T
151185
μ::T
152186
σ::T
153-
sharpness::T
154-
skew::T
187+
shp::T
188+
skw::T
155189
end
156190
function BiquadraticExponential(p::AbstractVector)
157191
@assert length(p) == 5
@@ -191,8 +225,8 @@
191225
pᵢ = ages[i]
192226
xs = (x[i]-pᵢ.μ)/(pᵢ.σ) # X scaled by mean and variance
193227
v = 1/2 - atan(xs)/3.141592653589793 # Sigmoid (positive on LHS)
194-
shp = pᵢ.sharpness
195-
skw = pᵢ.skew
228+
shp = pᵢ.shp
229+
skw = pᵢ.skw
196230
ll -= (shp*skw*xs*v - shp/skw*xs*(1-v))^2
197231
end
198232
return ll

test/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[deps]
2+
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
23
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
34
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Chron
2-
using Test, Statistics
2+
using Test, Statistics, Distributions
33

44
const make_plots = get(ENV, "MAKE_PLOTS", false) == "true"
55
@testset "Utilities" begin include("testUtilities.jl") end

test/testUtilities.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,29 @@
44
@test bilinear_exponential([66.02, 66.08, 66.15], [-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505]) [0.00010243952455548608, 1.0372066408907184e-13, 1.0569878258022094e-28]
55
@test bilinear_exponential_ll(66.02, [-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505]) -3.251727600957773
66

7+
d = Chron.BilinearExponential([-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505])
8+
@test d isa Chron.BilinearExponential{Float64}
9+
@test pdf(d, 66.02) 0.00010243952455548608
10+
@test logpdf(d, 66.02) (-3.251727600957773 -5.9345103368858085)
11+
12+
@test eltype(d) === partype(d) === Float64
13+
@test params(d) == (-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505)
14+
@test d + 1 isa Chron.BilinearExponential{Float64}
15+
@test location(d + 1) == 66.00606672179812 + 1
16+
@test d * 2 isa Chron.BilinearExponential{Float64}
17+
@test location(d * 2) == 66.00606672179812 * 2
18+
@test scale(d * 2) == 0.17739474265630253 * 2
19+
720
## --- Alternatively: biquadratic exponential
821

922
@test Chron.biquadratic_exponential(66.02, [-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505]) 1.3384991282229901e-5
1023
@test Chron.biquadratic_exponential([66.02, 66.08, 66.15], [-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505]) [1.3384991282229901e-5, 5.441899612287734e-128, 0.0]
1124
@test Chron.biquadratic_exponential_ll(66.02, [-5.9345103368858085, 66.00606672179812, 0.17739474265630253, 70.57882331291309, 0.6017142541555505]) -10.573732390830594
1225

1326

27+
## --- Other utility functions for log likelihoods
28+
29+
@test Chron.strat_ll([0.0, 0.0], [Normal(0,1), Normal(0,1)]) 0
30+
@test Chron.strat_ll([0.0, 0.5], [Normal(0,1), Uniform(0,1)]) -0.9189385332046728
31+
1432
## ---

0 commit comments

Comments
 (0)