Skip to content

Commit

Permalink
Fix Extreme Values Copulas (#226)
Browse files Browse the repository at this point in the history
* Fix A function in AsymGalambosCopula to improve readability.

* refactor: Update AsymLogCopula struct with individual asymmetry parameters + Fix the A function

* refactor: Simplify calculation in `A` function in AsymMixedCopula.

* Fully rewrite BC2Copula parameters and methods

Changed the BC2Copula parameters to 'a' and 'b'. Updated the functions
to reflect the new parameters. Added functions τ and ρ for calculations.

* refactor: Rename function `ρₛ` to `ρ` in CuadrasAugeCopula.

* refactor(GalambosCopula.jl): Improve handling of large parameter values

* refactor: Simplify calculation functions in HuslerReissCopula

* refactor: Update specific A function of LogCopula

* refactor: Rename parameters in MOCopula and update related functions

* refactor: Improve spacing in MixedCopula calculation formula

* refactor: Remove unused _pdf function in tEVCopula

* refactor: Simplify _pdf function and add τ and ρ functions to EVCs
  • Loading branch information
lrnv authored Sep 21, 2024
1 parent 4899441 commit 5aa54b5
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 165 deletions.
35 changes: 5 additions & 30 deletions src/ExtremeValueCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,6 @@ function D_B_ℓ(C::ExtremeValueCopula, t::Vector{Float64}, B::Vector{Int})
end

# Función PDF para ExtremeValueCopula usando ℓ
function _pdf(C::ExtremeValueCopula, u::AbstractArray{<:Real})
t = -log.(u)
c = exp(-(C, t))
D1 = D_B_ℓ(C, t, [1])
D2 = D_B_ℓ(C, t, [2])
D12 = D_B_ℓ(C, t, [1, 2])
return c * (-D12 + D1 * D2) / (u[1] * u[2])
end
function Distributions._logpdf(C::ExtremeValueCopula, u::AbstractArray{<:Real})
t = -log.(u)
c = exp(-(C, t))
Expand All @@ -106,26 +98,12 @@ function Distributions._logpdf(C::ExtremeValueCopula, u::AbstractArray{<:Real})
D12 = D_B_ℓ(C, t, [1, 2])
return log(c) + log(-D12 + D1 * D2) - log(u[1] * u[2])
end
# Definir la función para calcular τ
function τ(C::ExtremeValueCopula)
integrand(x) = begin
a = A(C, x)
da = dA(C, x)
return (x * (1 - x) / a) * da
end

integrate, _ = QuadGK.quadgk(integrand, 0.0, 1.0)
return integrate
end
# Definir la función para calcular τ and ρ
# Warning: the τ function can be veeeery unstable... it is actually very hard to compute correctly.
# In some case, it simply fails by a lot.
τ(C::ExtremeValueCopula) = QuadGK.quadgk(x -> d²A(C, x) * x * (1 - x) / A(C, x), 0.0, 1.0)[1]
ρ(C::ExtremeValueCopula) = 12 * QuadGK.quadgk(x -> 1 / (1 + A(C, x))^2, 0, 1)[1] - 3

function ρₛ(C::ExtremeValueCopula)
integrand(x) = 1 / (1 + A(C, x))^2

integral, _ = QuadGK.quadgk(integrand, 0, 1)

ρs = 12 * integral - 3
return ρs
end
# Función para calcular el coeficiente de dependencia en el límite superior
function λᵤ(C::ExtremeValueCopula)
return 2(1 - A(C, 0.5))
Expand All @@ -150,9 +128,6 @@ function Distributions._rand!(rng::Distributions.AbstractRNG, C::ExtremeValueCop
u1, u2 = rand(rng, Distributions.Uniform(0,1), 2)
z = rand(rng, ExtremeDist(C))
p = probability_z(C, z)
if p < -eps() || p > eps()
p = 0
end
c = rand(rng, Distributions.Bernoulli(p))
w = 0
if c == 1
Expand Down
13 changes: 3 additions & 10 deletions src/ExtremeValueCopulas/AsymGalambosCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,7 @@ struct AsymGalambosCopula{P} <: ExtremeValueCopula{P}
end

function A(C::AsymGalambosCopula, t::Real)
α = C.α
θ = C.θ

term1 = (θ[1] * t)^(-α)
term2 = (θ[2] * (1 - t))^(-α)

inner_term = term1 + term2

result = 1 - inner_term^(-1 / α)
return result
x₁ = - C.α * log(C.θ[1] * t)
x₂ = - C.α * log(C.θ[2] * (1-t))
return -expm1(-LogExpFunctions.logaddexp(x₁,x₂) / C.α)
end
14 changes: 4 additions & 10 deletions src/ExtremeValueCopulas/AsymLogCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ References:
"""
struct AsymLogCopula{P} <: ExtremeValueCopula{P}
α::P # Dependence Parameter
θ::Vector{P} # Asymmetry parameters (size 2)
θ₁::P
θ₂::P
function AsymLogCopula::P, θ::Vector{P}) where {P}
if length(θ) != 2
throw(ArgumentError("The vector θ must have 2 elements for the bivariate case"))
Expand All @@ -30,15 +31,8 @@ struct AsymLogCopula{P} <: ExtremeValueCopula{P}
elseif !(0 <= θ[1] <= 1) || !(0 <= θ[2] <= 1)
throw(ArgumentError("All parameters θ must be in the interval [0, 1]"))
else
return new{P}(α, θ)
return new{P}(α, θ[1],θ[2])
end
end
end

function A(C::AsymLogCopula, t::Real)
α = C.α
θ = C.θ

A = ((θ[1]^α)*(1-t)^α + (θ[2]^α)*(t^α))^(1/α)+(θ[1]- θ[2])*t + 1 -θ[1]
return A
end
A(C::AsymLogCopula, t::Real) = ((C.θ₁^C.α)*(1-t)^C.α + (C.θ₂^C.α)*(t^C.α))^(1/C.α)+(C.θ₁- C.θ₂)*t + 1 - C.θ₁
6 changes: 2 additions & 4 deletions src/ExtremeValueCopulas/AsymMixedCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ struct AsymMixedCopula{P} <: ExtremeValueCopula{P}
end

function A(C::AsymMixedCopula, t::Real)
θ = C.θ

a = θ[2]*t^3 + θ[1]*t^2-(θ[1]+θ[2])*t+1
return a
θ₁, θ₂ = C.θ[1], C.θ[2]
return θ₂*t^3 + θ₁*t^2 - (θ₁+θ₂)*t + 1
end
72 changes: 30 additions & 42 deletions src/ExtremeValueCopulas/BC2Copula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,63 @@
Fields:
- θ1::Real - parameter
- θ1::Real - parameter
- a::Real - parameter
- a::Real - parameter
Constructor
BC2Copula(θ1, θ2)
BC2Copula(a, b)
The bivariate BC₂ copula is parameterized by two parameters ``\\theta_{i} \\in [0,1], i=1,2``. It is an Extreme value copula with Pickands dependence function:
The bivariate BC2 copula is parameterized by two parameters ``a,b \\in [0,1]``. It is an Extreme value copula with Pickands dependence function:
```math
A(t) = \\max\\{\\theta_1 t, \\theta_2(1-t) \\} + \\max\\{(1-\\theta_1)t, (1-\\theta_2)(1-t)\\}
A(t) = \\max\\{a t, b (1-t) \\} + \\max\\{(1-a)t, (1-b)(1-t)\\}
```
References:
* [mai2011bivariate](@cite) Mai, J. F., & Scherer, M. (2011). Bivariate extreme-value copulas with discrete Pickands dependence measure. Extremes, 14, 311-324. Springer, 2011.
"""
struct BC2Copula{P} <: ExtremeValueCopula{P}
θ1::P
θ2::P

function BC2Copula::Vararg{Real})
if length(θ) !== 2
throw(ArgumentError("BC2Copula requires only 2 arguments."))
end
T = promote_type(typeof(θ[1]), typeof(θ[2]))
θ1, θ2 = T(θ[1]), T(θ[2])

if !(0 <= θ1 <= 1) || !(0 <= θ2 <= 1)
throw(ArgumentError("All θ parameters must be in [0,1]"))
a::P
b::P
function BC2Copula(a,b)
T = promote_type(typeof(a), typeof(b))
if !(0 <= a <= 1) || !(0 <= b <= 1)
throw(ArgumentError("Both parameters a and b must be in [0,1]"))
end
return new{T}(θ1, θ2)
return new{T}(T(a), T(b))
end
end

function (C::BC2Copula, t::Vector)
θ1, θ2 = C.θ1, C.θ2
a, b = C.a, C.b
t₁, t₂ = t
return max(θ1*t₁, θ2*t₂) + max((1-θ1)*t₁, (1-θ2)*t₂)
return max(a*t₁, b*t₂) + max((1-a)*t₁, (1-b)*t₂)
end

function A(C::BC2Copula, t::Real)
θ1, θ2 = C.θ1, C.θ2
return max(θ1*t, θ2*(1-t)) + max((1-θ1)*t, (1-θ2)*(1-t))
a, b = C.a, C.b
return max(a*t, b*(1-t)) + max((1-a)*t, (1-b)*(1-t))
end

function dA(C::BC2Copula, t::Float64)
θ1, θ2 = C.θ1, C.θ2

# Conditions for the derivative of the first part
if θ1*t >= θ2*(1-t)
f1_derivative = θ1
else
f1_derivative = -θ2
end

# Conditions for the derivative of the second part
if (1-θ1)*t >= (1-θ2)*(1-t)
f2_derivative = 1-θ1
else
f2_derivative = -(1-θ2)
end

return f1_derivative + f2_derivative
a, b = C.a, C.b
der1 = a*t >= b*(1-t) ? a : -b
der2 = (1-a)*t >= (1-b)*(1-t) ? -a : -(1-b)
return der1 + der2
end

function Distributions._rand!(rng::Distributions.AbstractRNG, C::BC2Copula, u::AbstractVector{T}) where {T<:Real}
θ1, θ2 = C.θ1, C.θ2
a, b = C.a, C.b
v1, v2 = rand(rng, Distributions.Uniform(0,1), 2)
u[1] = max(v1^(1/θ1),v2^(1/(1-θ1)))
u[2] = max(v1^(1/θ2),v2^(1/(1-θ2)))
u[1] = max(v1^(1/a), v2^(1/(1-a)))
u[2] = max(v1^(1/b), v2^(1/(1-b)))
return u
end

τ(C::BC2Copula) = 1 - abs(C.a - C.b)

function ρ(C::BC2Copula)
a,b = C.a, C.b
return 2 * (a + b + a*b + max(a,b) - 2a^2 - 2b^2) / (3 - a - b - min(a,b)) / (a + b + max(a,b))
end
2 changes: 1 addition & 1 deletion src/ExtremeValueCopulas/CuadrasAugeCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ dA(C::CuadrasAugeCopula, t::Real) = t <= 0.5 ? -C.θ : C.θ

τ(C::CuadrasAugeCopula) = C.θ/(2-C.θ)

ρₛ(C::CuadrasAugeCopula) = (3*C.θ)/(4-C.θ)
ρ(C::CuadrasAugeCopula) = (3*C.θ)/(4-C.θ)

# specific ℓ específica of Cuadras-Augé Copula
function (C::CuadrasAugeCopula, t::Vector)
Expand Down
4 changes: 2 additions & 2 deletions src/ExtremeValueCopulas/GalambosCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct GalambosCopula{P} <: ExtremeValueCopula{P}
θ::P # Copula parameter
function GalambosCopula(θ)
if θ > 19.5
@warn "The parameter θ = $(θ) is large, which may lead to numerical instability in any functions. Consider regularizing the input."
@info "GalambosCopula(θ=$(θ)): θ is large, which may lead to numerical issues."
end
if θ < 0
throw(ArgumentError("Theta must be >= 0"))
Expand All @@ -41,7 +41,7 @@ struct GalambosCopula{P} <: ExtremeValueCopula{P}
end
end

A(C::GalambosCopula, t::Real) = 1 - (t^(-C.θ) + (1 - t)^(-C.θ))^(-1/C.θ)
A(C::GalambosCopula, t::Real) = -expm1(-LogExpFunctions.logaddexp(-C.θ*log(t),-C.θ*log(1-t))/C.θ)
# This auxiliary function helps determine if we need binary search or not in the generation of random samples
function needs_binary_search(C::GalambosCopula)
return C.θ > 19.5
Expand Down
10 changes: 2 additions & 8 deletions src/ExtremeValueCopulas/HuslerReissCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,7 @@ function A(H::HuslerReissCopula, t::Real)
θ = H.θ
term1 = t * Distributions.cdf(Distributions.Normal(), θ^(-1) + 0.5 * θ * log(t / (1 - t)))
term2 = (1 - t) * Distributions.cdf(Distributions.Normal(), θ^(-1) + 0.5 * θ * log((1 - t) / t))

a = term1 + term2


return a
return term1 + term2
end

function dA(H::HuslerReissCopula, t::Real)
Expand All @@ -66,7 +62,5 @@ function dA(H::HuslerReissCopula, t::Real)
dA_term2 = -Distributions.cdf(Distributions.Normal(), θ^(-1) + 0.5 * θ * log((1 - t) / t)) +
(1 - t) * Distributions.pdf(Distributions.Normal(), θ^(-1) + 0.5 * θ * log((1 - t) / t)) * (0.5 * θ * (-1 / t - 1 / (1 - t)))

da = dA_term1 + dA_term2

return da
return dA_term1 + dA_term2
end
7 changes: 2 additions & 5 deletions src/ExtremeValueCopulas/LogCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,5 @@ function ℓ(G::LogCopula, t::Vector)
t₁, t₂ = t
return (t₁^θ + t₂^θ)^(1/θ)
end
# # specific A funcion of HuslerReissCopula
function A(C::LogCopula, t::Real)
θ = C.θ
return (t^θ + (1 - t)^θ)^(1/θ)
end
# # specific A funcion of LogCopula
A(C::LogCopula, t::Real) = exp(LogExpFunctions.logaddexp(C.θ*log(t),C.θ*log(1-t))/C.θ)
57 changes: 15 additions & 42 deletions src/ExtremeValueCopulas/MOCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
Fields:
- λ1::Real - parameter
- λ2::Real - parameter
- λ12::Real - parameter
- λ₁::Real - parameter
- λ₂::Real - parameter
- λ₁₂::Real - parameter
Constructor
Expand All @@ -21,49 +21,22 @@ References:
* [mai2012simulating](@cite) Mai, J. F., & Scherer, M. (2012). Simulating copulas: stochastic models, sampling algorithms, and applications (Vol. 4). World Scientific.
"""
struct MOCopula{P} <: ExtremeValueCopula{P}
λ1::P
λ2::P
λ12::P

function MOCopula::Vararg{Real})
if length(λ) !== 3
throw(ArgumentError("MOCopula requires only 3 arguments."))
end

T = promote_type(typeof(λ[1]), typeof(λ[2]), typeof(λ[3]))
λ1, λ2, λ12 = T(λ[1]), T(λ[2]), T(λ[3])

if λ1 < 0 || λ2 < 0 || λ12 < 0
a::P
b::P
function MOCopula(λ₁,λ₂,λ₁₂)
if λ₁ < 0 || λ₂ < 0 || λ₁₂ < 0
throw(ArgumentError("All λ parameters must be >= 0"))
end

return new{T}(λ1, λ2, λ12)
a, b = λ₁ / (λ₁ + λ₁₂), λ₂ / (λ₂ + λ₁₂)
return new{typeof(a)}(a,b)
end
end

function A(C::MOCopula, t::Real)
λ1, λ2, λ12 = C.λ1, C.λ2, C.λ12
return ((λ1 * (1 - t))/(λ1 + λ12)) + ((λ2 * t)/(λ2 + λ12)) + λ12 * max((1-t)/(λ1 + λ12), t/(λ2 + λ12))
end

function (C::MOCopula, t::Vector)
λ1, λ2, λ12 = C.λ1, C.λ2, C.λ12
t₁, t₂ = t
return ((λ1 * t₂)/(λ1 + λ12)) + ((λ2 * t₁)/(λ2 + λ12)) + λ12 * max(t₂/(λ1 + λ12), t₁/(λ2 + λ12))
end

function _cdf(C::MOCopula, u::AbstractArray{<:Real})
λ1, λ2, λ12 = C.λ1, C.λ2, C.λ12
u₁, u₂ = u
return min(u₂*u₁^(1 - λ12/(λ1 + λ12)), u₁ * u₂^(1 - λ12/(λ2 + λ12)))
end

A(C::MOCopula, t::Real) = max(t + (1-t)*C.b, (1-t)+C.a*t)
_cdf(C::MOCopula, u::AbstractArray{<:Real}) = min(u[1]^C.a * u[2], u[1] * u[2]^C.b)
function Distributions._rand!(rng::Distributions.AbstractRNG, C::MOCopula, u::AbstractVector{T}) where {T<:Real}
λ1, λ2, λ12 = C.λ1, C.λ2, C.λ12
r, s, t = rand(rng, Distributions.Uniform(0,1),3)
x = min(-log(r)/λ1, -log(t)/λ12)
y = min(-log(s)/λ2, -log(t)/λ12)
u[1] = exp(-(λ1+λ12)*x)
u[2] = exp(-(λ2+λ12)*y)
r, s, t = -log.(rand(rng,3)) # Exponentials(1)
u[1] = exp(-min(r/(1-C.a), t/C.a))
u[2] = exp(-min(s/(1-C.b), t/C.b))
return u
end
τ(C::MOCopula) = C.a*C.b/(C.a+C.b-C.a*C.b)
2 changes: 1 addition & 1 deletion src/ExtremeValueCopulas/MixedCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ struct MixedCopula{P} <: ExtremeValueCopula{P}
end
end

A(C::MixedCopula, t::Real) = C.θ*t^2 - C.θ*t + 1
A(C::MixedCopula, t::Real) = C.θ * t^2 - C.θ * t + 1
10 changes: 0 additions & 10 deletions src/ExtremeValueCopulas/tEVCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,6 @@ function d²A(C::tEVCopula, t::Real)
d2A = (dA_plus - dA_minus) / (2 * h)
return d2A
end

# PDF function for ExtremeValueCopula using ℓ
function _pdf(C::tEVCopula, u::AbstractArray{<:Real})
t = -log.(u)
c = exp(-(C, t))
D1 = D_B_ℓ(C, t, [1])
D2 = D_B_ℓ(C, t, [2])
D12 = D_B_ℓ(C, t, [1, 2])
return c * (-D12 + D1 * D2) / (u[1] * u[2])
end
function Distributions._logpdf(C::tEVCopula, u::AbstractArray{<:Real})
t = -log.(u)
c = exp(-(C, t))
Expand Down

0 comments on commit 5aa54b5

Please sign in to comment.