From 81c4b29f244ce0c1cb5154a15818f6e6f8fac4f9 Mon Sep 17 00:00:00 2001 From: Oskar Laverny Date: Tue, 21 Nov 2023 19:52:05 +0100 Subject: [PATCH 1/4] remove useless methods see https://discourse.julialang.org/t/how-to-dispatch-on-a-type-alias/106476/31 --- Project.toml | 1 + src/ArchimedeanCopula.jl | 64 +++++++++++++++------------------------- src/Copulas.jl | 1 + 3 files changed, 25 insertions(+), 41 deletions(-) diff --git a/Project.toml b/Project.toml index 7db003f8..b9908430 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" Cubature = "667455a9-e2ce-5579-9412-b964f529a492" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" MvNormalCDF = "37188c8d-bc69-4638-b057-733e744175ec" QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" diff --git a/src/ArchimedeanCopula.jl b/src/ArchimedeanCopula.jl index ce2b58b6..ca05650a 100644 --- a/src/ArchimedeanCopula.jl +++ b/src/ArchimedeanCopula.jl @@ -155,47 +155,29 @@ end ################ ################ ################################################################################################ -################################################################################################ -# Deal with easy cases with meta-programming: -const AMHCopula{d,T} = ArchimedeanCopula{d,AMHGenerator{T}} -AMHCopula(d,θ) = ArchimedeanCopula(d,AMHGenerator(θ)) -generatorof(::Type{AMHCopula}) = AMHGenerator - -const ClaytonCopula{d,T} = ArchimedeanCopula{d,ClaytonGenerator{T}} -ClaytonCopula(d,θ) = ArchimedeanCopula(d,ClaytonGenerator(θ)) -generatorof(::Type{ClaytonCopula}) = ClaytonGenerator - -const FrankCopula{d,T} = ArchimedeanCopula{d,FrankGenerator{T}} -FrankCopula(d,θ) = ArchimedeanCopula(d,FrankGenerator(θ)) -generatorof(::Type{FrankCopula}) = FrankGenerator - -const GumbelBarnettCopula{d,T} = ArchimedeanCopula{d,GumbelBarnettGenerator{T}} -GumbelBarnettCopula(d,θ) = ArchimedeanCopula(d,GumbelBarnettGenerator(θ)) -generatorof(::Type{GumbelBarnettCopula}) = GumbelBarnettGenerator - -const GumbelCopula{d,T} = ArchimedeanCopula{d,GumbelGenerator{T}} -GumbelCopula(d,θ) = ArchimedeanCopula(d,GumbelGenerator(θ)) -generatorof(::Type{GumbelCopula}) = GumbelGenerator - -const InvGaussianCopula{d,T} = ArchimedeanCopula{d,InvGaussianGenerator{T}} -InvGaussianCopula(d,θ) = ArchimedeanCopula(d,InvGaussianGenerator(θ)) -generatorof(::Type{InvGaussianCopula}) = InvGaussianGenerator - -const JoeCopula{d,T} = ArchimedeanCopula{d,JoeGenerator{T}} -JoeCopula(d,θ) = ArchimedeanCopula(d,JoeGenerator(θ)) -generatorof(::Type{JoeCopula}) = JoeGenerator - -const IndependentCopula{d} = ArchimedeanCopula{d,IndependentGenerator} -IndependentCopula(d) = ArchimedeanCopula(d,IndependentGenerator()) -generatorof(::Type{IndependentCopula}) = IndependentGenerator - -const MCopula{d} = ArchimedeanCopula{d,MGenerator} -MCopula(d) = ArchimedeanCopula(d,MGenerator()) -generatorof(::Type{MCopula}) = MGenerator - -const WCopula{d} = ArchimedeanCopula{d,WGenerator} -WCopula(d) = ArchimedeanCopula(d,WGenerator()) -generatorof(::Type{WCopula}) = WGenerator + +## Automatic syntactic sugar for all ZeroVariateGenerators and UnivariateGenerators. +function generatorof(::Type{S}) where {S <: ArchimedeanCopula} + S2 = hasproperty(S,:body) ? S.body : S + S3 = hasproperty(S2, :body) ? S2.body : S2 + S3.parameters[2].name.wrapper +end +for T in InteractiveUtils.subtypes(ZeroVariateGenerator) + G = Symbol(T) + C = Symbol(string(G)[begin:end-9]*"Copula") + @eval begin + const ($C){d} = ArchimedeanCopula{d,($G)} + ($C)(d) = ArchimedeanCopula(d,($G)()) + end +end +for T in InteractiveUtils.subtypes(UnivariateGenerator) + G = Symbol(T) + C = Symbol(string(G)[begin:end-9]*"Copula") + @eval begin + const ($C){d,Tθ} = ArchimedeanCopula{d,($G){Tθ}} + ($C)(d,θ) = ArchimedeanCopula(d,($G)(θ)) + end +end # The zero-variate ones just need a few more methods: Distributions._logpdf(::ArchimedeanCopula{d,IndependentGenerator}, u) where {d} = all(0 .<= u .<= 1) ? zero(eltype(u)) : eltype(u)(-Inf) diff --git a/src/Copulas.jl b/src/Copulas.jl index 264326ff..a5348721 100644 --- a/src/Copulas.jl +++ b/src/Copulas.jl @@ -2,6 +2,7 @@ module Copulas import Base import Random + import InteractiveUtils import SpecialFunctions import Roots import Distributions From b207f9df96d43e1d58e96e734363ddebb3c40adf Mon Sep 17 00:00:00 2001 From: Oskar Laverny Date: Wed, 22 Nov 2023 09:10:35 +0100 Subject: [PATCH 2/4] Split string "Copulas.XXXGenerator" --- src/ArchimedeanCopula.jl | 4 ++-- src/Generator/ZeroVariateGenerator/IndependentGenerator.jl | 2 +- src/Generator/ZeroVariateGenerator/MGenerator.jl | 2 +- src/Generator/ZeroVariateGenerator/WGenerator.jl | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ArchimedeanCopula.jl b/src/ArchimedeanCopula.jl index ca05650a..6e081e5a 100644 --- a/src/ArchimedeanCopula.jl +++ b/src/ArchimedeanCopula.jl @@ -163,7 +163,7 @@ function generatorof(::Type{S}) where {S <: ArchimedeanCopula} S3.parameters[2].name.wrapper end for T in InteractiveUtils.subtypes(ZeroVariateGenerator) - G = Symbol(T) + G = Symbol(last(split(string(T),'.'))) C = Symbol(string(G)[begin:end-9]*"Copula") @eval begin const ($C){d} = ArchimedeanCopula{d,($G)} @@ -171,7 +171,7 @@ for T in InteractiveUtils.subtypes(ZeroVariateGenerator) end end for T in InteractiveUtils.subtypes(UnivariateGenerator) - G = Symbol(T) + G = Symbol(last(split(string(T),'.'))) C = Symbol(string(G)[begin:end-9]*"Copula") @eval begin const ($C){d,Tθ} = ArchimedeanCopula{d,($G){Tθ}} diff --git a/src/Generator/ZeroVariateGenerator/IndependentGenerator.jl b/src/Generator/ZeroVariateGenerator/IndependentGenerator.jl index ef054377..7100316e 100644 --- a/src/Generator/ZeroVariateGenerator/IndependentGenerator.jl +++ b/src/Generator/ZeroVariateGenerator/IndependentGenerator.jl @@ -19,7 +19,7 @@ It happends to be an Archimedean Copula, with generator : \\phi(t) = \\exp{-t} ``` """ -struct IndependentGenerator <: Generator end +struct IndependentGenerator <: ZeroVariateGenerator end max_monotony(::IndependentGenerator) = Inf ϕ(::IndependentGenerator,t) = exp(-t) ϕ⁻¹(::IndependentGenerator,t) = -log(t) diff --git a/src/Generator/ZeroVariateGenerator/MGenerator.jl b/src/Generator/ZeroVariateGenerator/MGenerator.jl index 90b6e251..32bbc53b 100644 --- a/src/Generator/ZeroVariateGenerator/MGenerator.jl +++ b/src/Generator/ZeroVariateGenerator/MGenerator.jl @@ -16,7 +16,7 @@ W(\\mathbf{u}) \\le C(\\mathbf{u}) \\le M(\\mathbf{u}) The two Frechet-Hoeffding bounds are also Archimedean copulas. """ -struct MGenerator <: Generator end +struct MGenerator <: ZeroVariateGenerator end max_monotony(::MGenerator) = Inf τ(::MGenerator) = 1 ϕ(::MGenerator,t) = throw(ArgumentError("MGenerator cannot have a ϕ function")) diff --git a/src/Generator/ZeroVariateGenerator/WGenerator.jl b/src/Generator/ZeroVariateGenerator/WGenerator.jl index 11eebd1e..209933d6 100644 --- a/src/Generator/ZeroVariateGenerator/WGenerator.jl +++ b/src/Generator/ZeroVariateGenerator/WGenerator.jl @@ -14,7 +14,7 @@ W(\\mathbf{u}) \\le C(\\mathbf{u}) \\le M(\\mathbf{u}) The two Frechet-Hoeffding bounds are also Archimedean copulas. """ -struct WGenerator <: Generator end +struct WGenerator <: ZeroVariateGenerator end max_monotony(G::WGenerator) = 2 τ(::WGenerator) = -1 ϕ(::WGenerator,t) = throw(MethodError("WGenerator cannot have a ϕ function")) From 1a08a1a4b35e9a1eba12a0585d61be0dea397dcc Mon Sep 17 00:00:00 2001 From: Oskar Laverny Date: Wed, 22 Nov 2023 09:14:04 +0100 Subject: [PATCH 3/4] add link to issue --- src/ArchimedeanCopula.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ArchimedeanCopula.jl b/src/ArchimedeanCopula.jl index 6e081e5a..d00110ab 100644 --- a/src/ArchimedeanCopula.jl +++ b/src/ArchimedeanCopula.jl @@ -157,6 +157,7 @@ end ## Automatic syntactic sugar for all ZeroVariateGenerators and UnivariateGenerators. +## see https://discourse.julialang.org/t/how-to-dispatch-on-a-type-alias/106476/38?u=lrnv function generatorof(::Type{S}) where {S <: ArchimedeanCopula} S2 = hasproperty(S,:body) ? S.body : S S3 = hasproperty(S2, :body) ? S2.body : S2 From d59e173b1307952d91e030c9f0c824046432b7b3 Mon Sep 17 00:00:00 2001 From: Oskar Laverny Date: Wed, 22 Nov 2023 09:25:09 +0100 Subject: [PATCH 4/4] Add better error handling --- src/ArchimedeanCopula.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ArchimedeanCopula.jl b/src/ArchimedeanCopula.jl index d00110ab..aae18828 100644 --- a/src/ArchimedeanCopula.jl +++ b/src/ArchimedeanCopula.jl @@ -161,7 +161,11 @@ end function generatorof(::Type{S}) where {S <: ArchimedeanCopula} S2 = hasproperty(S,:body) ? S.body : S S3 = hasproperty(S2, :body) ? S2.body : S2 - S3.parameters[2].name.wrapper + try + return S3.parameters[2].name.wrapper + catch e + @error "There is no generator type associated with the archimedean type $S" + end end for T in InteractiveUtils.subtypes(ZeroVariateGenerator) G = Symbol(last(split(string(T),'.'))) @@ -200,8 +204,6 @@ function Distributions._rand!(rng::Distributions.AbstractRNG, ::ArchimedeanCopul x[1] = rand(rng) x[2] = 1-x[1] end - - function Distributions._rand!(rng::Distributions.AbstractRNG, C::ArchimedeanCopula{d,IndependentGenerator}, A::DenseMatrix{T}) where {T<:Real, d} Random.rand!(rng,A) return A