Skip to content

Commit

Permalink
Adding a few tests and fixing stuff to pass them.
Browse files Browse the repository at this point in the history
  • Loading branch information
lrnv committed Nov 17, 2023
1 parent bd38c39 commit f4dc2b0
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 36 deletions.
6 changes: 4 additions & 2 deletions src/ArchimedeanCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ end
ϕ⁻¹(C::ArchimedeanCopula{d},x) where d = Roots.find_zero(t -> ϕ(C,t) - x, (0.0, Inf))

williamson_dist(C::ArchimedeanCopula{d}) where d = WilliamsonTransforms.𝒲₋₁(t -> ϕ(C,t),d)
τ(C::ArchimedeanCopula) = 4*Distributions.expectation(r -> ϕ(C,r), williamson_dist(C)) - 1

function τ(C::ArchimedeanCopula)
@show C
return 4*Distributions.expectation(r -> ϕ(C,r), williamson_dist(C)) - 1
end

function _archi_rand!(rng,C::ArchimedeanCopula{d},R,x) where d
# x is assumed to already be random exponentials produced by Random.randexp
Expand Down
15 changes: 12 additions & 3 deletions src/ArchimedeanCopulas/AMHCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,25 @@ end
ϕ( C::AMHCopula,t) = (1-C.θ)/(exp(t)-C.θ)
ϕ⁻¹( C::AMHCopula,t) = log(C.θ + (1-C.θ)/t)

τ(C::AMHCopula) = 1 - 2(C.θ+(1-C.θ)^2*log(1-C.θ))/(3C.θ^2) # no closed form inverse...
τ(C::AMHCopula) = _amh_tau_f(C.θ) # no closed form inverse...

_amh_tau_f(θ) = θ == 1 ? 1/3 : 1 - 2+(1-θ)^2*log1p(-θ))/(3θ^2)

# if θ = -1, we obtain (5 -8*log(2))/3

function τ⁻¹(::Type{AMHCopula},τ)
if τ == zero(τ)
return τ
end
if τ > 1/3
@warn "AMHCopula cannot handle kendall tau's greater than 1/3. We capped it to 1/3."
return 1
return one(τ)
end
if τ < (5 - 8*log(2))/3
@warn "AMHCopula cannot handle kendall tau's smaller than (5- 8ln(2))/3 (approx -0.1817). We capped it to this value."
return -one(τ)
end
return Roots.find_zero-> 1 - 2+(1-θ)^2*log(1-θ))/(3θ^2) - τ, (-1.0, 1.0))
return Roots.find_zero-> _amh_tau_f(θ) - τ, (-one(τ), one(τ)))
end
williamson_dist(C::AMHCopula{d,T}) where {d,T} = C.θ >= 0 ? WilliamsonFromFrailty(1 + Distributions.Geometric(1-C.θ),d) : WilliamsonTransforms.𝒲₋₁(t -> ϕ(C,t),d)

Expand Down
3 changes: 3 additions & 0 deletions src/ArchimedeanCopulas/FrankCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ function τ⁻¹(::Type{FrankCopula},τ)
if τ == zero(τ)
return τ
end
if abs==1)
return Inf * τ
end
x₀ = (1-τ)/4
return Roots.fzero(x -> (1-D₁(x))/x - x₀, 1e-4, Inf)
end
Expand Down
15 changes: 8 additions & 7 deletions src/ArchimedeanCopulas/GumbelBarnettCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,23 @@ end
function τ(C::GumbelBarnettCopula)
# Define the function to integrate
f(x) = -x * (1 - C.θ * log(x)) * log(1 - C.θ * log(x)) / C.θ

result = Distributions.expectation(f,Distributions.Uniform(0,1))
# Calculate the integral using GSL
result, _ = gsl_integration_qags(f, 0.0, 1.0, [C.θ], 1e-7, 1000)
# result =
# result, _ = GSL.integration_qags(f, 0.0, 1.0, [C.θ], 1e-7, 1000)

return 1+4*result
end
function τ⁻¹(::Type{GumbelBarnettCopula}, τ)
if τ == zero(τ)
return τ
function τ⁻¹(::Type{GumbelBarnettCopula}, tau)
if tau == zero(tau)
return tau
end

# Define an anonymous function that takes a value x and computes τ
#for a GumbelBarnettCopula with θ = x
τ_func(x) = τ(GumbelBarnettCopula{d, Float64}(x))
τ_func(x) = τ(GumbelBarnettCopula(2,x))

# Use the bisection method to find the root
x = Roots.find_zero(x -> τ_func(x) - τ, (0.0, 1.0))
x = Roots.find_zero(x -> τ_func(x) - tau, (0.0, 1.0))
return x
end
16 changes: 14 additions & 2 deletions src/ArchimedeanCopulas/GumbelCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ struct GumbelCopula{d,T} <: ArchimedeanCopula{d}
θ::T
function GumbelCopula(d,θ)
if θ < 1
throw(ArgumentError("Theta must be greater than 1"))
@show θ
throw(ArgumentError("Theta must be greater than or equal to 1"))
elseif θ == 1
return IndependentCopula(d)
elseif θ == Inf
Expand All @@ -35,7 +36,18 @@ end
ϕ( C::GumbelCopula, t) = exp(-t^(1/C.θ))
ϕ⁻¹(C::GumbelCopula, t) = (-log(t))^C.θ
τ(C::GumbelCopula) = ifelse(isfinite(C.θ), (C.θ-1)/C.θ, 1)
τ⁻¹(::Type{GumbelCopula},τ) =ifelse== 1, Inf, 1/(1-τ))
function τ⁻¹(::Type{GumbelCopula},τ)
if τ == 1
return Inf
else
θ = 1/(1-τ)
if θ < 1
@warn "GumbelCopula cannot handle negative kendall tau's, returning independence.."
return 1
end
return θ
end
end
williamson_dist(C::GumbelCopula{d,T}) where {d,T} = WilliamsonFromFrailty(AlphaStable= 1/C.θ, β = 1,scale = cos/(2C.θ))^C.θ, location = (C.θ == 1 ? 1 : 0)), d)


Expand Down
4 changes: 3 additions & 1 deletion src/ArchimedeanCopulas/InvGaussianCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct InvGaussianCopula{d,T} <: ArchimedeanCopula{d}
throw(ArgumentError("Theta must be non-negative."))
elseif θ == 0
return IndependentCopula(d)
elseif isinf(θ)
throw(ArgumentError("Theta cannot be infinite"))
else
return new{d,typeof(θ)}(θ)
end
Expand All @@ -51,7 +53,7 @@ function τ⁻¹(::Type{InvGaussianCopula}, τ)
end

# Define an anonymous function that takes a value x and computes τ for an InvGaussianCopula copula with θ = x
τ_func(x) = τ(InvGaussianCopula{d, Float64}(x))
τ_func(x) = τ(InvGaussianCopula{2, Float64}(x))

# Set an initial value for x₀ (adjustable)
x₀ = (1-τ)/4
Expand Down
15 changes: 14 additions & 1 deletion src/ArchimedeanCopulas/JoeCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@ struct JoeCopula{d,T} <: ArchimedeanCopula{d}
end
ϕ( C::JoeCopula, t) = 1-(-expm1(-t))^(1/C.θ)
ϕ⁻¹(C::JoeCopula, t) = -log1p(-(1-t)^C.θ)
τ(C::JoeCopula) = 1 - 4sum(1/(k*(2+k*C.θ)*(C.θ*(k-1)+2)) for k in 1:1000) # 446 in R copula.
_joe_tau_f(θ) = 1 - 4sum(1/(k*(2+k*θ)**(k-1)+2)) for k in 1:1000) # 446 in R copula.
τ(C::JoeCopula) = _joe_tau_f(C.θ)
function τ⁻¹(::Type{JoeCopula},τ)
if τ == 1
return Inf
elseif τ == 0
return 1
elseif τ < 0
@warn "JoeCoula cannot handle negative kendall taus, we return the independence..."
return one(τ)
else
return Roots.find_zero-> _joe_tau_f(θ) - τ, (one(τ),τ*Inf))
end
end
williamson_dist(C::JoeCopula{d,T}) where {d,T} = WilliamsonFromFrailty(Sibuya(1/C.θ), d)


5 changes: 4 additions & 1 deletion src/Copula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ Base.length(::Copula{d}) where d = d
# Base.eltype
# τ, τ⁻¹
# Base.eltype
function Distributions.cdf(C::Copula{d},u) where d
function Distributions.cdf(C::Copula{d},u::AbstractVector) where d
length(u) != length(C) && throw(ArgumentError("Dimension mismatch between copula and input vector"))
return _cdf(C,u)
end
function Distributions.cdf(C::Copula{d},A::AbstractMatrix) where d
return [Distributions.cdf(C,u) for u in eachcol(A)]
end
function _cdf(C::CT,u) where {CT<:Copula}
f(x) = Distributions.pdf(C,x)
z = zeros(eltype(u),length(C))
Expand Down
3 changes: 3 additions & 0 deletions src/MiscellaneousCopulas/MCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ The two Frechet-Hoeffding bounds are also Archimedean copulas, although this lin
struct MCopula{d} <: Copula{d} end
MCopula(d) = MCopula{d}()
_cdf(::MCopula{d},u) where {d} = minimum(u)
function Distributions._logpdf(C::MCopula, u)
return all(u == u[1]) ? zero(eltype(u)) : eltype(u)(-Inf)
end
function Distributions._rand!(rng::Distributions.AbstractRNG, ::MCopula{d}, x::AbstractVector{T}) where {d,T<:Real}
x .= rand(rng)
end
Expand Down
4 changes: 4 additions & 0 deletions src/MiscellaneousCopulas/WCopula.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ The two Frechet-Hoeffding bounds are also Archimedean copulas, although this lin
struct WCopula{d} <: Copula{d} end
WCopula(d) = WCopula{d}()
_cdf(::WCopula{d},u) where {d} = max(1 + sum(u)-d,0)
function Distributions._logpdf(C::WCopula{d}, u) where {d}
@assert d == 2
return u[1] + u[2] == 1 ? zero(eltype(u)) : eltype(u)(-Inf)
end
function Distributions._rand!(rng::Distributions.AbstractRNG, ::WCopula{d}, x::AbstractVector{T}) where {d,T<:Real}
@assert d==2
x[1] = rand(rng)
Expand Down
95 changes: 76 additions & 19 deletions test/archimedean_tests.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@

@testitem "Test of τ ∘ τ_inv bijection" begin
using Random
using StableRNGs
rng = StableRNG(123)
taus = [0.0, 0.1, 0.5, 0.9, 1.0]

for T in (
Expand All @@ -21,79 +19,138 @@
end
end

# For each archimedean, we test:
# - The sampling of a dataset
# - evaluation of pdf and cdf
# - fitting on the dataset.

@testitem "AMHCopula - Test sampling all cases" begin
@testitem "AMHCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
for d in 2:10
for θ [-1.0,-rand(rng),0.0,rand(rng)]
rand(rng,AMHCopula(d,θ),10)
C = AMHCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
fit(AMHCopula,data)
end
end
@test true
end
@testitem "ClaytonCopula - Test sampling all cases" begin
@testitem "ClaytonCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
rand(rng,ClaytonCopula(2,-1),10)
C = ClaytonCopula(2,-1)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
fit(ClaytonCopula,data)
for d in 2:10
for θ [-1/(d-1) * rand(rng),0.0,-log(rand(rng)), Inf]
rand(rng,ClaytonCopula(d,θ),10)
C = ClaytonCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
fit(ClaytonCopula,data)
end
end
@test true
end
@testitem "FrankCopula - Test sampling all cases" begin
@testitem "FrankCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
rand(rng,FrankCopula(2,-Inf),10)
rand(rng,FrankCopula(2,log(rand(rng))),10)

C = FrankCopula(2,-Inf)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
@test_broken fit(FrankCopula,data)

C = FrankCopula(2,log(rand(rng)))
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
@test_broken fit(FrankCopula,data)


for d in 2:10
for θ [0.0,rand(rng),1.0,-log(rand(rng)), Inf]
rand(rng,FrankCopula(d,θ),10)
C = FrankCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
@test_broken fit(FrankCopula,data)
end
end
@test true
end
@testitem "GumbelCopula - Test sampling all cases" begin
@testitem "GumbelCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
for d in 2:10
for θ [1.0,1-log(rand(rng)), Inf]
rand(rng,GumbelCopula(d,θ),10)
@show d,θ
C = GumbelCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
fit(GumbelCopula,data)
end
end
@test true
end
@testitem "JoeCopula - Test sampling all cases" begin
@testitem "JoeCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
for d in 2:10
for θ [1.0,1-log(rand(rng)), Inf]
rand(rng,JoeCopula(d,θ),10)
C = JoeCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
fit(JoeCopula,data)
end
end
@test true
end

@testitem "GumbelBarnettCopula - Test sampling all cases" begin
@testitem "GumbelBarnettCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
for d in 2:10
for θ [0.0,rand(rng),1.0]
rand(rng,GumbelBarnettCopula(d,θ),10)
C = GumbelBarnettCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
@test_broken fit(GumbelBarnettCopula,data)
end
end
@test true
end

@testitem "InvGaussianCopula - Test sampling all cases" begin
@testitem "InvGaussianCopula - sampling,pdf,cdf,fit" begin
using StableRNGs
using Distributions
rng = StableRNG(123)
for d in 2:10
for θ [rand(rng),1.0, Inf]
rand(rng,InvGaussianCopula(d,θ),10)
for θ [rand(rng),1.0, -log(rand(rng))]
@show d,θ
C = InvGaussianCopula(d,θ)
data = rand(rng,C,100)
@test all(pdf(C,data) .>= 0)
@test all(0 .<= cdf(C,data) .<= 1)
@test_broken fit(InvGaussianCopula,data)
end
end
@test true
end
end

0 comments on commit f4dc2b0

Please sign in to comment.