Skip to content

Commit 4ee85b8

Browse files
Add BetaFourParameters (#121)
1 parent 4a45fb2 commit 4ee85b8

File tree

7 files changed

+106
-9
lines changed

7 files changed

+106
-9
lines changed

docs/src/manual.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ this is handled internally.
9595
| Distribution | Identity scaling | Inverse and inverse square root scalings |
9696
| :------- |:---------------:|:--------------------:|
9797
|[`Beta`](@ref)|||
98+
|[`BetaFourParameters`](@ref)|| x |
9899
|[`Exponential`](@ref)|||
99100
|[`Gamma`](@ref)|||
100101
|[`LogitNormal`](@ref)|||
@@ -109,6 +110,7 @@ this is handled internally.
109110

110111
```@docs
111112
ScoreDrivenModels.Beta
113+
ScoreDrivenModels.BetaFourParameters
112114
ScoreDrivenModels.Exponential
113115
ScoreDrivenModels.Gamma
114116
ScoreDrivenModels.LogitNormal

src/MLE.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,15 @@ end
155155
function fit!(gas::Model{D, T}, y::Vector{T};
156156
initial_params::Matrix{T} = DEFAULT_INITIAL_PARAM,
157157
opt_method::AbstractOptimizationMethod = NelderMead(gas, DEFAULT_NUM_SEEDS),
158-
verbose::Int = DEFAULT_VERBOSE) where {D, T}
158+
verbose::Int = DEFAULT_VERBOSE,
159+
throw_errors::Bool = false) where {D, T}
159160

160161
unknowns = find_unknowns(gas)
161162
# Check if the model has no unknowns
162163
n_unknowns = length(unknowns)
163164
check_model_estimated(n_unknowns) && return gas
164165

165-
f = fit(gas, y; initial_params = initial_params, opt_method = opt_method, verbose = verbose)
166+
f = fit(gas, y; initial_params = initial_params, opt_method = opt_method, verbose = verbose, throw_errors = throw_errors)
166167
fill_psitilde!(gas, f.coefs, unknowns)
167168
return f
168169
end

src/ScoreDrivenModels.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ include("opt_methods/NelderMead.jl")
3232
include("distributions/non_native_dists.jl")
3333
include("distributions/common_interface.jl")
3434
include("distributions/beta.jl")
35+
include("distributions/betafour.jl")
3536
include("distributions/exponential.jl")
3637
include("distributions/gamma.jl")
3738
include("distributions/logitnormal.jl")

src/distributions/betafour.jl

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""
2+
BetaFourParameters
3+
* Parametrization a, c, \\alpha, \\beta
4+
* Score
5+
* Fisher Information
6+
* `time_varying_params` map.
7+
* Default link
8+
9+
Right now when estimating a BetaFourParameters model is recomended to provide fixed parameters a and c, dont
10+
with the code:
11+
12+
```julia
13+
gas_beta_4 = Model([1, 2, 11, 12], [1, 2, 11, 12], BetaFourParameters, 0.0; time_varying_params=[3])
14+
gas_beta_4.ω[1] = minimum(y) - 0.1*std(y) # parameter a
15+
gas_beta_4.ω[2] = maximum(y) + 0.1*std(y) # parameter c
16+
```
17+
18+
This code is a simple and not very accurate heuristic on how to estimate the a and c parameters. If you have estimated
19+
using maximum likelihood it will be probably better
20+
21+
The fact occurs because if in the optimization the gradient makes `c < maximum(y)` or `a > minimum(y)` then it
22+
is impossible that y comes from the BetaFourParameters distribution leading to DomainErrors
23+
"""
24+
BetaFourParameters
25+
26+
function score!(score_til::Matrix{T}, y::T, ::Type{BetaFourParameters}, param::Matrix{T}, t::Int) where T
27+
score_til[t, 1] = -(param[t, 3] - 1)/(y - param[t, 1]) + (param[t, 4] + param[t, 3] - 1)/(param[t, 2] - param[t, 1])
28+
score_til[t, 2] = (param[t, 4] - 1)/(param[t, 2] - y) - (param[t, 4] + param[t, 3] - 1)/(param[t, 2] - param[t, 1])
29+
score_til[t, 3] = log(y - param[t, 1]) - log(param[t, 2] - param[t, 1]) +
30+
digamma(param[t, 3] + param[t, 4]) - digamma(param[t, 3])
31+
score_til[t, 4] = log(param[t, 2] - y) - log(param[t, 2] - param[t, 1]) +
32+
digamma(param[t, 3] + param[t, 4]) - digamma(param[t, 4])
33+
return
34+
end
35+
36+
function log_likelihood(::Type{BetaFourParameters}, y::Vector{T}, param::Matrix{T}, n::Int) where T
37+
loglik = 0.0
38+
for t in 1:n
39+
loglik += (param[t, 3] - 1)*log(y[t] - param[t, 1]) + (param[t, 4] - 1) * log(param[t, 2] - y[t]) -
40+
(param[t, 3] + param[t, 4] - 1) * log(param[t, 2] - param[t, 1]) - logbeta(param[t, 3], param[t, 4])
41+
end
42+
return -loglik
43+
end
44+
45+
# Links
46+
function link!(param_tilde::Matrix{T}, ::Type{BetaFourParameters}, param::Matrix{T}, t::Int) where T
47+
param_tilde[t, 1] = link(IdentityLink, param[t, 1])
48+
param_tilde[t, 2] = link(IdentityLink, param[t, 2])
49+
param_tilde[t, 3] = link(LogLink, param[t, 3], zero(T))
50+
param_tilde[t, 4] = link(LogLink, param[t, 4], zero(T))
51+
return
52+
end
53+
function unlink!(param::Matrix{T}, ::Type{BetaFourParameters}, param_tilde::Matrix{T}, t::Int) where T
54+
param[t, 1] = unlink(IdentityLink, param_tilde[t, 1])
55+
param[t, 2] = unlink(IdentityLink, param_tilde[t, 2])
56+
param[t, 3] = unlink(LogLink, param_tilde[t, 3], zero(T))
57+
param[t, 4] = unlink(LogLink, param_tilde[t, 4], zero(T))
58+
return
59+
end
60+
function jacobian_link!(aux::AuxiliaryLinAlg{T}, ::Type{BetaFourParameters}, param::Matrix{T}, t::Int) where T
61+
aux.jac[1] = jacobian_link(IdentityLink, param[t, 1])
62+
aux.jac[2] = jacobian_link(IdentityLink, param[t, 2])
63+
aux.jac[3] = jacobian_link(LogLink, param[t, 3], zero(T))
64+
aux.jac[4] = jacobian_link(LogLink, param[t, 4], zero(T))
65+
return
66+
end
67+
68+
# utils
69+
function update_dist(::Type{BetaFourParameters}, param::Matrix{T}, t::Int) where T
70+
small_threshold!(param[:, 3:4], SMALL_NUM, t)
71+
beta = Beta(param[t, 3], param[t, 4])
72+
return LocationScale(param[t, 1], param[t, 2] - param[t, 1], beta)
73+
end
74+
75+
function params_sdm(d::BetaFourParameters)
76+
return (d.μ, d.σ + d.μ, Distributions.params(d.ρ)...)
77+
end
78+
79+
function num_params(::Type{BetaFourParameters})
80+
return 4
81+
end
82+
83+
# d = Beta(1, 1)
84+
# d2 = LocationScale(-1, 3, d)
85+
# maximum(d2)
86+
# minimum(d2)

src/distributions/common_interface.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Currently supported distributions
22
const DISTS = [
33
Beta,
4+
BetaFourParameters,
45
Exponential,
56
Gamma,
67
LogitNormal,
@@ -14,6 +15,7 @@ const DISTS = [
1415
]
1516

1617
export Beta,
18+
BetaFourParameters,
1719
Exponential,
1820
Gamma,
1921
LogitNormal,
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# Define TDistLocationScale as the location scale transformation of the Distributions.jl TDist
22
export TDistLocationScale
3-
TDistLocationScale = LocationScale{Float64,TDist{Float64}}
3+
TDistLocationScale = LocationScale{Float64,TDist{Float64}}
4+
5+
export BetaFourParameters
6+
BetaFourParameters = LocationScale{Float64,Beta{Float64}}

test/utils.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
const PARAMETER_REQUIRED_DISTS = [Chisq; Chi; TDist]
2-
const FI_NOT_IMPLEMENTED = [Weibull; NegativeBinomial]
1+
const FI_NOT_IMPLEMENTED = [Weibull; BetaFourParameters; NegativeBinomial]
32

43
struct FakeDist{T<:Real} <: Distributions.ContinuousUnivariateDistribution
54
foo::T
65
end
76

87
function instantiate_dist(D::Type{<:Distribution})
9-
if D == TDistLocationScale
8+
if D == ScoreDrivenModels.TDistLocationScale
109
params = [1.0 2.0 10]
1110
return ScoreDrivenModels.update_dist(D, params, 1)
12-
elseif D in PARAMETER_REQUIRED_DISTS
11+
elseif D == ScoreDrivenModels.TDist
1312
params = [10][:, :]
1413
return ScoreDrivenModels.update_dist(D, params, 1)
14+
elseif D == ScoreDrivenModels.BetaFourParameters
15+
params = [0.0 10.0 2.0 3]
16+
return ScoreDrivenModels.update_dist(D, params, 1)
1517
else
1618
params = 0.5 * ones(1, ScoreDrivenModels.num_params(D))
1719
return ScoreDrivenModels.update_dist(D, params, 1)
@@ -26,8 +28,8 @@ function test_score_mean(D::Type{<:Distribution}; n::Int = 10^7, seed::Int = 10,
2628
n_params = ScoreDrivenModels.num_params(D)
2729
avg = zeros(1, n_params)
2830
obs = rand(dist, n)
31+
score_aux = ones(1, n_params)
2932
for i = 1:n
30-
score_aux = ones(1, n_params)
3133
ScoreDrivenModels.score!(score_aux, obs[i], D, pars, 1)
3234
avg .+= score_aux
3335
end
@@ -43,8 +45,8 @@ function test_fisher_information(D::Type{<:Distribution}; n::Int = 10^6, seed::I
4345
n_params = ScoreDrivenModels.num_params(D)
4446
var_terms = zeros(n_params, n_params)
4547
obs = rand(dist, n)
48+
score_aux = ones(1, n_params)
4649
for i = 1:n
47-
score_aux = ones(1, n_params)
4850
ScoreDrivenModels.score!(score_aux, obs[i], D, pars, 1)
4951
var_terms .+= score_aux' * score_aux
5052
end

0 commit comments

Comments
 (0)