From 7ee927908ecdbbf905489a6964a067409575a58b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 28 Jan 2020 10:50:00 +0100 Subject: [PATCH 01/23] Removed transform fiels from all base kernels --- src/KernelFunctions.jl | 2 +- src/generic.jl | 22 +++-- src/kernels/constant.jl | 41 ++++---- src/kernels/exponential.jl | 36 ++----- src/kernels/exponentiated.jl | 9 +- src/kernels/kernelproduct.jl | 4 +- src/kernels/kernelsum.jl | 9 +- src/kernels/matern.jl | 30 ++---- src/kernels/polynomial.jl | 43 +++----- src/kernels/rationalquad.jl | 53 +++------- test/test_constructors.jl | 2 + test/test_custom.jl | 5 +- test/test_kernels.jl | 184 +++++++++++++++++------------------ 13 files changed, 186 insertions(+), 254 deletions(-) diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 5d74e2f86..6dd25919e 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -27,7 +27,7 @@ const defaultobs = 2 Abstract type defining a slice-wise transformation on an input matrix """ abstract type Transform end -abstract type Kernel{Tr<:Transform} end +abstract type Kernel end include("utils.jl") include("distances/dotproduct.jl") diff --git a/src/generic.jl b/src/generic.jl index 4154cd46c..5dbf1472a 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -1,5 +1,4 @@ -@inline metric(κ::Kernel) = κ.metric - +@inline transform(::Kernel) = IdentityTransform() ## Allows to iterate over kernels Base.length(::Kernel) = 1 Base.iterate(k::Kernel) = (k,nothing) @@ -19,15 +18,24 @@ for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:Mater end ### Transform generics -@inline transform(κ::Kernel) = κ.transform @inline transform(κ::Kernel, x) = transform(transform(κ), x) @inline transform(κ::Kernel, x, obsdim::Int) = transform(transform(κ), x, obsdim) ## Constructors for kernels without parameters -for kernel in [:ExponentialKernel,:SqExponentialKernel,:Matern32Kernel,:Matern52Kernel,:ExponentiatedKernel] +# for kernel in [:ExponentialKernel,:SqExponentialKernel,:Matern32Kernel,:Matern52Kernel,:ExponentiatedKernel] +# @eval begin +# $kernel() = $kernel(IdentityTransform()) +# $kernel(ρ::Real) = $kernel(ScaleTransform(ρ)) +# $kernel(ρ::AbstractVector{<:Real}) = $kernel(ARDTransform(ρ)) +# end +# end + +for k in [:SqExponentialKernel,:ExponentialKernel,:GammaExponentialKernel] + new_k = Symbol(lowercase(string(k))) @eval begin - $kernel() = $kernel(IdentityTransform()) - $kernel(ρ::Real) = $kernel(ScaleTransform(ρ)) - $kernel(ρ::AbstractVector{<:Real}) = $kernel(ARDTransform(ρ)) + $new_k(args...) = $k(args...) + $new_k(ρ::Real,args...) = TransformedKernel($k(args...),ScaleTransform(ρ)) + $new_k(ρ::AbstractVector{<:Real},args...) = TransformedKernel($k(args...),ARDTransform(ρ)) + $new_k(t::Transform,args...) = TransformedKernel($k(args...),t) end end diff --git a/src/kernels/constant.jl b/src/kernels/constant.jl index 947de0153..25d2a362f 100644 --- a/src/kernels/constant.jl +++ b/src/kernels/constant.jl @@ -1,55 +1,50 @@ """ -ZeroKernel([tr=IdentityTransform()]) +ZeroKernel() -Create a kernel always returning zero +Create a kernel that always returning zero +``` + κ(x,y) = 0.0 +``` +The output type depends of `x` and `y` """ -struct ZeroKernel{Tr} <: Kernel{Tr} - transform::Tr -end - -ZeroKernel() = ZeroKernel(IdentityTransform()) +struct ZeroKernel <: Kernel end @inline kappa(κ::ZeroKernel, d::T) where {T<:Real} = zero(T) metric(::ZeroKernel) = Delta() """ -`WhiteKernel([tr=IdentityTransform()])` +`WhiteKernel()` ``` κ(x,y) = δ(x,y) ``` Kernel function working as an equivalent to add white noise. """ -struct WhiteKernel{Tr} <: Kernel{Tr} - transform::Tr -end - -WhiteKernel() = WhiteKernel(IdentityTransform()) +struct WhiteKernel <: Kernel end @inline kappa(κ::WhiteKernel,δₓₓ::Real) = δₓₓ metric(::WhiteKernel) = Delta() """ -`ConstantKernel([tr=IdentityTransform(),[c=1.0]])` +`ConstantKernel(c=1.0)` ``` κ(x,y) = c ``` Kernel function always returning a constant value `c` """ -struct ConstantKernel{Tr, Tc<:Real} <: Kernel{Tr} - transform::Tr +struct ConstantKernel{Tc<:Real} <: Kernel c::Tc end -params(k::ConstantKernel) = (params(k.transform),k.c) -opt_params(k::ConstantKernel) = (opt_params(k.transform),k.c) - -ConstantKernel(c::Real=1.0) = ConstantKernel(IdentityTransform(),c) +function ConstantKernel(c::Tc=1.0) where {Tc<:Real} + ConstantKernel{Tc}(c) +end -ConstantKernel(t::Tr,c::Tc=1.0) where {Tr<:Transform,Tc<:Real} = ConstantKernel{Tr,Tc}(t,c) +params(k::ConstantKernel) = (k.c) +opt_params(k::ConstantKernel) = (k.c) -@inline kappa(κ::ConstantKernel,x::Real) = κ.c +@inline kappa(κ::ConstantKernel,x::Real) = κ.c*one(x) -metric(::ConstantKernel) = Delta() +@inline metric(::ConstantKernel) = Delta() diff --git a/src/kernels/exponential.jl b/src/kernels/exponential.jl index 0302128c1..008ef2889 100644 --- a/src/kernels/exponential.jl +++ b/src/kernels/exponential.jl @@ -1,16 +1,14 @@ """ -`SqExponentialKernel([ρ=1.0])` +`SqExponentialKernel()` The squared exponential kernel is an isotropic Mercer kernel given by the formula: ``` - κ(x,y) = exp(-ρ²‖x-y‖²) + κ(x,y) = exp(-‖x-y‖²) ``` See also [`ExponentialKernel`](@ref) for a related form of the kernel or [`GammaExponentialKernel`](@ref) for a generalization. """ -struct SqExponentialKernel{Tr} <: Kernel{Tr} - transform::Tr -end +struct SqExponentialKernel <: Kernel end @inline kappa(κ::SqExponentialKernel, d²::Real) = exp(-d²) @inline iskroncompatible(::SqExponentialKernel) = true @@ -28,9 +26,7 @@ The exponential kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = exp(-ρ‖x-y‖) ``` """ -struct ExponentialKernel{Tr} <: Kernel{Tr} - transform::Tr -end +struct ExponentialKernel <: Kernel end @inline kappa(κ::ExponentialKernel, d::Real) = exp(-d) @inline iskroncompatible(::ExponentialKernel) = true @@ -46,29 +42,17 @@ The γ-exponential kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = exp(-ρ^(2γ)‖x-y‖^(2γ)) ``` """ -struct GammaExponentialKernel{Tr, Tγ<:Real} <: Kernel{Tr} - transform::Tr +struct GammaExponentialKernel{Tγ<:Real} <: Kernel γ::Tγ - function GammaExponentialKernel{Tr,Tγ}(t::Tr, γ::Tγ) where {Tr<:Transform,Tγ<:Real} - @check_args(GammaExponentialKernel, γ, γ >= zero(Tγ), "γ > 0") - return new{Tr, Tγ}(t, γ) - end -end - -params(k::GammaExponentialKernel) = (params(transform),γ) -opt_params(k::GammaExponentialKernel) = (opt_params(transform),γ) - -function GammaExponentialKernel(ρ::Real=1.0, γ::Real=2.0) - GammaExponentialKernel(ScaleTransform(ρ), γ) end -function GammaExponentialKernel(ρ::AbstractVector{<:Real}, γ::Real=2.0) - GammaExponentialKernel(ARDTransform(ρ), γ) +function GammaExponentialKernel(γ::Tγ=2.0) where {Tγ<:Real} + @check_args(GammaExponentialKernel, γ, γ >= zero(Tγ), "γ > 0") + return GammaExponentialKernel{Tγ}(γ) end -function GammaExponentialKernel(t::Tr, γ::Tγ=2.0) where {Tr<:Transform, Tγ<:Real} - GammaExponentialKernel{Tr, Tγ}(t, γ) -end +params(k::GammaExponentialKernel) = (γ) +opt_params(k::GammaExponentialKernel) = (γ) @inline kappa(κ::GammaExponentialKernel, d²::Real) = exp(-d²^κ.γ) @inline iskroncompatible(::GammaExponentialKernel) = true diff --git a/src/kernels/exponentiated.jl b/src/kernels/exponentiated.jl index 5e19ef990..ea3244ef8 100644 --- a/src/kernels/exponentiated.jl +++ b/src/kernels/exponentiated.jl @@ -5,9 +5,10 @@ The exponentiated kernel is a Mercer kernel given by: κ(x,y) = exp(ρ²xᵀy) ``` """ -struct ExponentiatedKernel{Tr} <: Kernel{Tr} - transform::Tr -end -@inline kappa(κ::ExponentiatedKernel, xᵀy::T) where {T<:Real} = exp(xᵀy) +struct ExponentiatedKernel <: Kernel end + +@inline kappa(κ::ExponentiatedKernel, xᵀy::Real) = exp(xᵀy) metric(::ExponentiatedKernel) = DotProduct() + +@inline iskroncompatible(::ExponentiatedKernel) = true diff --git a/src/kernels/kernelproduct.jl b/src/kernels/kernelproduct.jl index 3c39584ef..5e90f67c3 100644 --- a/src/kernels/kernelproduct.jl +++ b/src/kernels/kernelproduct.jl @@ -10,11 +10,11 @@ kernelmatrix(k,X) == kernelmatrix(k1,X).*kernelmatrix(k2,X) kernelmatrix(k,X) == kernelmatrix(k1*k2,X) ``` """ -struct KernelProduct{Tr} <: Kernel{Tr} +struct KernelProduct <: Kernel kernels::Vector{Kernel} end -KernelProduct(kernels::AbstractVector{<:Kernel}) = KernelProduct{Transform}(kernels) +KernelProduct(kernels::AbstractVector{<:Kernel}) = KernelProduct(kernels) params(k::KernelProduct) = params.(k.kernels) opt_params(k::KernelProduct) = opt_params.(k.kernels) diff --git a/src/kernels/kernelsum.jl b/src/kernels/kernelsum.jl index 7af88658b..209bbe59e 100644 --- a/src/kernels/kernelsum.jl +++ b/src/kernels/kernelsum.jl @@ -11,18 +11,18 @@ kernelmatrix(k,X) == kernelmatrix(k1+k2,X) kweighted = 0.5*k1 + 2.0*k2 ``` """ -struct KernelSum{Tr} <: Kernel{Tr} +struct KernelSum <: Kernel kernels::Vector{Kernel} weights::Vector{Real} - function KernelSum{Tr}(kernels::AbstractVector{<:Kernel},weights::AbstractVector{<:Real}) where {Tr} - new{Tr}(kernels,weights) + function KernelSum(kernels::AbstractVector{<:Kernel},weights::AbstractVector{<:Real}) + new(kernels,weights) end end function KernelSum(kernels::AbstractVector{<:Kernel}; weights::AbstractVector{<:Real}=ones(Float64,length(kernels))) @assert length(kernels)==length(weights) "Weights and kernel vector should be of the same length" @assert all(weights.>=0) "All weights should be positive" - KernelSum{Transform}(kernels,weights) + KernelSum(kernels,weights) end params(k::KernelSum) = (k.weights,params.(k.kernels)) @@ -33,7 +33,6 @@ Base.:+(k1::Kernel,k2::Kernel) = KernelSum([k1,k2],weights=[1.0,1.0]) Base.:+(k1::KernelSum,k2::KernelSum) = KernelSum(vcat(k1.kernels,k2.kernels),weights=vcat(k1.weights,k2.weights)) Base.:+(k::Kernel,ks::KernelSum) = KernelSum(vcat(k,ks.kernels),weights=vcat(1.0,ks.weights)) Base.:+(ks::KernelSum,k::Kernel) = KernelSum(vcat(ks.kernels,k),weights=vcat(ks.weights,1.0)) -Base.:*(w::Real,k::Kernel) = KernelSum([k],weights=[w]) #TODO add tests Base.:*(w::Real,k::KernelSum) = KernelSum(k.kernels,weights=w*k.weights) #TODO add tests diff --git a/src/kernels/matern.jl b/src/kernels/matern.jl index b9bc22988..9be2bb653 100644 --- a/src/kernels/matern.jl +++ b/src/kernels/matern.jl @@ -6,25 +6,19 @@ The matern kernel is an isotropic Mercer kernel given by the formula: ``` For `ν=n+1/2, n=0,1,2,...` it can be simplified and you should instead use [`ExponentialKernel`](@ref) for `n=0`, [`Matern32Kernel`](@ref), for `n=1`, [`Matern52Kernel`](@ref) for `n=2` and [`SqExponentialKernel`](@ref) for `n=∞`. """ -struct MaternKernel{Tr<:Transform, Tν<:Real} <: Kernel{Tr} - transform::Tr +struct MaternKernel{Tν<:Real} <: Kernel ν::Tν - function MaternKernel{Tr, Tν}(t::Tr, ν::Tν) where {Tr, Tν} - @check_args(MaternKernel, ν, ν > zero(Tν), "ν > 0") - return new{Tr, Tν}(t, ν) - end end -MaternKernel(ρ::Real=1.0, ν::Real=1.5) = MaternKernel(ScaleTransform(ρ), ν) - -MaternKernel(ρ::AbstractVector{<:Real},ν::Real=1.5) = MaternKernel(ARDTransform(ρ), ν) - -MaternKernel(t::Tr, ν::T=1.5) where {Tr<:Transform, T<:Real} = MaternKernel{Tr, T}(t, ν) +function MaternKernel(ν::Tν=1.5) where {Tν<:Real} + @check_args(MaternKernel, ν, ν > zero(Tν), "ν > 0") + return MaternKernel{Tν}(ν) +end -params(k::MaternKernel) = (params(transform(k)),k.ν) -opt_params(k::MaternKernel) = (opt_params(transform(k)),k.ν) +params(k::MaternKernel) = (k.ν) +opt_params(k::MaternKernel) = (k.ν) -@inline kappa(κ::MaternKernel, d::Real) = iszero(d) ? one(d) : exp((1.0-κ.ν)*logtwo-logabsgamma(κ.ν)[1] + κ.ν*log(sqrt(2κ.ν)*d)+log(besselk(κ.ν,sqrt(2κ.ν)*d))) +@inline kappa(κ::MaternKernel, d::Real) = iszero(d) ? one(d) : exp((one(d)-κ.ν)*logtwo-logabsgamma(κ.ν)[1] + κ.ν*log(sqrt(2κ.ν)*d)+log(besselk(κ.ν,sqrt(2κ.ν)*d))) metric(::MaternKernel) = Euclidean() @@ -35,9 +29,7 @@ The matern 3/2 kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = (1+√(3)ρ‖x-y‖)exp(-√(3)ρ‖x-y‖) ``` """ -struct Matern32Kernel{Tr} <: Kernel{Tr} - transform::Tr -end +struct Matern32Kernel <: Kernel end @inline kappa(κ::Matern32Kernel, d::Real) = (1+sqrt(3)*d)*exp(-sqrt(3)*d) @@ -50,9 +42,7 @@ The matern 5/2 kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = (1+√(5)ρ‖x-y‖ + 5ρ²‖x-y‖^2/3)exp(-√(5)ρ‖x-y‖) ``` """ -struct Matern52Kernel{Tr} <: Kernel{Tr} - transform::Tr -end +struct Matern52Kernel <: Kernel end @inline kappa(κ::Matern52Kernel, d::Real) = (1+sqrt(5)*d+5*d^2/3)*exp(-sqrt(5)*d) diff --git a/src/kernels/polynomial.jl b/src/kernels/polynomial.jl index 4c5c78feb..c84b81f5d 100644 --- a/src/kernels/polynomial.jl +++ b/src/kernels/polynomial.jl @@ -6,25 +6,18 @@ The linear kernel is a Mercer kernel given by ``` Where `c` is a real number """ -struct LinearKernel{Tr, Tc<:Real} <: Kernel{Tr} - transform::Tr +struct LinearKernel{Tc<:Real} <: Kernel c::Tc end -function LinearKernel(ρ::T=1.0, c::Real=zero(T)) where {T<:Real} - LinearKernel(ScaleTransform(ρ), c) +function LinearKernel(c::Tc=0.0) where {Tc} + LinearKernel{Tc}(c) end -function LinearKernel(ρ::AbstractVector{T}, c::Real=zero(T)) where {T<:Real} - LinearKernel(ARDTransform(ρ), c) -end - -LinearKernel(t::Transform) = LinearKernel(t, 0.0) +params(k::LinearKernel) = (k.c) +opt_params(k::LinearKernel) = (k.c) -params(k::LinearKernel) = (params(transform(k)),k.c) -opt_params(k::LinearKernel) = (opt_params(transform(k)),k.c) - -@inline kappa(κ::LinearKernel, xᵀy::T) where {T<:Real} = xᵀy + κ.c +@inline kappa(κ::LinearKernel, xᵀy::Real) = xᵀy + κ.c metric(::LinearKernel) = DotProduct() @@ -36,30 +29,18 @@ The polynomial kernel is a Mercer kernel given by ``` Where `c` is a real number, and `d` is a shape parameter bigger than 1 """ -struct PolynomialKernel{Tr,Tc<:Real,Td<:Real} <: Kernel{Tr} - transform::Tr +struct PolynomialKernel{Tc<:Real,Td<:Real} <: Kernel d::Td c::Tc - function PolynomialKernel{Tr, Tc, Td}(transform::Tr, d::Td, c::Tc) where {Tr<:Transform, Td<:Real, Tc<:Real} - @check_args(PolynomialKernel, d, d >= one(Td), "d >= 1") - return new{Tr, Td, Tc}(transform,d, c) - end -end - -function PolynomialKernel(ρ::Real=1.0, d::Td=2.0, c::Real=zero(Td)) where {Td<:Real} - PolynomialKernel(ScaleTransform(ρ), d, c) -end - -function PolynomialKernel(ρ::AbstractVector{T}, d::Real=2.0, c::Real=zero(T₁)) where {T<:Real} - PolynomialKernel(ARDTransform(ρ), d, c) end -function PolynomialKernel(t::Tr, d::Td=2.0, c::Tc=zero(eltype(Td))) where {Tr<:Transform, Td<:Real, Tc<:Real} - PolynomialKernel{Tr, Tc, Td}(t, d, c) +function PolynomialKernel(d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} + @check_args(PolynomialKernel, d, d >= one(Td), "d >= 1") + return PolynomialKernel{Td, Tc}(d, c) end -params(k::PolynomialKernel) = (params(transform(k)),k.d,k.c) -opt_params(k::PolynomialKernel) = (opt_params(transform(k)),k.d,k.c) +params(k::PolynomialKernel) = (k.d,k.c) +opt_params(k::PolynomialKernel) = (k.d,k.c) @inline kappa(κ::PolynomialKernel, xᵀy::T) where {T<:Real} = (xᵀy + κ.c)^(κ.d) diff --git a/src/kernels/rationalquad.jl b/src/kernels/rationalquad.jl index d6e3c3821..d607490e6 100644 --- a/src/kernels/rationalquad.jl +++ b/src/kernels/rationalquad.jl @@ -2,34 +2,21 @@ RationalQuadraticKernel([ρ=1.0[,α=2.0]]) The rational-quadratic kernel is an isotropic Mercer kernel given by the formula: ``` - κ(x,y)=(1+ρ²||x−y||²/α)^(-α) + κ(x,y)=(1+||x−y||²/α)^(-α) ``` where `α` is a shape parameter of the Euclidean distance. Check [`GammaRationalQuadraticKernel`](@ref) for a generalization. """ -struct RationalQuadraticKernel{Tr,Tα<:Real} <: Kernel{Tr} - transform::Tr +struct RationalQuadraticKernel{Tα<:Real} <: Kernel α::Tα - function RationalQuadraticKernel{Tr, Tα}(t::Tr, α::Tα) where {Tr, Tα} - @check_args(RationalQuadraticKernel, α, α > zero(Tα), "α > 1") - return new{Tr, Tα}(t, α) - end end -function RationalQuadraticKernel(ρ::Real=1.0, α::Real=2.0) - RationalQuadraticKernel(ScaleTransform(ρ),α) +function RationalQuadraticKernel(α::Tα=2.0) where {Tα} + @check_args(RationalQuadraticKernel, α, α > zero(Tα), "α > 1") + return RationalQuadraticKernel{Tα}(α) end -function RationalQuadraticKernel(ρ::AbstractVector{<:Real}, α::Real=2.0) - RationalQuadraticKernel(ARDTransform(ρ), α) -end - -function RationalQuadraticKernel(t::Tr, α::Tα=2.0) where {Tr<:Transform, Tα<:Real} - return RationalQuadraticKernel{Tr, Tα}(t, α) -end - - -params(k::RationalQuadraticKernel) = (params(transform(k)),k.α) -opt_params(k::RationalQuadraticKernel) = (opt_params(transform(k)),k.α) +params(k::RationalQuadraticKernel) = (k.α) +opt_params(k::RationalQuadraticKernel) = (k.α) @inline kappa(κ::RationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²/κ.α)^(-κ.α) @@ -43,31 +30,19 @@ The Gamma-rational-quadratic kernel is an isotropic Mercer kernel given by the f ``` where `α` is a shape parameter of the Euclidean distance and `γ` is another shape parameter. """ -struct GammaRationalQuadraticKernel{Tr, Tα<:Real, Tγ<:Real} <: Kernel{Tr} - transform::Tr +struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: Kernel α::Tα γ::Tγ - function GammaRationalQuadraticKernel{Tr,Tα,Tγ}(t::Tr, α::Tα, γ::Tγ) where {Tr, Tα<:Real, Tγ<:Real} - @check_args(GammaRationalQuadraticKernel, α, α > one(Tα), "α > 1") - @check_args(GammaRationalQuadraticKernel, γ, γ >= one(Tγ), "γ >= 1") - return new{Tr, Tα, Tγ}(t, α, γ) - end -end - -function GammaRationalQuadraticKernel(ρ::Real=1.0, α::Real=2.0, γ::Real=2.0) - GammaRationalQuadraticKernel(ScaleTransform(ρ), α, γ) -end - -function GammaRationalQuadraticKernel(ρ::AbstractVector{<:Real}, α::Real=2.0, γ::Real=2.0) - GammaRationalQuadraticKernel(ARDTransform(ρ),α,γ) end -function GammaRationalQuadraticKernel(t::Tr,α::Tα=2.0,γ::Tγ=2.0) where {Tr<:Transform, Tα<:Real, Tγ<:Real} - GammaRationalQuadraticKernel{Tr, Tα, Tγ}(t, α, γ) +function GammaRationalQuadraticKernel(α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} + @check_args(GammaRationalQuadraticKernel, α, α > one(Tα), "α > 1") + @check_args(GammaRationalQuadraticKernel, γ, γ >= one(Tγ), "γ >= 1") + return GammaRationalQuadraticKernel{Tα, Tγ}(α, γ) end -params(k::GammaRationalQuadraticKernel) = (params(k.transform),k.α,k.γ) -opt_params(k::GammaRationalQuadraticKernel) = (opt_params(k.transform),k.α,k.γ) +params(k::GammaRationalQuadraticKernel) = (k.α,k.γ) +opt_params(k::GammaRationalQuadraticKernel) = (k.α,k.γ) @inline kappa(κ::GammaRationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²^κ.γ/κ.α)^(-κ.α) diff --git a/test/test_constructors.jl b/test/test_constructors.jl index 8c1724e0f..17d260cc3 100644 --- a/test/test_constructors.jl +++ b/test/test_constructors.jl @@ -5,6 +5,8 @@ l = 2.0 vl = [l,l] s = ScaleTransform(l) +## Add tests for Transformed Kernel and Scaled Kernel + ## SqExponentialKernel @testset "SqExponentialKernel" begin @test KernelFunctions.metric(SqExponentialKernel(l)) == SqEuclidean() diff --git a/test/test_custom.jl b/test/test_custom.jl index cd8c8db50..4edb29a1e 100644 --- a/test/test_custom.jl +++ b/test/test_custom.jl @@ -2,11 +2,10 @@ using KernelFunctions using Test # minimal definition of a custom kernel -struct MyKernel <: Kernel{IdentityTransform} end +struct MyKernel <: Kernel end KernelFunctions.kappa(::MyKernel, d2::Real) = exp(-d2) KernelFunctions.metric(::MyKernel) = SqEuclidean() -KernelFunctions.transform(::MyKernel) = IdentityTransform() @test kappa(MyKernel(), 3) == kappa(SqExponentialKernel(), 3) @test kappa(MyKernel(), 1, 3) == kappa(SqExponentialKernel(), 1, 3) @@ -23,4 +22,4 @@ KernelFunctions.transform(::MyKernel) = IdentityTransform() @test MyKernel()(3) == SqExponentialKernel()(3) @test MyKernel()([1, 2], [3, 4]) == SqExponentialKernel()([1, 2], [3, 4]) @test MyKernel()([1 2; 3 4], [5 6; 7 8]) == SqExponentialKernel()([1 2; 3 4], [5 6; 7 8]) -@test MyKernel()([1 2; 3 4]) == SqExponentialKernel()([1 2; 3 4]) \ No newline at end of file +@test MyKernel()([1 2; 3 4]) == SqExponentialKernel()([1 2; 3 4]) diff --git a/test/test_kernels.jl b/test/test_kernels.jl index 5eb9202c0..9b5d6f5c2 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -20,9 +20,7 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @testset "ConstantKernel" begin c = 2.0 k = ConstantKernel(c) - k₂ = ConstantKernel(IdentityTransform(),c) @test eltype(k) == Any - @test kappa(k,1.5)== kappa(k₂,1.5) @test kappa(k,1.0) == c @test kappa(k,0.5) == c end @@ -32,40 +30,40 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() k = SqExponentialKernel() @test kappa(k,x) ≈ exp(-x) @test k(v1,v2) ≈ exp(-norm(v1-v2)^2) - @test kappa(SqExponentialKernel(id),x) == kappa(k,x) - l = 0.5 - k = SqExponentialKernel(l) - @test k(v1,v2) ≈ exp(-l^2*norm(v1-v2)^2) - v = rand(3) - k = SqExponentialKernel(v) - @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2))^2) + @test kappa(SqExponentialKernel(),x) == kappa(k,x) + # l = 0.5 + # k = SqExponentialKernel(l) + # @test k(v1,v2) ≈ exp(-l^2*norm(v1-v2)^2) + # v = rand(3) + # k = SqExponentialKernel(v) + # @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2))^2) end @testset "ExponentialKernel" begin k = ExponentialKernel() @test kappa(k,x) ≈ exp(-x) @test k(v1,v2) ≈ exp(-norm(v1-v2)) - @test kappa(ExponentialKernel(id),x) == kappa(k,x) - l = 0.5 - k = ExponentialKernel(l) - @test k(v1,v2) ≈ exp(-l*norm(v1-v2)) - v = rand(3) - k = ExponentialKernel(v) - @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2))) + @test kappa(ExponentialKernel(),x) == kappa(k,x) + # l = 0.5 + # k = ExponentialKernel(l) + # @test k(v1,v2) ≈ exp(-l*norm(v1-v2)) + # v = rand(3) + # k = ExponentialKernel(v) + # @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2))) end @testset "GammaExponentialKernel" begin - k = GammaExponentialKernel(1.0,2.0) + k = GammaExponentialKernel(2.0) @test kappa(k,x) ≈ exp(-(x)^(k.γ)) @test k(v1,v2) ≈ exp(-norm(v1-v2)^(2k.γ)) - @test kappa(GammaExponentialKernel(id),x) == kappa(k,x) - l = 0.5 - k = GammaExponentialKernel(l,1.5) - @test k(v1,v2) ≈ exp(-l^(3.0)*norm(v1-v2)^(3.0)) - v = rand(3) - k = GammaExponentialKernel(v,3.0) - @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2)).^6.0) + @test kappa(GammaExponentialKernel(),x) == kappa(k,x) + # l = 0.5 + # k = GammaExponentialKernel(l,1.5) + # @test k(v1,v2) ≈ exp(-l^(3.0)*norm(v1-v2)^(3.0)) + # v = rand(3) + # k = GammaExponentialKernel(v,3.0) + # @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2)).^6.0) #Coherence : - @test KernelFunctions._kernel(GammaExponentialKernel(1.0,1.0),v1,v2) ≈ KernelFunctions._kernel(SqExponentialKernel(),v1,v2) - @test KernelFunctions._kernel(GammaExponentialKernel(1.0,0.5),v1,v2) ≈ KernelFunctions._kernel(ExponentialKernel(),v1,v2) + @test KernelFunctions._kernel(GammaExponentialKernel(1.0),v1,v2) ≈ KernelFunctions._kernel(SqExponentialKernel(),v1,v2) + @test KernelFunctions._kernel(GammaExponentialKernel(0.5),v1,v2) ≈ KernelFunctions._kernel(ExponentialKernel(),v1,v2) end end @testset "Exponentiated" begin @@ -74,57 +72,57 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(k,x) ≈ exp(x) @test kappa(k,-x) ≈ exp(-x) @test k(v1,v2) ≈ exp(dot(v1,v2)) - l = 0.5 - k = ExponentiatedKernel(l) - @test k(v1,v2) ≈ exp(l^2*dot(v1,v2)) - v = rand(3) - k = ExponentiatedKernel(v) - @test k(v1,v2) ≈ exp(dot(v.*v1,v.*v2)) + # l = 0.5 + # k = ExponentiatedKernel(l) + # @test k(v1,v2) ≈ exp(l^2*dot(v1,v2)) + # v = rand(3) + # k = ExponentiatedKernel(v) + # @test k(v1,v2) ≈ exp(dot(v.*v1,v.*v2)) end end @testset "Matern" begin @testset "MaternKernel" begin ν = 2.0 - k = MaternKernel(1.0,ν) + k = MaternKernel(ν) matern(x,ν) = 2^(1-ν)/gamma(ν)*(sqrt(2ν)*x)^ν*besselk(ν,sqrt(2ν)*x) @test kappa(k,x) ≈ matern(x,ν) @test kappa(k,0.0) == 1.0 - @test kappa(MaternKernel(id,ν),x) == kappa(k,x) - l = 0.5; ν = 3.0 - k = MaternKernel(l,ν) - @test k(v1,v2) ≈ matern(l*norm(v1-v2),ν) - v = rand(3); ν = 2.1 - k = MaternKernel(v,ν) - @test k(v1,v2) ≈ matern(norm(v.*(v1-v2)),ν) + @test kappa(MaternKernel(ν),x) == kappa(k,x) + # l = 0.5; ν = 3.0 + # k = MaternKernel(l,ν) + # @test k(v1,v2) ≈ matern(l*norm(v1-v2),ν) + # v = rand(3); ν = 2.1 + # k = MaternKernel(v,ν) + # @test k(v1,v2) ≈ matern(norm(v.*(v1-v2)),ν) end @testset "Matern32Kernel" begin k = Matern32Kernel() @test kappa(k,x) ≈ (1+sqrt(3)*x)exp(-sqrt(3)*x) @test k(v1,v2) ≈ (1+sqrt(3)*norm(v1-v2))exp(-sqrt(3)*norm(v1-v2)) - @test kappa(Matern32Kernel(id),x) == kappa(k,x) - l = 0.5 - k = Matern32Kernel(l) - @test k(v1,v2) ≈ (1+l*sqrt(3)*norm(v1-v2))exp(-l*sqrt(3)*norm(v1-v2)) - v = rand(3) - k = Matern32Kernel(v) - @test k(v1,v2) ≈ (1+sqrt(3)*norm(v.*(v1-v2)))exp(-sqrt(3)*norm(v.*(v1-v2))) + @test kappa(Matern32Kernel(),x) == kappa(k,x) + # l = 0.5 + # k = Matern32Kernel(l) + # @test k(v1,v2) ≈ (1+l*sqrt(3)*norm(v1-v2))exp(-l*sqrt(3)*norm(v1-v2)) + # v = rand(3) + # k = Matern32Kernel(v) + # @test k(v1,v2) ≈ (1+sqrt(3)*norm(v.*(v1-v2)))exp(-sqrt(3)*norm(v.*(v1-v2))) end @testset "Matern52Kernel" begin k = Matern52Kernel() @test kappa(k,x) ≈ (1+sqrt(5)*x+5/3*x^2)exp(-sqrt(5)*x) @test k(v1,v2) ≈ (1+sqrt(5)*norm(v1-v2)+5/3*norm(v1-v2)^2)exp(-sqrt(5)*norm(v1-v2)) - @test kappa(Matern52Kernel(id),x) == kappa(k,x) - l = 0.5 - k = Matern52Kernel(l) - @test k(v1,v2) ≈ (1+l*sqrt(5)*norm(v1-v2)+l^2*5/3*norm(v1-v2)^2)exp(-l*sqrt(5)*norm(v1-v2)) - v = rand(3) - k = Matern52Kernel(v) - @test k(v1,v2) ≈ (1+sqrt(5)*norm(v.*(v1-v2))+5/3*norm(v.*(v1-v2))^2)exp(-sqrt(5)*norm(v.*(v1-v2))) + @test kappa(Matern52Kernel(),x) == kappa(k,x) + # l = 0.5 + # k = Matern52Kernel(l) + # @test k(v1,v2) ≈ (1+l*sqrt(5)*norm(v1-v2)+l^2*5/3*norm(v1-v2)^2)exp(-l*sqrt(5)*norm(v1-v2)) + # v = rand(3) + # k = Matern52Kernel(v) + # @test k(v1,v2) ≈ (1+sqrt(5)*norm(v.*(v1-v2))+5/3*norm(v.*(v1-v2))^2)exp(-sqrt(5)*norm(v.*(v1-v2))) end @testset "Coherence Materns" begin - @test kappa(MaternKernel(1.0,0.5),x) ≈ kappa(ExponentialKernel(),x) - @test kappa(MaternKernel(1.0,1.5),x) ≈ kappa(Matern32Kernel(),x) - @test kappa(MaternKernel(1.0,2.5),x) ≈ kappa(Matern52Kernel(),x) + @test kappa(MaternKernel(0.5),x) ≈ kappa(ExponentialKernel(),x) + @test kappa(MaternKernel(1.5),x) ≈ kappa(Matern32Kernel(),x) + @test kappa(MaternKernel(2.5),x) ≈ kappa(Matern52Kernel(),x) end end @testset "Polynomial" begin @@ -133,28 +131,28 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() k = LinearKernel() @test kappa(k,x) ≈ x @test k(v1,v2) ≈ dot(v1,v2) - @test kappa(LinearKernel(id),x) == kappa(k,x) - l = 0.5 - k = LinearKernel(l,c) - @test k(v1,v2) ≈ l^2*dot(v1,v2) + c - v = rand(3) - k = LinearKernel(v,c) - @test k(v1,v2) ≈ dot(v.*v1,v.*v2) + c + @test kappa(LinearKernel(),x) == kappa(k,x) + # l = 0.5 + # k = LinearKernel(l,c) + # @test k(v1,v2) ≈ l^2*dot(v1,v2) + c + # v = rand(3) + # k = LinearKernel(v,c) + # @test k(v1,v2) ≈ dot(v.*v1,v.*v2) + c end @testset "PolynomialKernel" begin k = PolynomialKernel() @test kappa(k,x) ≈ x^2 @test k(v1,v2) ≈ dot(v1,v2)^2 - @test kappa(PolynomialKernel(id),x) == kappa(k,x) - d = 3.0 - l = 0.5 - k = PolynomialKernel(l,d,c) - @test k(v1,v2) ≈ (l^2*dot(v1,v2) + c)^d - v = rand(3) - k = PolynomialKernel(v,d,c) - @test k(v1,v2) ≈ (dot(v.*v1,v.*v2) + c)^d + @test kappa(PolynomialKernel(),x) == kappa(k,x) + # d = 3.0 + # l = 0.5 + # k = PolynomialKernel(l,d,c) + # @test k(v1,v2) ≈ (l^2*dot(v1,v2) + c)^d + # v = rand(3) + # k = PolynomialKernel(v,d,c) + # @test k(v1,v2) ≈ (dot(v.*v1,v.*v2) + c)^d #Coherence test - @test kappa(PolynomialKernel(1.0,1.0,c),x) ≈ kappa(LinearKernel(1.0,c),x) + @test kappa(PolynomialKernel(1.0,c),x) ≈ kappa(LinearKernel(c),x) end end @testset "RationalQuadratic" begin @@ -162,30 +160,30 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() k = RationalQuadraticKernel() @test kappa(k,x) ≈ (1.0+x/2.0)^-2 @test k(v1,v2) ≈ (1.0+norm(v1-v2)^2/2.0)^-2 - @test kappa(RationalQuadraticKernel(id),x) == kappa(k,x) - l = 0.5 - a = 1.0 + rand() - k = RationalQuadraticKernel(l,a) - @test k(v1,v2) ≈ (1.0+l^2*norm(v1-v2)^2/a)^-a - v = rand(3) - k = RationalQuadraticKernel(v,a) - @test k(v1,v2) ≈ (1.0+norm(v.*(v1-v2))^2/a)^-a + @test kappa(RationalQuadraticKernel(),x) == kappa(k,x) + # l = 0.5 + # a = 1.0 + rand() + # k = RationalQuadraticKernel(l,a) + # @test k(v1,v2) ≈ (1.0+l^2*norm(v1-v2)^2/a)^-a + # v = rand(3) + # k = RationalQuadraticKernel(v,a) + # @test k(v1,v2) ≈ (1.0+norm(v.*(v1-v2))^2/a)^-a end @testset "GammaRationalQuadraticKernel" begin k = GammaRationalQuadraticKernel() @test kappa(k,x) ≈ (1.0+x^2.0/2.0)^-2 @test k(v1,v2) ≈ (1.0+norm(v1-v2)^4.0/2.0)^-2 - @test kappa(GammaRationalQuadraticKernel(id),x) == kappa(k,x) - l = 0.5 + @test kappa(GammaRationalQuadraticKernel(),x) == kappa(k,x) + # l = 0.5 a = 1.0 + rand() - g = 4.0 - k = GammaRationalQuadraticKernel(l,a,g) - @test k(v1,v2) ≈ (1.0+(l^2g)*norm(v1-v2)^(2g)/a)^-a - v = rand(3) - k = GammaRationalQuadraticKernel(v,a,g) - @test k(v1,v2) ≈ (1.0+(norm(v.*(v1-v2))^(2g))/a)^-a + # g = 4.0 + # k = GammaRationalQuadraticKernel(l,a,g) + # @test k(v1,v2) ≈ (1.0+(l^2g)*norm(v1-v2)^(2g)/a)^-a + # v = rand(3) + # k = GammaRationalQuadraticKernel(v,a,g) + # @test k(v1,v2) ≈ (1.0+(norm(v.*(v1-v2))^(2g))/a)^-a #Coherence test - @test kappa(GammaRationalQuadraticKernel(1.0,a,1.0),x) ≈ kappa(RationalQuadraticKernel(1.0,a),x) + @test kappa(GammaRationalQuadraticKernel(a,1.0),x) ≈ kappa(RationalQuadraticKernel(a),x) end end @testset "KernelCombinations" begin @@ -196,9 +194,9 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() k = k1 + k2 @test KernelFunctions.metric(k) == [KernelFunctions.DotProduct(),KernelFunctions.SqEuclidean()] @test length(k) == 2 - @test transform(k) == [transform(k1),transform(k2)] - @test transform(k,X) == [transform(k1,X),transform(k2,X)] - @test transform(k,X,1) == [transform(k1,X,1),transform(k2,X,1)] + # @test transform(k) == [transform(k1),transform(k2)] + # @test transform(k,X) == [transform(k1,X),transform(k2,X)] + # @test transform(k,X,1) == [transform(k1,X,1),transform(k2,X,1)] end @testset "KernelProduct" begin From ad53f32c2abd702b6f7af84de8c5592aa09b1644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 28 Jan 2020 10:50:13 +0100 Subject: [PATCH 02/23] Added ScaledKernel and TransformedKernel --- src/KernelFunctions.jl | 2 ++ src/kernels/scaledkernel.jl | 20 ++++++++++++++++++++ src/kernels/transformedkernel.jl | 13 +++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 src/kernels/scaledkernel.jl create mode 100644 src/kernels/transformedkernel.jl diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 6dd25919e..a7ee982d4 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -39,6 +39,8 @@ for k in ["exponential","matern","polynomial","constant","rationalquad","exponen end include("matrix/kernelmatrix.jl") include("matrix/kernelpdmat.jl") +include("kernels/scaledkernel.jl") +include("kernels/transformedkernel.jl") include("kernels/kernelsum.jl") include("kernels/kernelproduct.jl") diff --git a/src/kernels/scaledkernel.jl b/src/kernels/scaledkernel.jl new file mode 100644 index 000000000..ae57245e8 --- /dev/null +++ b/src/kernels/scaledkernel.jl @@ -0,0 +1,20 @@ +struct ScaledKernel{Tk<:Kernel,Tσ<:Real} <: Kernel + kernel::Tk + σ::Vector{Tσ} +end + +function ScaledKernel(kernel::Tk,σ::Tσ=1.0) where {Tk<:Kernel,Tσ<:Real} + @check_args(ScaledKernel, σ, σ > zero(Tσ), "σ > 0") + ScaledKernel{Tk,Tσ)}(kernel,[σ]) +end + +@inline transform(k::ScaledKernel) = transform(k.kernel) + +@inline kappa(k::ScaledKernel, x) = first(k.σ)*kappa(k.kernel, x) + +@inline metric(k::ScaledKernel) = metric(k.kernel) + +params(k::ScaledKernel) = (k.σ,params(k.kernel)) +opt_params(k::ScaledKernel) = (k.σ,opt_params(k.kernel)) + +Base.:*(w::Real,k::Kernel) = ScaledKernel(k,w) diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl new file mode 100644 index 000000000..368efc67d --- /dev/null +++ b/src/kernels/transformedkernel.jl @@ -0,0 +1,13 @@ +struct TransformedKernel{Tk<:Kernel,Tr<:Transform} <: Kernel + kernel::Tk + transform::Tr +end + +@inline transform(k::TransformedKernel) = k.transform + +@inline kappa(k::TransformedKernel, x) = kappa(k.kernel, x) + +@inline metric(k::TransformedKernel) = metric(k.kernel) + +params(k::TransformedKernel) = (params(k.transform),params(k.kernel)) +opt_params(k::TransformedKernel) = (opt_params(k.transform),opt_params(k.kernel)) From 836265feae68891ea2ac6f9a461557d6efea5611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 28 Jan 2020 11:01:16 +0100 Subject: [PATCH 03/23] Fixing all tests --- test/test_constructors.jl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/test_constructors.jl b/test/test_constructors.jl index 17d260cc3..8aa234734 100644 --- a/test/test_constructors.jl +++ b/test/test_constructors.jl @@ -9,24 +9,24 @@ s = ScaleTransform(l) ## SqExponentialKernel @testset "SqExponentialKernel" begin - @test KernelFunctions.metric(SqExponentialKernel(l)) == SqEuclidean() - @test isequal(transform(SqExponentialKernel(l)),s) - @test KernelFunctions.transform(SqExponentialKernel(vl)) == ARDTransform(vl) - @test isequal(KernelFunctions.transform(SqExponentialKernel(s)),s) + @test KernelFunctions.metric(SqExponentialKernel()) == SqEuclidean() + # @test isequal(transform(SqExponentialKernel(l)),s) + # @test KernelFunctions.transform(SqExponentialKernel(vl)) == ARDTransform(vl) + # @test isequal(KernelFunctions.transform(SqExponentialKernel(s)),s) end ## MaternKernel @testset "MaternKernel" begin - @test KernelFunctions.metric(MaternKernel(l)) == Euclidean() - @test KernelFunctions.metric(Matern32Kernel(l)) == Euclidean() - @test KernelFunctions.metric(Matern52Kernel(l)) == Euclidean() - @test isequal(KernelFunctions.transform(MaternKernel(l)),s) - @test isequal(KernelFunctions.transform(Matern32Kernel(l)),s) - @test isequal(KernelFunctions.transform(Matern52Kernel(l)),s) - @test KernelFunctions.transform(MaternKernel(vl)) == ARDTransform(vl) - @test KernelFunctions.transform(Matern32Kernel(vl)) == ARDTransform(vl) - @test KernelFunctions.transform(Matern52Kernel(vl)) == ARDTransform(vl) - @test KernelFunctions.transform(MaternKernel(s)) == s - @test KernelFunctions.transform(Matern32Kernel(s)) == s - @test KernelFunctions.transform(Matern52Kernel(s)) == s + @test KernelFunctions.metric(MaternKernel()) == Euclidean() + @test KernelFunctions.metric(Matern32Kernel()) == Euclidean() + @test KernelFunctions.metric(Matern52Kernel()) == Euclidean() + # @test isequal(KernelFunctions.transform(MaternKernel(l)),s) + # @test isequal(KernelFunctions.transform(Matern32Kernel(l)),s) + # @test isequal(KernelFunctions.transform(Matern52Kernel(l)),s) + # @test KernelFunctions.transform(MaternKernel(vl)) == ARDTransform(vl) + # @test KernelFunctions.transform(Matern32Kernel(vl)) == ARDTransform(vl) + # @test KernelFunctions.transform(Matern52Kernel(vl)) == ARDTransform(vl) + # @test KernelFunctions.transform(MaternKernel(s)) == s + # @test KernelFunctions.transform(Matern32Kernel(s)) == s + # @test KernelFunctions.transform(Matern52Kernel(s)) == s end From d1e9efc50ec0f70a51392ada918287678c28f6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 28 Jan 2020 15:09:58 +0100 Subject: [PATCH 04/23] Fixed typo --- src/kernels/scaledkernel.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kernels/scaledkernel.jl b/src/kernels/scaledkernel.jl index ae57245e8..78bb7fcb3 100644 --- a/src/kernels/scaledkernel.jl +++ b/src/kernels/scaledkernel.jl @@ -5,7 +5,7 @@ end function ScaledKernel(kernel::Tk,σ::Tσ=1.0) where {Tk<:Kernel,Tσ<:Real} @check_args(ScaledKernel, σ, σ > zero(Tσ), "σ > 0") - ScaledKernel{Tk,Tσ)}(kernel,[σ]) + ScaledKernel{Tk,Tσ}(kernel,[σ]) end @inline transform(k::ScaledKernel) = transform(k.kernel) From 0e629b207b43c16f7b29df55acc536ad59c5567d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 28 Jan 2020 15:14:24 +0100 Subject: [PATCH 05/23] Making constructors uniform --- src/kernels/constant.jl | 7 +++---- src/kernels/exponential.jl | 9 ++++----- src/kernels/matern.jl | 9 ++++----- src/kernels/polynomial.jl | 18 +++++++++--------- src/kernels/rationalquad.jl | 20 +++++++++----------- test/test_constructors.jl | 33 ++++++++++++++++++++++++++++++++- 6 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/kernels/constant.jl b/src/kernels/constant.jl index 25d2a362f..9649340d0 100644 --- a/src/kernels/constant.jl +++ b/src/kernels/constant.jl @@ -36,10 +36,9 @@ Kernel function always returning a constant value `c` """ struct ConstantKernel{Tc<:Real} <: Kernel c::Tc -end - -function ConstantKernel(c::Tc=1.0) where {Tc<:Real} - ConstantKernel{Tc}(c) + function ConstantKernel(c::T=1.0) where {T<:Real} + new{T}(c) + end end params(k::ConstantKernel) = (k.c) diff --git a/src/kernels/exponential.jl b/src/kernels/exponential.jl index 008ef2889..27a4ec273 100644 --- a/src/kernels/exponential.jl +++ b/src/kernels/exponential.jl @@ -44,11 +44,10 @@ The γ-exponential kernel is an isotropic Mercer kernel given by the formula: """ struct GammaExponentialKernel{Tγ<:Real} <: Kernel γ::Tγ -end - -function GammaExponentialKernel(γ::Tγ=2.0) where {Tγ<:Real} - @check_args(GammaExponentialKernel, γ, γ >= zero(Tγ), "γ > 0") - return GammaExponentialKernel{Tγ}(γ) + function GammaExponentialKernel(γ::T=2.0) where {T<:Real} + @check_args(GammaExponentialKernel, γ, γ >= zero(T), "γ > 0") + return new{T}(γ) + end end params(k::GammaExponentialKernel) = (γ) diff --git a/src/kernels/matern.jl b/src/kernels/matern.jl index 9be2bb653..f3471b602 100644 --- a/src/kernels/matern.jl +++ b/src/kernels/matern.jl @@ -8,11 +8,10 @@ For `ν=n+1/2, n=0,1,2,...` it can be simplified and you should instead use [`Ex """ struct MaternKernel{Tν<:Real} <: Kernel ν::Tν -end - -function MaternKernel(ν::Tν=1.5) where {Tν<:Real} - @check_args(MaternKernel, ν, ν > zero(Tν), "ν > 0") - return MaternKernel{Tν}(ν) + function MaternKernel(ν::T=1.5) where {T<:Real} + @check_args(MaternKernel, ν, ν > zero(T), "ν > 0") + return new{T}(ν) + end end params(k::MaternKernel) = (k.ν) diff --git a/src/kernels/polynomial.jl b/src/kernels/polynomial.jl index c84b81f5d..b0677eea8 100644 --- a/src/kernels/polynomial.jl +++ b/src/kernels/polynomial.jl @@ -8,11 +8,12 @@ Where `c` is a real number """ struct LinearKernel{Tc<:Real} <: Kernel c::Tc + function LinearKernel(c::T=0.0) where {T} + new{T}(c) + end end -function LinearKernel(c::Tc=0.0) where {Tc} - LinearKernel{Tc}(c) -end + params(k::LinearKernel) = (k.c) opt_params(k::LinearKernel) = (k.c) @@ -29,14 +30,13 @@ The polynomial kernel is a Mercer kernel given by ``` Where `c` is a real number, and `d` is a shape parameter bigger than 1 """ -struct PolynomialKernel{Tc<:Real,Td<:Real} <: Kernel +struct PolynomialKernel{Td<:Real,Tc<:Real} <: Kernel d::Td c::Tc -end - -function PolynomialKernel(d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} - @check_args(PolynomialKernel, d, d >= one(Td), "d >= 1") - return PolynomialKernel{Td, Tc}(d, c) + function PolynomialKernel(d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} + @check_args(PolynomialKernel, d, d >= one(Td), "d >= 1") + return new{Td, Tc}(d, c) + end end params(k::PolynomialKernel) = (k.d,k.c) diff --git a/src/kernels/rationalquad.jl b/src/kernels/rationalquad.jl index d607490e6..e1c048a77 100644 --- a/src/kernels/rationalquad.jl +++ b/src/kernels/rationalquad.jl @@ -8,11 +8,10 @@ where `α` is a shape parameter of the Euclidean distance. Check [`GammaRational """ struct RationalQuadraticKernel{Tα<:Real} <: Kernel α::Tα -end - -function RationalQuadraticKernel(α::Tα=2.0) where {Tα} - @check_args(RationalQuadraticKernel, α, α > zero(Tα), "α > 1") - return RationalQuadraticKernel{Tα}(α) + function RationalQuadraticKernel(α::T=2.0) where {T} + @check_args(RationalQuadraticKernel, α, α > zero(T), "α > 1") + return new{T}(α) + end end params(k::RationalQuadraticKernel) = (k.α) @@ -33,12 +32,11 @@ where `α` is a shape parameter of the Euclidean distance and `γ` is another sh struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: Kernel α::Tα γ::Tγ -end - -function GammaRationalQuadraticKernel(α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} - @check_args(GammaRationalQuadraticKernel, α, α > one(Tα), "α > 1") - @check_args(GammaRationalQuadraticKernel, γ, γ >= one(Tγ), "γ >= 1") - return GammaRationalQuadraticKernel{Tα, Tγ}(α, γ) + function GammaRationalQuadraticKernel(α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} + @check_args(GammaRationalQuadraticKernel, α, α > one(Tα), "α > 1") + @check_args(GammaRationalQuadraticKernel, γ, γ >= one(Tγ), "γ >= 1") + return new{Tα, Tγ}(α, γ) + end end params(k::GammaRationalQuadraticKernel) = (k.α,k.γ) diff --git a/test/test_constructors.jl b/test/test_constructors.jl index 8aa234734..7eea7734e 100644 --- a/test/test_constructors.jl +++ b/test/test_constructors.jl @@ -8,8 +8,11 @@ s = ScaleTransform(l) ## Add tests for Transformed Kernel and Scaled Kernel ## SqExponentialKernel -@testset "SqExponentialKernel" begin +@testset "Exponential" begin + @test KernelFunctions.metric(ExponentialKernel()) == Euclidean() @test KernelFunctions.metric(SqExponentialKernel()) == SqEuclidean() + @test KernelFunctions.metric(GammaExponentialKernel()) == SqEuclidean() + @test KernelFunctions.metric(GammaExponentialKernel(2.0)) == SqEuclidean() # @test isequal(transform(SqExponentialKernel(l)),s) # @test KernelFunctions.transform(SqExponentialKernel(vl)) == ARDTransform(vl) # @test isequal(KernelFunctions.transform(SqExponentialKernel(s)),s) @@ -18,6 +21,7 @@ end ## MaternKernel @testset "MaternKernel" begin @test KernelFunctions.metric(MaternKernel()) == Euclidean() + @test KernelFunctions.metric(MaternKernel(2.0)) == Euclidean() @test KernelFunctions.metric(Matern32Kernel()) == Euclidean() @test KernelFunctions.metric(Matern52Kernel()) == Euclidean() # @test isequal(KernelFunctions.transform(MaternKernel(l)),s) @@ -30,3 +34,30 @@ end # @test KernelFunctions.transform(Matern32Kernel(s)) == s # @test KernelFunctions.transform(Matern52Kernel(s)) == s end + +@testset "Exponentiated" begin + @test KernelFunctions.metric(ExponentiatedKernel()) == KernelFunctions.DotProduct() +end + +@testset "Constant" begin + @test KernelFunctions.metric(ConstantKernel()) == KernelFunctions.Delta() + @test KernelFunctions.metric(ConstantKernel(2.0)) == KernelFunctions.Delta() + @test KernelFunctions.metric(WhiteKernel()) == KernelFunctions.Delta() + @test KernelFunctions.metric(ZeroKernel()) == KernelFunctions.Delta() +end + +@testset "Polynomial" begin + @test KernelFunctions.metric(LinearKernel()) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(LinearKernel(2.0)) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(PolynomialKernel()) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(PolynomialKernel(3.0)) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(PolynomialKernel(3.0,2.0)) == KernelFunctions.DotProduct() +end + +@testset "RationalQuadratic" begin + @test KernelFunctions.metric(RationalQuadraticKernel()) == SqEuclidean() + @test KernelFunctions.metric(RationalQuadraticKernel(2.0)) == SqEuclidean() + @test KernelFunctions.metric(GammaRationalQuadraticKernel()) == SqEuclidean() + @test KernelFunctions.metric(GammaRationalQuadraticKernel(2.0)) == SqEuclidean() + @test KernelFunctions.metric(GammaRationalQuadraticKernel(2.0,3.0)) == SqEuclidean() +end From af12fe84160e8b433c515c183ed70235073624a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Wed, 29 Jan 2020 11:20:26 +0100 Subject: [PATCH 06/23] Removed transform for base kernels and rewrote the generic for kappa --- src/generic.jl | 8 ++------ src/kernels/transformedkernel.jl | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 5dbf1472a..550b3a4f5 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -1,11 +1,11 @@ -@inline transform(::Kernel) = IdentityTransform() ## Allows to iterate over kernels Base.length(::Kernel) = 1 Base.iterate(k::Kernel) = (k,nothing) Base.iterate(k::Kernel, ::Any) = nothing # default fallback for evaluating a kernel with two arguments (such as vectors etc) -kappa(κ::Kernel, x, y) = kappa(κ, evaluate(metric(κ), transform(κ, x), transform(κ, y))) +kappa(κ::Kernel, x, y) = kappa(κ, evaluate(metric(κ), x, y)) +kappa(κ::TransformedKernel, x, x) = kappa(κ.kernel, κ.transform(x), κ.transform(y)) ### Syntactic sugar for creating matrices and using kernel functions for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:MaternKernel,:Matern32Kernel,:Matern52Kernel,:LinearKernel,:PolynomialKernel,:ExponentiatedKernel,:ZeroKernel,:WhiteKernel,:ConstantKernel,:RationalQuadraticKernel,:GammaRationalQuadraticKernel] @@ -17,10 +17,6 @@ for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:Mater end end -### Transform generics -@inline transform(κ::Kernel, x) = transform(transform(κ), x) -@inline transform(κ::Kernel, x, obsdim::Int) = transform(transform(κ), x, obsdim) - ## Constructors for kernels without parameters # for kernel in [:ExponentialKernel,:SqExponentialKernel,:Matern32Kernel,:Matern52Kernel,:ExponentiatedKernel] # @eval begin diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl index 368efc67d..df790fb1c 100644 --- a/src/kernels/transformedkernel.jl +++ b/src/kernels/transformedkernel.jl @@ -3,7 +3,7 @@ struct TransformedKernel{Tk<:Kernel,Tr<:Transform} <: Kernel transform::Tr end -@inline transform(k::TransformedKernel) = k.transform +@inline transform(κ::Kernel,t::Transform) = TransformedKernel(κ,t) @inline kappa(k::TransformedKernel, x) = kappa(k.kernel, x) From 8cbc30e159ed7277636e4207e20b68e6b43cbd9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Wed, 29 Jan 2020 11:20:37 +0100 Subject: [PATCH 07/23] Esthetic corrections --- src/generic.jl | 4 ++-- src/kernels/constant.jl | 4 ++-- src/kernels/exponential.jl | 4 ++-- src/kernels/matern.jl | 4 ++-- src/kernels/polynomial.jl | 4 ++-- src/kernels/rationalquad.jl | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 550b3a4f5..0fe74dc45 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -12,8 +12,8 @@ for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:Mater @eval begin @inline (κ::$k)(d::Real) = kappa(κ,d) #TODO Add test @inline (κ::$k)(x::AbstractVector{<:Real}, y::AbstractVector{<:Real}) = kappa(κ, x, y) - @inline (κ::$k)(X::AbstractMatrix{T},Y::AbstractMatrix{T};obsdim::Integer=defaultobs) where {T} = kernelmatrix(κ,X,Y,obsdim=obsdim) - @inline (κ::$k)(X::AbstractMatrix{T};obsdim::Integer=defaultobs) where {T} = kernelmatrix(κ,X,obsdim=obsdim) + @inline (κ::$k)(X::AbstractMatrix{T}, Y::AbstractMatrix{T}; obsdim::Integer=defaultobs) where {T} = kernelmatrix(κ, X, Y, obsdim=obsdim) + @inline (κ::$k)(X::AbstractMatrix{T}; obsdim::Integer=defaultobs) where {T} = kernelmatrix(κ, X, obsdim=obsdim) end end diff --git a/src/kernels/constant.jl b/src/kernels/constant.jl index 9649340d0..cbe11ed36 100644 --- a/src/kernels/constant.jl +++ b/src/kernels/constant.jl @@ -41,8 +41,8 @@ struct ConstantKernel{Tc<:Real} <: Kernel end end -params(k::ConstantKernel) = (k.c) -opt_params(k::ConstantKernel) = (k.c) +params(k::ConstantKernel) = (k.c,) +opt_params(k::ConstantKernel) = (k.c,) @inline kappa(κ::ConstantKernel,x::Real) = κ.c*one(x) diff --git a/src/kernels/exponential.jl b/src/kernels/exponential.jl index 27a4ec273..37f8694f9 100644 --- a/src/kernels/exponential.jl +++ b/src/kernels/exponential.jl @@ -50,8 +50,8 @@ struct GammaExponentialKernel{Tγ<:Real} <: Kernel end end -params(k::GammaExponentialKernel) = (γ) -opt_params(k::GammaExponentialKernel) = (γ) +params(k::GammaExponentialKernel) = (γ,) +opt_params(k::GammaExponentialKernel) = (γ,) @inline kappa(κ::GammaExponentialKernel, d²::Real) = exp(-d²^κ.γ) @inline iskroncompatible(::GammaExponentialKernel) = true diff --git a/src/kernels/matern.jl b/src/kernels/matern.jl index f3471b602..b5cb3254e 100644 --- a/src/kernels/matern.jl +++ b/src/kernels/matern.jl @@ -14,8 +14,8 @@ struct MaternKernel{Tν<:Real} <: Kernel end end -params(k::MaternKernel) = (k.ν) -opt_params(k::MaternKernel) = (k.ν) +params(k::MaternKernel) = (k.ν,) +opt_params(k::MaternKernel) = (k.ν,) @inline kappa(κ::MaternKernel, d::Real) = iszero(d) ? one(d) : exp((one(d)-κ.ν)*logtwo-logabsgamma(κ.ν)[1] + κ.ν*log(sqrt(2κ.ν)*d)+log(besselk(κ.ν,sqrt(2κ.ν)*d))) diff --git a/src/kernels/polynomial.jl b/src/kernels/polynomial.jl index b0677eea8..ad33a1d1e 100644 --- a/src/kernels/polynomial.jl +++ b/src/kernels/polynomial.jl @@ -15,8 +15,8 @@ end -params(k::LinearKernel) = (k.c) -opt_params(k::LinearKernel) = (k.c) +params(k::LinearKernel) = (k.c,) +opt_params(k::LinearKernel) = (k.c,) @inline kappa(κ::LinearKernel, xᵀy::Real) = xᵀy + κ.c diff --git a/src/kernels/rationalquad.jl b/src/kernels/rationalquad.jl index e1c048a77..e1d537c23 100644 --- a/src/kernels/rationalquad.jl +++ b/src/kernels/rationalquad.jl @@ -14,8 +14,8 @@ struct RationalQuadraticKernel{Tα<:Real} <: Kernel end end -params(k::RationalQuadraticKernel) = (k.α) -opt_params(k::RationalQuadraticKernel) = (k.α) +params(k::RationalQuadraticKernel) = (k.α,) +opt_params(k::RationalQuadraticKernel) = (k.α,) @inline kappa(κ::RationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²/κ.α)^(-κ.α) From d5d077ccae55372cf4dcbc5d6b1c5a6526fbb931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Wed, 29 Jan 2020 11:24:18 +0100 Subject: [PATCH 08/23] Removed inner constructor kernelsum --- src/kernels/kernelsum.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/kernels/kernelsum.jl b/src/kernels/kernelsum.jl index 209bbe59e..ffb235216 100644 --- a/src/kernels/kernelsum.jl +++ b/src/kernels/kernelsum.jl @@ -14,9 +14,6 @@ kweighted = 0.5*k1 + 2.0*k2 struct KernelSum <: Kernel kernels::Vector{Kernel} weights::Vector{Real} - function KernelSum(kernels::AbstractVector{<:Kernel},weights::AbstractVector{<:Real}) - new(kernels,weights) - end end function KernelSum(kernels::AbstractVector{<:Kernel}; weights::AbstractVector{<:Real}=ones(Float64,length(kernels))) From 197f663e6ac18e61bfce832796352e6cd2c7ddbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Wed, 29 Jan 2020 11:40:01 +0100 Subject: [PATCH 09/23] Set obsdim as a keyword for transform --- src/kernels/kernelsum.jl | 2 +- src/matrix/kernelmatrix.jl | 8 ++++---- src/transform/ardtransform.jl | 4 ++-- src/transform/chaintransform.jl | 4 ++-- src/transform/functiontransform.jl | 2 +- src/transform/lowranktransform.jl | 4 ++-- src/transform/scaletransform.jl | 2 +- src/transform/selecttransform.jl | 4 ++-- src/transform/transform.jl | 2 +- test/test_transform.jl | 18 +++++++++--------- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/kernels/kernelsum.jl b/src/kernels/kernelsum.jl index ffb235216..14486d701 100644 --- a/src/kernels/kernelsum.jl +++ b/src/kernels/kernelsum.jl @@ -37,7 +37,7 @@ Base.length(k::KernelSum) = length(k.kernels) metric(k::KernelSum) = metric.(k.kernels) transform(k::KernelSum) = transform.(k.kernels) transform(k::KernelSum,x::AbstractVecOrMat) = transform.(k.kernels,[x]) -transform(k::KernelSum,x::AbstractVecOrMat,obsdim::Int) = transform.(k.kernels,[x],obsdim) +transform(k::KernelSum,x::AbstractVecOrMat,obsdim::Int) = transform.(k.kernels,[x],obsdim=obsdim) function kernelmatrix( κ::KernelSum, diff --git a/src/matrix/kernelmatrix.jl b/src/matrix/kernelmatrix.jl index 6c91e232c..306f69b40 100644 --- a/src/matrix/kernelmatrix.jl +++ b/src/matrix/kernelmatrix.jl @@ -18,7 +18,7 @@ function kernelmatrix!( if !check_dims(K,X,X,feature_dim(obsdim),obsdim) throw(DimensionMismatch("Dimensions of the target array K $(size(K)) are not consistent with X $(size(X))")) end - map!(x->kappa(κ,x),K,pairwise(metric(κ),transform(κ,X,obsdim),dims=obsdim)) + map!(x->kappa(κ,x),K,pairwise(metric(κ),transform(κ,X,obsdim=obsdim),dims=obsdim)) end function kernelmatrix!( @@ -32,7 +32,7 @@ function kernelmatrix!( if !check_dims(K,X,Y,feature_dim(obsdim),obsdim) throw(DimensionMismatch("Dimensions $(size(K)) of the target array K are not consistent with X ($(size(X))) and Y ($(size(Y)))")) end - map!(x->kappa(κ,x),K,pairwise(metric(κ),transform(κ,X,obsdim),transform(κ,Y,obsdim),dims=obsdim)) + map!(x->kappa(κ,x),K,pairwise(metric(κ),transform(κ,X,obsdim=obsdim),transform(κ,Y,obsdim=obsdim),dims=obsdim)) end ## Apply kernel on two reals ## @@ -75,7 +75,7 @@ function kernelmatrix( X::AbstractMatrix; obsdim::Int = defaultobs ) - K = map(x->kappa(κ,x),pairwise(metric(κ),transform(κ,X,obsdim),dims=obsdim)) + K = map(x->kappa(κ,x),pairwise(metric(κ),transform(κ,X,obsdim=obsdim),dims=obsdim)) end function kernelmatrix( @@ -91,7 +91,7 @@ function kernelmatrix( _kernelmatrix(κ,X,Y,obsdim) end -@inline _kernelmatrix(κ,X,Y,obsdim) = map(x->kappa(κ,x),pairwise(metric(κ),transform(κ,X,obsdim),transform(κ,Y,obsdim),dims=obsdim)) +@inline _kernelmatrix(κ,X,Y,obsdim) = map(x->kappa(κ,x),pairwise(metric(κ),transform(κ,X,obsdim=obsdim),transform(κ,Y,obsdim=obsdim),dims=obsdim)) """ ``` diff --git a/src/transform/ardtransform.jl b/src/transform/ardtransform.jl index 8aceba177..1288a2212 100644 --- a/src/transform/ardtransform.jl +++ b/src/transform/ardtransform.jl @@ -28,13 +28,13 @@ end params(t::ARDTransform) = t.v dim(t::ARDTransform) = length(t.v) -function transform(t::ARDTransform,X::AbstractMatrix{<:Real},obsdim::Int) +function transform(t::ARDTransform,X::AbstractMatrix{<:Real};obsdim::Int) @boundscheck if dim(t) != size(X,feature_dim(obsdim)) throw(DimensionMismatch("Array has size $(size(X,!Bool(obsdim-1)+1)) on dimension $(!Bool(obsdim-1)+1)) which does not match the length of the scale transform length , $(dim(t)).")) #TODO Add test end _transform(t,X,obsdim) end -transform(t::ARDTransform,x::AbstractVector{<:Real},obsdim::Int=defaultobs) = t.v .* x +transform(t::ARDTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) = t.v .* x _transform(t::ARDTransform,X::AbstractMatrix{<:Real},obsdim::Int=defaultobs) = obsdim == 1 ? t.v'.*X : t.v .* X Base.isequal(t::ARDTransform,t2::ARDTransform) = isequal(t.v,t2.v) diff --git a/src/transform/chaintransform.jl b/src/transform/chaintransform.jl index 6c3485d18..0365b3156 100644 --- a/src/transform/chaintransform.jl +++ b/src/transform/chaintransform.jl @@ -22,10 +22,10 @@ function ChainTransform(v::AbstractVector{<:Type{<:Transform}},θ::AbstractVecto ChainTransform(v.(θ)) end -function transform(t::ChainTransform,X::T,obsdim::Int=defaultobs) where {T} +function transform(t::ChainTransform,X::T;obsdim::Int=defaultobs) where {T} Xtr = copy(X) for tr in t.transforms - Xtr = transform(tr,Xtr,obsdim) + Xtr = transform(tr,Xtr,obsdim=obsdim) end return Xtr end diff --git a/src/transform/functiontransform.jl b/src/transform/functiontransform.jl index b69d27c99..bfd995634 100644 --- a/src/transform/functiontransform.jl +++ b/src/transform/functiontransform.jl @@ -12,7 +12,7 @@ struct FunctionTransform{F} <: Transform f::F end -transform(t::FunctionTransform,X::T,obsdim::Int=defaultobs) where {T} = mapslices(t.f,X,dims=feature_dim(obsdim)) +transform(t::FunctionTransform,X::T;obsdim::Int=defaultobs) where {T} = mapslices(t.f,X,dims=feature_dim(obsdim)) duplicate(t::FunctionTransform,f) = FunctionTransform(f) params(t::FunctionTransform) = t.f diff --git a/src/transform/lowranktransform.jl b/src/transform/lowranktransform.jl index 313f38cd6..467c02707 100644 --- a/src/transform/lowranktransform.jl +++ b/src/transform/lowranktransform.jl @@ -21,13 +21,13 @@ params(t::LowRankTransform) = t.proj Base.size(tr::LowRankTransform,i::Int) = size(tr.proj,i) Base.size(tr::LowRankTransform) = size(tr.proj) # TODO Add test -function transform(t::LowRankTransform,X::AbstractMatrix{<:Real},obsdim::Int=defaultobs) +function transform(t::LowRankTransform,X::AbstractMatrix{<:Real};obsdim::Int=defaultobs) @boundscheck size(t,2) != size(X,feature_dim(obsdim)) ? throw(DimensionMismatch("The projection matrix has size $(size(t)) and cannot be used on X with dimensions $(size(X))")) : nothing @inbounds _transform(t,X,obsdim) end -function transform(t::LowRankTransform,x::AbstractVector{<:Real},obsdim::Int=defaultobs) #TODO Add test +function transform(t::LowRankTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) #TODO Add test @assert size(t,2) == length(x) "Vector has wrong dimensions $(length(x)) compared to projection matrix" t.proj*x end diff --git a/src/transform/scaletransform.jl b/src/transform/scaletransform.jl index 9a71e6010..1384977a4 100644 --- a/src/transform/scaletransform.jl +++ b/src/transform/scaletransform.jl @@ -19,6 +19,6 @@ set!(t::ScaleTransform,ρ::Real) = t.s .= [ρ] params(t::ScaleTransform) = first(t.s) dim(str::ScaleTransform) = 1 -transform(t::ScaleTransform,x::AbstractVecOrMat,obsdim::Int=defaultobs) = first(t.s) * x +transform(t::ScaleTransform,x::AbstractVecOrMat;obsdim::Int=defaultobs) = first(t.s) * x Base.isequal(t::ScaleTransform,t2::ScaleTransform) = isequal(first(t.s),first(t2.s)) diff --git a/src/transform/selecttransform.jl b/src/transform/selecttransform.jl index 4151c6591..b426d866f 100644 --- a/src/transform/selecttransform.jl +++ b/src/transform/selecttransform.jl @@ -29,13 +29,13 @@ duplicate(t::SelectTransform,θ) = t Base.maximum(t::SelectTransform) = maximum(t.select) -function transform(t::SelectTransform,X::AbstractMatrix{<:Real},obsdim::Int=defaultobs) +function transform(t::SelectTransform,X::AbstractMatrix{<:Real};obsdim::Int=defaultobs) @boundscheck maximum(t) >= size(X,feature_dim(obsdim)) ? throw(DimensionMismatch("The highest index $(maximum(t)) is higher then the feature dimension of X : $(size(X,feature_dim(obsdim)))")) : nothing @inbounds _transform(t,X,obsdim) end -function transform(t::SelectTransform,x::AbstractVector{<:Real},obsdim::Int=defaultobs) #TODO Add test +function transform(t::SelectTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) #TODO Add test @assert maximum(t) <= length(x) "The highest index $(maximum(t)) is higher then the vector length : $(length(x))" return @inbounds view(x,t.select) end diff --git a/src/transform/transform.jl b/src/transform/transform.jl index 5c70c1302..d4544a539 100644 --- a/src/transform/transform.jl +++ b/src/transform/transform.jl @@ -26,7 +26,7 @@ struct IdentityTransform <: Transform end params(t::IdentityTransform) = nothing duplicate(t::IdentityTransform,θ) = t -transform(t::IdentityTransform, x, obsdim::Int=defaultobs) = x #TODO add test +transform(t::IdentityTransform, x; obsdim::Int=defaultobs) = x #TODO add test ### TODO Maybe defining adjoints could help but so far it's not working diff --git a/test/test_transform.jl b/test/test_transform.jl index 511b6613e..33dae25ea 100644 --- a/test/test_transform.jl +++ b/test/test_transform.jl @@ -27,13 +27,13 @@ f(x) = sin.(x) @testset "ARDTransform" begin vt1 = ARDTransform(v1) vt2 = ARDTransform(v2) - @test all(KernelFunctions.transform(vt1,X,1).==v1'.*X) - @test all(KernelFunctions.transform(vt2,X,2).==v2.*X) + @test all(KernelFunctions.transform(vt1,X,obsdim=1).==v1'.*X) + @test all(KernelFunctions.transform(vt2,X,obsdim=2).==v2.*X) end ## Test LowRankTransform @testset "LowRankTransform" begin tp = LowRankTransform(P) - @test all(KernelFunctions.transform(tp,X,2).==P*X) + @test all(KernelFunctions.transform(tp,X,obsdim=2).==P*X) @test all(KernelFunctions.transform(tp,x).==P*x) @test all(KernelFunctions.params(tp).==P) P2 = rand(5,10) @@ -43,13 +43,13 @@ f(x) = sin.(x) ## Test FunctionTransform @testset "FunctionTransform" begin tf = FunctionTransform(f) - KernelFunctions.transform(tf,X,1) - @test all(KernelFunctions.transform(tf,X,1).==f(X)) + KernelFunctions.transform(tf,X,obsdim=1) + @test all(KernelFunctions.transform(tf,X,obsdim=1).==f(X)) end ## Test SelectTransform @testset "SelectTransform" begin ts = SelectTransform(sdims) - @test all(KernelFunctions.transform(ts,X,2).==X[sdims,:]) + @test all(KernelFunctions.transform(ts,X,obsdim=2).==X[sdims,:]) @test all(KernelFunctions.transform(ts,x).==x[sdims]) @test all(KernelFunctions.params(ts).==sdims) sdims2 = [2,3,5] @@ -62,8 +62,8 @@ f(x) = sin.(x) tp = LowRankTransform(P) tf = FunctionTransform(f) tchain = ChainTransform([t,tp,tf]) - @test all(KernelFunctions.transform(tchain,X,2).==f(P*(s*X))) - @test all(KernelFunctions.transform(tchain,X,2).== - KernelFunctions.transform(tf∘tp∘t,X,2)) + @test all(KernelFunctions.transform(tchain,X,obsdim=2).==f(P*(s*X))) + @test all(KernelFunctions.transform(tchain,X,obsdim=2).== + KernelFunctions.transform(tf∘tp∘t,X,obsdim=2)) end end From e8f4e0084e321a01ba24403ae124a7fc8261959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Wed, 29 Jan 2020 11:40:18 +0100 Subject: [PATCH 10/23] Readded a transform(kernel,x) function --- src/generic.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/generic.jl b/src/generic.jl index 0fe74dc45..c33e565d5 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -5,7 +5,11 @@ Base.iterate(k::Kernel, ::Any) = nothing # default fallback for evaluating a kernel with two arguments (such as vectors etc) kappa(κ::Kernel, x, y) = kappa(κ, evaluate(metric(κ), x, y)) -kappa(κ::TransformedKernel, x, x) = kappa(κ.kernel, κ.transform(x), κ.transform(y)) +kappa(κ::TransformedKernel, x, y) = kappa(κ.kernel, κ.transform(x), κ.transform(y)) +transform(κ::Kernel,x;obsdim=defaultobs) = x +transform(κ::TransformedKernel,x;obsdim=defaultobs) = transform(κ.transform,x,obsdim=obsdim) + + ### Syntactic sugar for creating matrices and using kernel functions for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:MaternKernel,:Matern32Kernel,:Matern52Kernel,:LinearKernel,:PolynomialKernel,:ExponentiatedKernel,:ZeroKernel,:WhiteKernel,:ConstantKernel,:RationalQuadraticKernel,:GammaRationalQuadraticKernel] From 70b508f8a6f23d09dfcb490f65d1863d4395e8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 11 Feb 2020 12:34:55 +0100 Subject: [PATCH 11/23] Removed the transform function and adapted the kernelmatrix functions --- src/KernelFunctions.jl | 4 ++-- src/generic.jl | 5 +---- src/matrix/kernelmatrix.jl | 27 +++++++++++++++++++++------ src/transform/ardtransform.jl | 4 ++-- src/transform/chaintransform.jl | 4 ++-- src/transform/functiontransform.jl | 2 +- src/transform/lowranktransform.jl | 4 ++-- src/transform/scaletransform.jl | 4 ++-- src/transform/selecttransform.jl | 4 ++-- src/transform/transform.jl | 3 +-- test/test_transform.jl | 25 ++++++++++++------------- 11 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index a7ee982d4..36c99e88e 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -37,10 +37,10 @@ include("transform/transform.jl") for k in ["exponential","matern","polynomial","constant","rationalquad","exponentiated"] include(joinpath("kernels",k*".jl")) end +include("kernels/transformedkernel.jl") +include("kernels/scaledkernel.jl") include("matrix/kernelmatrix.jl") include("matrix/kernelpdmat.jl") -include("kernels/scaledkernel.jl") -include("kernels/transformedkernel.jl") include("kernels/kernelsum.jl") include("kernels/kernelproduct.jl") diff --git a/src/generic.jl b/src/generic.jl index c33e565d5..8e0414203 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -6,10 +6,6 @@ Base.iterate(k::Kernel, ::Any) = nothing # default fallback for evaluating a kernel with two arguments (such as vectors etc) kappa(κ::Kernel, x, y) = kappa(κ, evaluate(metric(κ), x, y)) kappa(κ::TransformedKernel, x, y) = kappa(κ.kernel, κ.transform(x), κ.transform(y)) -transform(κ::Kernel,x;obsdim=defaultobs) = x -transform(κ::TransformedKernel,x;obsdim=defaultobs) = transform(κ.transform,x,obsdim=obsdim) - - ### Syntactic sugar for creating matrices and using kernel functions for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:MaternKernel,:Matern32Kernel,:Matern52Kernel,:LinearKernel,:PolynomialKernel,:ExponentiatedKernel,:ZeroKernel,:WhiteKernel,:ConstantKernel,:RationalQuadraticKernel,:GammaRationalQuadraticKernel] @@ -37,5 +33,6 @@ for k in [:SqExponentialKernel,:ExponentialKernel,:GammaExponentialKernel] $new_k(ρ::Real,args...) = TransformedKernel($k(args...),ScaleTransform(ρ)) $new_k(ρ::AbstractVector{<:Real},args...) = TransformedKernel($k(args...),ARDTransform(ρ)) $new_k(t::Transform,args...) = TransformedKernel($k(args...),t) + export $new_k end end diff --git a/src/matrix/kernelmatrix.jl b/src/matrix/kernelmatrix.jl index 306f69b40..503fd4160 100644 --- a/src/matrix/kernelmatrix.jl +++ b/src/matrix/kernelmatrix.jl @@ -18,9 +18,12 @@ function kernelmatrix!( if !check_dims(K,X,X,feature_dim(obsdim),obsdim) throw(DimensionMismatch("Dimensions of the target array K $(size(K)) are not consistent with X $(size(X))")) end - map!(x->kappa(κ,x),K,pairwise(metric(κ),transform(κ,X,obsdim=obsdim),dims=obsdim)) + map!(x->kappa(κ,x),K,pairwise(metric(κ),X,dims=obsdim)) end +kernelmatrix!(K::Matrix, κ::TransformedKernel, X; obsdim::Int = defaultobs) = + kernelmatrix!(K, kernel(κ), apply(κ.transform, X, obsdim = obsdim), obsdim = obsdim) + function kernelmatrix!( K::AbstractMatrix, κ::Kernel, @@ -32,9 +35,12 @@ function kernelmatrix!( if !check_dims(K,X,Y,feature_dim(obsdim),obsdim) throw(DimensionMismatch("Dimensions $(size(K)) of the target array K are not consistent with X ($(size(X))) and Y ($(size(Y)))")) end - map!(x->kappa(κ,x),K,pairwise(metric(κ),transform(κ,X,obsdim=obsdim),transform(κ,Y,obsdim=obsdim),dims=obsdim)) + map!(x->kappa(κ,x),K,pairwise(metric(κ),X,Y,dims=obsdim)) end +kernelmatrix!(K::AbstractMatrix, κ::TransformedKernel, X, Y; obsdim::Int = defaultobs) = + kernelmatrix!(K, kernel(κ), apply(κ.transform, X, obsdim = obsdim), apply(κ.transform, Y, obsdim = obsdim), obsdim = obsdim) + ## Apply kernel on two reals ## function _kernel(κ::Kernel, x::Real, y::Real) _kernel(κ, [x], [y]) @@ -48,9 +54,12 @@ function _kernel( obsdim::Int = defaultobs ) @assert length(x) == length(y) "x and y don't have the same dimension!" - kappa(κ, evaluate(metric(κ),transform(κ,x),transform(κ,y))) + kappa(κ, evaluate(metric(κ),x,y)) end +_kernel(κ::TransformedKernel, x::AbstractVector, y::AbstractVector; obsdim::Int = defaultobs) = + _kernel(kernel(κ), apply(κ.transform, x), apply(κ.transform, y), obsdim = obsdim) + """ ``` kernelmatrix(κ::Kernel, X::Matrix ; obsdim::Int=2) @@ -74,10 +83,13 @@ function kernelmatrix( κ::Kernel, X::AbstractMatrix; obsdim::Int = defaultobs - ) - K = map(x->kappa(κ,x),pairwise(metric(κ),transform(κ,X,obsdim=obsdim),dims=obsdim)) + ) + K = map(x->kappa(κ,x),pairwise(metric(κ),X,dims=obsdim)) end +kernelmatrix(κ::TransformedKernel, X; obsdim::Int = defaultobs) = + kernelmatrix(kernel(κ), apply(κ.transform, X, obsdim = obsdim), obsdim = obsdim) + function kernelmatrix( κ::Kernel, X::AbstractMatrix, @@ -91,7 +103,10 @@ function kernelmatrix( _kernelmatrix(κ,X,Y,obsdim) end -@inline _kernelmatrix(κ,X,Y,obsdim) = map(x->kappa(κ,x),pairwise(metric(κ),transform(κ,X,obsdim=obsdim),transform(κ,Y,obsdim=obsdim),dims=obsdim)) +@inline _kernelmatrix(κ::Kernel,X,Y,obsdim) = map(x->kappa(κ,x),pairwise(metric(κ),X,Y,dims=obsdim)) + +kernelmatrix(κ::TransformedKernel, X, Y; obsdim::Int = defaultobs) = + kernelmatrix(kernel(κ), apply(κ.transform, X, obsdim = obsdim), apply(κ.transform, Y, obsdim = obsdim), obsdim = obsdim) """ ``` diff --git a/src/transform/ardtransform.jl b/src/transform/ardtransform.jl index 1288a2212..debc78967 100644 --- a/src/transform/ardtransform.jl +++ b/src/transform/ardtransform.jl @@ -28,13 +28,13 @@ end params(t::ARDTransform) = t.v dim(t::ARDTransform) = length(t.v) -function transform(t::ARDTransform,X::AbstractMatrix{<:Real};obsdim::Int) +function apply(t::ARDTransform,X::AbstractMatrix{<:Real};obsdim::Int) @boundscheck if dim(t) != size(X,feature_dim(obsdim)) throw(DimensionMismatch("Array has size $(size(X,!Bool(obsdim-1)+1)) on dimension $(!Bool(obsdim-1)+1)) which does not match the length of the scale transform length , $(dim(t)).")) #TODO Add test end _transform(t,X,obsdim) end -transform(t::ARDTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) = t.v .* x +apply(t::ARDTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) = t.v .* x _transform(t::ARDTransform,X::AbstractMatrix{<:Real},obsdim::Int=defaultobs) = obsdim == 1 ? t.v'.*X : t.v .* X Base.isequal(t::ARDTransform,t2::ARDTransform) = isequal(t.v,t2.v) diff --git a/src/transform/chaintransform.jl b/src/transform/chaintransform.jl index 0365b3156..0547f6ae1 100644 --- a/src/transform/chaintransform.jl +++ b/src/transform/chaintransform.jl @@ -22,10 +22,10 @@ function ChainTransform(v::AbstractVector{<:Type{<:Transform}},θ::AbstractVecto ChainTransform(v.(θ)) end -function transform(t::ChainTransform,X::T;obsdim::Int=defaultobs) where {T} +function apply(t::ChainTransform,X::T;obsdim::Int=defaultobs) where {T} Xtr = copy(X) for tr in t.transforms - Xtr = transform(tr,Xtr,obsdim=obsdim) + Xtr = apply(tr, Xtr, obsdim = obsdim) end return Xtr end diff --git a/src/transform/functiontransform.jl b/src/transform/functiontransform.jl index bfd995634..7f78398c3 100644 --- a/src/transform/functiontransform.jl +++ b/src/transform/functiontransform.jl @@ -12,7 +12,7 @@ struct FunctionTransform{F} <: Transform f::F end -transform(t::FunctionTransform,X::T;obsdim::Int=defaultobs) where {T} = mapslices(t.f,X,dims=feature_dim(obsdim)) +apply(t::FunctionTransform, X::T; obsdim::Int = defaultobs) where {T} = mapslices(t.f, X, dims = feature_dim(obsdim)) duplicate(t::FunctionTransform,f) = FunctionTransform(f) params(t::FunctionTransform) = t.f diff --git a/src/transform/lowranktransform.jl b/src/transform/lowranktransform.jl index 467c02707..af8541996 100644 --- a/src/transform/lowranktransform.jl +++ b/src/transform/lowranktransform.jl @@ -21,13 +21,13 @@ params(t::LowRankTransform) = t.proj Base.size(tr::LowRankTransform,i::Int) = size(tr.proj,i) Base.size(tr::LowRankTransform) = size(tr.proj) # TODO Add test -function transform(t::LowRankTransform,X::AbstractMatrix{<:Real};obsdim::Int=defaultobs) +function apply(t::LowRankTransform, X::AbstractMatrix{<:Real}; obsdim::Int = defaultobs) @boundscheck size(t,2) != size(X,feature_dim(obsdim)) ? throw(DimensionMismatch("The projection matrix has size $(size(t)) and cannot be used on X with dimensions $(size(X))")) : nothing @inbounds _transform(t,X,obsdim) end -function transform(t::LowRankTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) #TODO Add test +function apply(t::LowRankTransform, x::AbstractVector{<:Real}; obsdim::Int = defaultobs) #TODO Add test @assert size(t,2) == length(x) "Vector has wrong dimensions $(length(x)) compared to projection matrix" t.proj*x end diff --git a/src/transform/scaletransform.jl b/src/transform/scaletransform.jl index 1384977a4..3efdc6ae8 100644 --- a/src/transform/scaletransform.jl +++ b/src/transform/scaletransform.jl @@ -16,9 +16,9 @@ function ScaleTransform(s::T=1.0) where {T<:Real} end set!(t::ScaleTransform,ρ::Real) = t.s .= [ρ] -params(t::ScaleTransform) = first(t.s) +params(t::ScaleTransform) = t.s dim(str::ScaleTransform) = 1 -transform(t::ScaleTransform,x::AbstractVecOrMat;obsdim::Int=defaultobs) = first(t.s) * x +apply(t::ScaleTransform,x::AbstractVecOrMat;obsdim::Int=defaultobs) = first(t.s) * x Base.isequal(t::ScaleTransform,t2::ScaleTransform) = isequal(first(t.s),first(t2.s)) diff --git a/src/transform/selecttransform.jl b/src/transform/selecttransform.jl index b426d866f..9e8770139 100644 --- a/src/transform/selecttransform.jl +++ b/src/transform/selecttransform.jl @@ -29,13 +29,13 @@ duplicate(t::SelectTransform,θ) = t Base.maximum(t::SelectTransform) = maximum(t.select) -function transform(t::SelectTransform,X::AbstractMatrix{<:Real};obsdim::Int=defaultobs) +function apply(t::SelectTransform, X::AbstractMatrix{<:Real}; obsdim::Int = defaultobs) @boundscheck maximum(t) >= size(X,feature_dim(obsdim)) ? throw(DimensionMismatch("The highest index $(maximum(t)) is higher then the feature dimension of X : $(size(X,feature_dim(obsdim)))")) : nothing @inbounds _transform(t,X,obsdim) end -function transform(t::SelectTransform,x::AbstractVector{<:Real};obsdim::Int=defaultobs) #TODO Add test +function apply(t::SelectTransform, x::AbstractVector{<:Real}; obsdim::Int = defaultobs) #TODO Add test @assert maximum(t) <= length(x) "The highest index $(maximum(t)) is higher then the vector length : $(length(x))" return @inbounds view(x,t.select) end diff --git a/src/transform/transform.jl b/src/transform/transform.jl index d4544a539..e278adc50 100644 --- a/src/transform/transform.jl +++ b/src/transform/transform.jl @@ -1,5 +1,4 @@ export Transform, IdentityTransform, ScaleTransform, ARDTransform, LowRankTransform, FunctionTransform, ChainTransform -export transform """ ```julia @@ -26,7 +25,7 @@ struct IdentityTransform <: Transform end params(t::IdentityTransform) = nothing duplicate(t::IdentityTransform,θ) = t -transform(t::IdentityTransform, x; obsdim::Int=defaultobs) = x #TODO add test +apply(t::IdentityTransform, x; obsdim::Int=defaultobs) = x #TODO add test ### TODO Maybe defining adjoints could help but so far it's not working diff --git a/test/test_transform.jl b/test/test_transform.jl index 33dae25ea..17bc81886 100644 --- a/test/test_transform.jl +++ b/test/test_transform.jl @@ -18,7 +18,7 @@ f(x) = sin.(x) ## Test Scale Transform @testset "ScaleTransform" begin t = ScaleTransform(s) - @test all(KernelFunctions.transform(t,X).==s*X) + @test all(KernelFunctions.apply(t,X).==s*X) s2 = 2.0 KernelFunctions.set!(t,s2) @test all(t.s.==[s2]) @@ -27,14 +27,14 @@ f(x) = sin.(x) @testset "ARDTransform" begin vt1 = ARDTransform(v1) vt2 = ARDTransform(v2) - @test all(KernelFunctions.transform(vt1,X,obsdim=1).==v1'.*X) - @test all(KernelFunctions.transform(vt2,X,obsdim=2).==v2.*X) + @test all(KernelFunctions.apply(vt1,X,obsdim=1).==v1'.*X) + @test all(KernelFunctions.apply(vt2,X,obsdim=2).==v2.*X) end ## Test LowRankTransform @testset "LowRankTransform" begin tp = LowRankTransform(P) - @test all(KernelFunctions.transform(tp,X,obsdim=2).==P*X) - @test all(KernelFunctions.transform(tp,x).==P*x) + @test all(KernelFunctions.apply(tp,X,obsdim=2).==P*X) + @test all(KernelFunctions.apply(tp,x).==P*x) @test all(KernelFunctions.params(tp).==P) P2 = rand(5,10) KernelFunctions.set!(tp,P2) @@ -43,15 +43,14 @@ f(x) = sin.(x) ## Test FunctionTransform @testset "FunctionTransform" begin tf = FunctionTransform(f) - KernelFunctions.transform(tf,X,obsdim=1) - @test all(KernelFunctions.transform(tf,X,obsdim=1).==f(X)) + KernelFunctions.apply(tf,X,obsdim=1) + @test all(KernelFunctions.apply(tf,X,obsdim=1).==f(X)) end ## Test SelectTransform @testset "SelectTransform" begin ts = SelectTransform(sdims) - @test all(KernelFunctions.transform(ts,X,obsdim=2).==X[sdims,:]) - @test all(KernelFunctions.transform(ts,x).==x[sdims]) - @test all(KernelFunctions.params(ts).==sdims) + @test all(KernelFunctions.apply(ts,X,obsdim=2).==X[sdims,:]) + @test all(KernelFunctions.apply(ts,x).==x[sdims]) sdims2 = [2,3,5] KernelFunctions.set!(ts,sdims2) @test all(ts.select.==sdims2) @@ -62,8 +61,8 @@ f(x) = sin.(x) tp = LowRankTransform(P) tf = FunctionTransform(f) tchain = ChainTransform([t,tp,tf]) - @test all(KernelFunctions.transform(tchain,X,obsdim=2).==f(P*(s*X))) - @test all(KernelFunctions.transform(tchain,X,obsdim=2).== - KernelFunctions.transform(tf∘tp∘t,X,obsdim=2)) + @test all(KernelFunctions.apply(tchain,X,obsdim=2).==f(P*(s*X))) + @test all(KernelFunctions.apply(tchain,X,obsdim=2).== + KernelFunctions.apply(tf∘tp∘t,X,obsdim=2)) end end From 6559873f11e76be0eb0fcaac3c2078cc8bf861dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 11 Feb 2020 15:21:13 +0100 Subject: [PATCH 12/23] Adapting test to the new backend --- src/generic.jl | 6 ++- src/kernels/scaledkernel.jl | 2 - src/kernels/transformedkernel.jl | 12 ++++-- src/matrix/kernelmatrix.jl | 8 ++-- src/transform/ardtransform.jl | 2 +- test/test_kernelmatrix.jl | 18 +++++++++ test/test_kernels.jl | 64 +++++--------------------------- test/test_transform.jl | 7 ++++ 8 files changed, 53 insertions(+), 66 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 8e0414203..8c6f04339 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -5,7 +5,11 @@ Base.iterate(k::Kernel, ::Any) = nothing # default fallback for evaluating a kernel with two arguments (such as vectors etc) kappa(κ::Kernel, x, y) = kappa(κ, evaluate(metric(κ), x, y)) -kappa(κ::TransformedKernel, x, y) = kappa(κ.kernel, κ.transform(x), κ.transform(y)) +kappa(κ::TransformedKernel, x, y) = kappa(kernel(κ), apply(κ.transform,x), apply(κ.transform,y)) +kappa(κ::TransformedKernel{<:Kernel,<:ScaleTransform}, x, y) = kappa(κ.kernel, _scale(κ.transform, metric(κ.kernel), x, y)) +_scale(t::ScaleTransform, metric::Euclidean, x, y) = first(t.s) * evaluate(metric, x, y) +_scale(t::ScaleTransform, metric::Union{SqEuclidean,DotProduct}, x, y) = first(t.s)^2 * evaluate(metric, x, y) +_scale(t::ScaleTransform, metric, x, y) = evaluate(metric, apply(t, x), apply(t, y)) ### Syntactic sugar for creating matrices and using kernel functions for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:MaternKernel,:Matern32Kernel,:Matern52Kernel,:LinearKernel,:PolynomialKernel,:ExponentiatedKernel,:ZeroKernel,:WhiteKernel,:ConstantKernel,:RationalQuadraticKernel,:GammaRationalQuadraticKernel] diff --git a/src/kernels/scaledkernel.jl b/src/kernels/scaledkernel.jl index 78bb7fcb3..c208a30c7 100644 --- a/src/kernels/scaledkernel.jl +++ b/src/kernels/scaledkernel.jl @@ -8,8 +8,6 @@ function ScaledKernel(kernel::Tk,σ::Tσ=1.0) where {Tk<:Kernel,Tσ<:Real} ScaledKernel{Tk,Tσ}(kernel,[σ]) end -@inline transform(k::ScaledKernel) = transform(k.kernel) - @inline kappa(k::ScaledKernel, x) = first(k.σ)*kappa(k.kernel, x) @inline metric(k::ScaledKernel) = metric(k.kernel) diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl index df790fb1c..8b914acb0 100644 --- a/src/kernels/transformedkernel.jl +++ b/src/kernels/transformedkernel.jl @@ -3,11 +3,15 @@ struct TransformedKernel{Tk<:Kernel,Tr<:Transform} <: Kernel transform::Tr end +@inline kernel(κ) = κ.kernel + @inline transform(κ::Kernel,t::Transform) = TransformedKernel(κ,t) -@inline kappa(k::TransformedKernel, x) = kappa(k.kernel, x) +@inline kappa(κ::TransformedKernel, x) = kappa(κ.kernel, x) + +@inline metric(κ::TransformedKernel) = metric(κ.kernel) -@inline metric(k::TransformedKernel) = metric(k.kernel) +params(κ::TransformedKernel) = (params(κ.transform),params(κ.kernel)) +opt_params(κ::TransformedKernel) = (opt_params(κ.transform),opt_params(κ.kernel)) -params(k::TransformedKernel) = (params(k.transform),params(k.kernel)) -opt_params(k::TransformedKernel) = (opt_params(k.transform),opt_params(k.kernel)) +Base.show(io::IO,κ::TransformedKernel) = print(io,"$(κ.kernel) with $(κ.transform)") diff --git a/src/matrix/kernelmatrix.jl b/src/matrix/kernelmatrix.jl index 503fd4160..e01572164 100644 --- a/src/matrix/kernelmatrix.jl +++ b/src/matrix/kernelmatrix.jl @@ -21,7 +21,7 @@ function kernelmatrix!( map!(x->kappa(κ,x),K,pairwise(metric(κ),X,dims=obsdim)) end -kernelmatrix!(K::Matrix, κ::TransformedKernel, X; obsdim::Int = defaultobs) = +kernelmatrix!(K::Matrix, κ::TransformedKernel, X::AbstractMatrix; obsdim::Int = defaultobs) = kernelmatrix!(K, kernel(κ), apply(κ.transform, X, obsdim = obsdim), obsdim = obsdim) function kernelmatrix!( @@ -38,7 +38,7 @@ function kernelmatrix!( map!(x->kappa(κ,x),K,pairwise(metric(κ),X,Y,dims=obsdim)) end -kernelmatrix!(K::AbstractMatrix, κ::TransformedKernel, X, Y; obsdim::Int = defaultobs) = +kernelmatrix!(K::AbstractMatrix, κ::TransformedKernel, X::AbstractMatrix, Y::AbstractMatrix; obsdim::Int = defaultobs) = kernelmatrix!(K, kernel(κ), apply(κ.transform, X, obsdim = obsdim), apply(κ.transform, Y, obsdim = obsdim), obsdim = obsdim) ## Apply kernel on two reals ## @@ -87,7 +87,7 @@ function kernelmatrix( K = map(x->kappa(κ,x),pairwise(metric(κ),X,dims=obsdim)) end -kernelmatrix(κ::TransformedKernel, X; obsdim::Int = defaultobs) = +kernelmatrix(κ::TransformedKernel, X::AbstractMatrix; obsdim::Int = defaultobs) = kernelmatrix(kernel(κ), apply(κ.transform, X, obsdim = obsdim), obsdim = obsdim) function kernelmatrix( @@ -105,7 +105,7 @@ end @inline _kernelmatrix(κ::Kernel,X,Y,obsdim) = map(x->kappa(κ,x),pairwise(metric(κ),X,Y,dims=obsdim)) -kernelmatrix(κ::TransformedKernel, X, Y; obsdim::Int = defaultobs) = +kernelmatrix(κ::TransformedKernel, X::AbstractMatrix, Y::AbstractMatrix; obsdim::Int = defaultobs) = kernelmatrix(kernel(κ), apply(κ.transform, X, obsdim = obsdim), apply(κ.transform, Y, obsdim = obsdim), obsdim = obsdim) """ diff --git a/src/transform/ardtransform.jl b/src/transform/ardtransform.jl index debc78967..0f4b23e9b 100644 --- a/src/transform/ardtransform.jl +++ b/src/transform/ardtransform.jl @@ -28,7 +28,7 @@ end params(t::ARDTransform) = t.v dim(t::ARDTransform) = length(t.v) -function apply(t::ARDTransform,X::AbstractMatrix{<:Real};obsdim::Int) +function apply(t::ARDTransform,X::AbstractMatrix{<:Real};obsdim::Int = defaultobs) @boundscheck if dim(t) != size(X,feature_dim(obsdim)) throw(DimensionMismatch("Array has size $(size(X,!Bool(obsdim-1)+1)) on dimension $(!Bool(obsdim-1)+1)) which does not match the length of the scale transform length , $(dim(t)).")) #TODO Add test end diff --git a/test/test_kernelmatrix.jl b/test/test_kernelmatrix.jl index 82c955799..942f1d122 100644 --- a/test/test_kernelmatrix.jl +++ b/test/test_kernelmatrix.jl @@ -10,7 +10,9 @@ B = rand(dims...) C = rand(8,9) K = [zeros(dims[1],dims[1]),zeros(dims[2],dims[2])] Kdiag = [zeros(dims[1]),zeros(dims[2])] +s = rand() k = SqExponentialKernel() +kt = sqexponentialkernel(s) @testset "Kernel Matrix Operations" begin @testset "Inplace Kernel Matrix" begin for obsdim in [1,2] @@ -33,6 +35,22 @@ k = SqExponentialKernel() @test_throws DimensionMismatch kernelmatrix(k,A,C,obsdim=obsdim) end end + @testset "Transformed Kernel Matrix Operations" begin + @testset "Inplace Kernel Matrix" begin + for obsdim in [1,2] + @test kernelmatrix!(K[obsdim],kt,A,B,obsdim=obsdim) == kernelmatrix(k,s*A,s*B,obsdim=obsdim) + @test kernelmatrix!(K[obsdim],kt,A,obsdim=obsdim) == kernelmatrix(k,s*A,obsdim=obsdim) + @test kerneldiagmatrix!(Kdiag[obsdim],kt,A,obsdim=obsdim) == kerneldiagmatrix(k,s*A,obsdim=obsdim) + end + end + @testset "Kernel matrix" begin + for obsdim in [1,2] + @test kernelmatrix(kt,A,B,obsdim=obsdim) == kernelmatrix(k,s*A,s*B,obsdim=obsdim) + @test kernelmatrix(kt,A,obsdim=obsdim) == kernelmatrix(k,s*A,obsdim=obsdim) + @test kerneldiagmatrix(kt,A,obsdim=obsdim) == kerneldiagmatrix(k,s*A,obsdim=obsdim) + end + end + end @testset "KernelSum" begin k1 = SqExponentialKernel() k2 = LinearKernel() diff --git a/test/test_kernels.jl b/test/test_kernels.jl index 9b5d6f5c2..7b1959c54 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -72,12 +72,6 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(k,x) ≈ exp(x) @test kappa(k,-x) ≈ exp(-x) @test k(v1,v2) ≈ exp(dot(v1,v2)) - # l = 0.5 - # k = ExponentiatedKernel(l) - # @test k(v1,v2) ≈ exp(l^2*dot(v1,v2)) - # v = rand(3) - # k = ExponentiatedKernel(v) - # @test k(v1,v2) ≈ exp(dot(v.*v1,v.*v2)) end end @testset "Matern" begin @@ -88,36 +82,18 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(k,x) ≈ matern(x,ν) @test kappa(k,0.0) == 1.0 @test kappa(MaternKernel(ν),x) == kappa(k,x) - # l = 0.5; ν = 3.0 - # k = MaternKernel(l,ν) - # @test k(v1,v2) ≈ matern(l*norm(v1-v2),ν) - # v = rand(3); ν = 2.1 - # k = MaternKernel(v,ν) - # @test k(v1,v2) ≈ matern(norm(v.*(v1-v2)),ν) end @testset "Matern32Kernel" begin k = Matern32Kernel() @test kappa(k,x) ≈ (1+sqrt(3)*x)exp(-sqrt(3)*x) @test k(v1,v2) ≈ (1+sqrt(3)*norm(v1-v2))exp(-sqrt(3)*norm(v1-v2)) @test kappa(Matern32Kernel(),x) == kappa(k,x) - # l = 0.5 - # k = Matern32Kernel(l) - # @test k(v1,v2) ≈ (1+l*sqrt(3)*norm(v1-v2))exp(-l*sqrt(3)*norm(v1-v2)) - # v = rand(3) - # k = Matern32Kernel(v) - # @test k(v1,v2) ≈ (1+sqrt(3)*norm(v.*(v1-v2)))exp(-sqrt(3)*norm(v.*(v1-v2))) end @testset "Matern52Kernel" begin k = Matern52Kernel() @test kappa(k,x) ≈ (1+sqrt(5)*x+5/3*x^2)exp(-sqrt(5)*x) @test k(v1,v2) ≈ (1+sqrt(5)*norm(v1-v2)+5/3*norm(v1-v2)^2)exp(-sqrt(5)*norm(v1-v2)) @test kappa(Matern52Kernel(),x) == kappa(k,x) - # l = 0.5 - # k = Matern52Kernel(l) - # @test k(v1,v2) ≈ (1+l*sqrt(5)*norm(v1-v2)+l^2*5/3*norm(v1-v2)^2)exp(-l*sqrt(5)*norm(v1-v2)) - # v = rand(3) - # k = Matern52Kernel(v) - # @test k(v1,v2) ≈ (1+sqrt(5)*norm(v.*(v1-v2))+5/3*norm(v.*(v1-v2))^2)exp(-sqrt(5)*norm(v.*(v1-v2))) end @testset "Coherence Materns" begin @test kappa(MaternKernel(0.5),x) ≈ kappa(ExponentialKernel(),x) @@ -132,25 +108,12 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(k,x) ≈ x @test k(v1,v2) ≈ dot(v1,v2) @test kappa(LinearKernel(),x) == kappa(k,x) - # l = 0.5 - # k = LinearKernel(l,c) - # @test k(v1,v2) ≈ l^2*dot(v1,v2) + c - # v = rand(3) - # k = LinearKernel(v,c) - # @test k(v1,v2) ≈ dot(v.*v1,v.*v2) + c end @testset "PolynomialKernel" begin k = PolynomialKernel() @test kappa(k,x) ≈ x^2 @test k(v1,v2) ≈ dot(v1,v2)^2 @test kappa(PolynomialKernel(),x) == kappa(k,x) - # d = 3.0 - # l = 0.5 - # k = PolynomialKernel(l,d,c) - # @test k(v1,v2) ≈ (l^2*dot(v1,v2) + c)^d - # v = rand(3) - # k = PolynomialKernel(v,d,c) - # @test k(v1,v2) ≈ (dot(v.*v1,v.*v2) + c)^d #Coherence test @test kappa(PolynomialKernel(1.0,c),x) ≈ kappa(LinearKernel(c),x) end @@ -161,31 +124,27 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(k,x) ≈ (1.0+x/2.0)^-2 @test k(v1,v2) ≈ (1.0+norm(v1-v2)^2/2.0)^-2 @test kappa(RationalQuadraticKernel(),x) == kappa(k,x) - # l = 0.5 - # a = 1.0 + rand() - # k = RationalQuadraticKernel(l,a) - # @test k(v1,v2) ≈ (1.0+l^2*norm(v1-v2)^2/a)^-a - # v = rand(3) - # k = RationalQuadraticKernel(v,a) - # @test k(v1,v2) ≈ (1.0+norm(v.*(v1-v2))^2/a)^-a end @testset "GammaRationalQuadraticKernel" begin k = GammaRationalQuadraticKernel() @test kappa(k,x) ≈ (1.0+x^2.0/2.0)^-2 @test k(v1,v2) ≈ (1.0+norm(v1-v2)^4.0/2.0)^-2 @test kappa(GammaRationalQuadraticKernel(),x) == kappa(k,x) - # l = 0.5 a = 1.0 + rand() - # g = 4.0 - # k = GammaRationalQuadraticKernel(l,a,g) - # @test k(v1,v2) ≈ (1.0+(l^2g)*norm(v1-v2)^(2g)/a)^-a - # v = rand(3) - # k = GammaRationalQuadraticKernel(v,a,g) - # @test k(v1,v2) ≈ (1.0+(norm(v.*(v1-v2))^(2g))/a)^-a #Coherence test @test kappa(GammaRationalQuadraticKernel(a,1.0),x) ≈ kappa(RationalQuadraticKernel(a),x) end end + @testset "Transformed/Scaled Kernel" begin + s = rand() + k = SqExponentialKernel() + kt = KernelFunctions.TransformedKernel(k,ScaleTransform(s)) + ks = KernelFunctions.ScaledKernel(k,s) + @test KernelFunctions.kappa(kt,v1,v2) == KernelFunctions.kappa(KernelFunctions.transform(k,ScaleTransform(s)),v1,v2) + @test KernelFunctions.metric(kt) == KernelFunctions.metric(k) + @test kappa(ks,x) == s*kappa(k,x) + @test kappa(ks,x) == kappa(s*k,x) + end @testset "KernelCombinations" begin k1 = LinearKernel() k2 = SqExponentialKernel() @@ -194,9 +153,6 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() k = k1 + k2 @test KernelFunctions.metric(k) == [KernelFunctions.DotProduct(),KernelFunctions.SqEuclidean()] @test length(k) == 2 - # @test transform(k) == [transform(k1),transform(k2)] - # @test transform(k,X) == [transform(k1,X),transform(k2,X)] - # @test transform(k,X,1) == [transform(k1,X,1),transform(k2,X,1)] end @testset "KernelProduct" begin diff --git a/test/test_transform.jl b/test/test_transform.jl index 17bc81886..2d6dc4c5e 100644 --- a/test/test_transform.jl +++ b/test/test_transform.jl @@ -16,6 +16,9 @@ f(x) = sin.(x) @testset "Transform Test" begin ## Test Scale Transform + @testset "IdentityTransform" begin + @test KernelFunctions.apply(IdentityTransform(),X)==X + end @testset "ScaleTransform" begin t = ScaleTransform(s) @test all(KernelFunctions.apply(t,X).==s*X) @@ -29,6 +32,10 @@ f(x) = sin.(x) vt2 = ARDTransform(v2) @test all(KernelFunctions.apply(vt1,X,obsdim=1).==v1'.*X) @test all(KernelFunctions.apply(vt2,X,obsdim=2).==v2.*X) + newv1 = rand(5) + KernelFunctions.set!(vt1,newv1) + @test all(vt1.v .== newv1) + @test_throws DimensionMismatch KernelFunctions.apply(vt1,rand(3,4)) end ## Test LowRankTransform @testset "LowRankTransform" begin From ba12bc80b51434970f6ad2badeceaacddbeea498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 11 Feb 2020 17:18:03 +0100 Subject: [PATCH 13/23] Removed all @inline and added BaseKernel --- src/KernelFunctions.jl | 1 + src/generic.jl | 2 +- src/kernels/constant.jl | 14 +++++++------- src/kernels/exponential.jl | 22 +++++++++++++--------- src/kernels/exponentiated.jl | 6 +++--- src/kernels/matern.jl | 10 +++++----- src/kernels/polynomial.jl | 10 ++++------ src/kernels/rationalquad.jl | 4 ++-- src/kernels/scaledkernel.jl | 4 ++-- src/kernels/transformedkernel.jl | 8 ++++---- src/transform/chaintransform.jl | 1 + 11 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 36c99e88e..132cb6e43 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -28,6 +28,7 @@ Abstract type defining a slice-wise transformation on an input matrix """ abstract type Transform end abstract type Kernel end +abstract type BaseKernel <: Kernel end include("utils.jl") include("distances/dotproduct.jl") diff --git a/src/generic.jl b/src/generic.jl index 8c6f04339..aea60e5cf 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -6,7 +6,7 @@ Base.iterate(k::Kernel, ::Any) = nothing # default fallback for evaluating a kernel with two arguments (such as vectors etc) kappa(κ::Kernel, x, y) = kappa(κ, evaluate(metric(κ), x, y)) kappa(κ::TransformedKernel, x, y) = kappa(kernel(κ), apply(κ.transform,x), apply(κ.transform,y)) -kappa(κ::TransformedKernel{<:Kernel,<:ScaleTransform}, x, y) = kappa(κ.kernel, _scale(κ.transform, metric(κ.kernel), x, y)) +kappa(κ::TransformedKernel{<:BaseKernel,<:ScaleTransform}, x, y) = kappa(κ, _scale(κ.transform, metric(κ), x, y)) _scale(t::ScaleTransform, metric::Euclidean, x, y) = first(t.s) * evaluate(metric, x, y) _scale(t::ScaleTransform, metric::Union{SqEuclidean,DotProduct}, x, y) = first(t.s)^2 * evaluate(metric, x, y) _scale(t::ScaleTransform, metric, x, y) = evaluate(metric, apply(t, x), apply(t, y)) diff --git a/src/kernels/constant.jl b/src/kernels/constant.jl index cbe11ed36..4d9549864 100644 --- a/src/kernels/constant.jl +++ b/src/kernels/constant.jl @@ -7,9 +7,9 @@ Create a kernel that always returning zero ``` The output type depends of `x` and `y` """ -struct ZeroKernel <: Kernel end +struct ZeroKernel <: BaseKernel end -@inline kappa(κ::ZeroKernel, d::T) where {T<:Real} = zero(T) +kappa(κ::ZeroKernel, d::T) where {T<:Real} = zero(T) metric(::ZeroKernel) = Delta() @@ -21,9 +21,9 @@ metric(::ZeroKernel) = Delta() ``` Kernel function working as an equivalent to add white noise. """ -struct WhiteKernel <: Kernel end +struct WhiteKernel <: BaseKernel end -@inline kappa(κ::WhiteKernel,δₓₓ::Real) = δₓₓ +kappa(κ::WhiteKernel,δₓₓ::Real) = δₓₓ metric(::WhiteKernel) = Delta() @@ -34,7 +34,7 @@ metric(::WhiteKernel) = Delta() ``` Kernel function always returning a constant value `c` """ -struct ConstantKernel{Tc<:Real} <: Kernel +struct ConstantKernel{Tc<:Real} <: BaseKernel c::Tc function ConstantKernel(c::T=1.0) where {T<:Real} new{T}(c) @@ -44,6 +44,6 @@ end params(k::ConstantKernel) = (k.c,) opt_params(k::ConstantKernel) = (k.c,) -@inline kappa(κ::ConstantKernel,x::Real) = κ.c*one(x) +kappa(κ::ConstantKernel,x::Real) = κ.c*one(x) -@inline metric(::ConstantKernel) = Delta() +metric(::ConstantKernel) = Delta() diff --git a/src/kernels/exponential.jl b/src/kernels/exponential.jl index 37f8694f9..7747d844c 100644 --- a/src/kernels/exponential.jl +++ b/src/kernels/exponential.jl @@ -8,13 +8,15 @@ The squared exponential kernel is an isotropic Mercer kernel given by the formul See also [`ExponentialKernel`](@ref) for a related form of the kernel or [`GammaExponentialKernel`](@ref) for a generalization. """ -struct SqExponentialKernel <: Kernel end +struct SqExponentialKernel <: BaseKernel end -@inline kappa(κ::SqExponentialKernel, d²::Real) = exp(-d²) -@inline iskroncompatible(::SqExponentialKernel) = true +kappa(κ::SqExponentialKernel, d²::Real) = exp(-d²) +iskroncompatible(::SqExponentialKernel) = true metric(::SqExponentialKernel) = SqEuclidean() +Base.show(io::IO,::SqExponentialKernel) = print(io,"Squared Exponential Kernel") + ## Aliases ## const RBFKernel = SqExponentialKernel const GaussianKernel = SqExponentialKernel @@ -26,12 +28,14 @@ The exponential kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = exp(-ρ‖x-y‖) ``` """ -struct ExponentialKernel <: Kernel end +struct ExponentialKernel <: BaseKernel end -@inline kappa(κ::ExponentialKernel, d::Real) = exp(-d) -@inline iskroncompatible(::ExponentialKernel) = true +kappa(κ::ExponentialKernel, d::Real) = exp(-d) +iskroncompatible(::ExponentialKernel) = true metric(::ExponentialKernel) = Euclidean() +Base.show(io::IO,::ExponentialKernel) = print(io,"Exponential Kernel") + ## Alias ## const LaplacianKernel = ExponentialKernel @@ -42,7 +46,7 @@ The γ-exponential kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = exp(-ρ^(2γ)‖x-y‖^(2γ)) ``` """ -struct GammaExponentialKernel{Tγ<:Real} <: Kernel +struct GammaExponentialKernel{Tγ<:Real} <: BaseKernel γ::Tγ function GammaExponentialKernel(γ::T=2.0) where {T<:Real} @check_args(GammaExponentialKernel, γ, γ >= zero(T), "γ > 0") @@ -53,6 +57,6 @@ end params(k::GammaExponentialKernel) = (γ,) opt_params(k::GammaExponentialKernel) = (γ,) -@inline kappa(κ::GammaExponentialKernel, d²::Real) = exp(-d²^κ.γ) -@inline iskroncompatible(::GammaExponentialKernel) = true +kappa(κ::GammaExponentialKernel, d²::Real) = exp(-d²^κ.γ) +iskroncompatible(::GammaExponentialKernel) = true metric(::GammaExponentialKernel) = SqEuclidean() diff --git a/src/kernels/exponentiated.jl b/src/kernels/exponentiated.jl index ea3244ef8..ef177cb40 100644 --- a/src/kernels/exponentiated.jl +++ b/src/kernels/exponentiated.jl @@ -5,10 +5,10 @@ The exponentiated kernel is a Mercer kernel given by: κ(x,y) = exp(ρ²xᵀy) ``` """ -struct ExponentiatedKernel <: Kernel end +struct ExponentiatedKernel <: BaseKernel end -@inline kappa(κ::ExponentiatedKernel, xᵀy::Real) = exp(xᵀy) +kappa(κ::ExponentiatedKernel, xᵀy::Real) = exp(xᵀy) metric(::ExponentiatedKernel) = DotProduct() -@inline iskroncompatible(::ExponentiatedKernel) = true +iskroncompatible(::ExponentiatedKernel) = true diff --git a/src/kernels/matern.jl b/src/kernels/matern.jl index b5cb3254e..af8dc2dc2 100644 --- a/src/kernels/matern.jl +++ b/src/kernels/matern.jl @@ -6,7 +6,7 @@ The matern kernel is an isotropic Mercer kernel given by the formula: ``` For `ν=n+1/2, n=0,1,2,...` it can be simplified and you should instead use [`ExponentialKernel`](@ref) for `n=0`, [`Matern32Kernel`](@ref), for `n=1`, [`Matern52Kernel`](@ref) for `n=2` and [`SqExponentialKernel`](@ref) for `n=∞`. """ -struct MaternKernel{Tν<:Real} <: Kernel +struct MaternKernel{Tν<:Real} <: BaseKernel ν::Tν function MaternKernel(ν::T=1.5) where {T<:Real} @check_args(MaternKernel, ν, ν > zero(T), "ν > 0") @@ -28,9 +28,9 @@ The matern 3/2 kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = (1+√(3)ρ‖x-y‖)exp(-√(3)ρ‖x-y‖) ``` """ -struct Matern32Kernel <: Kernel end +struct Matern32Kernel <: BaseKernel end -@inline kappa(κ::Matern32Kernel, d::Real) = (1+sqrt(3)*d)*exp(-sqrt(3)*d) +kappa(κ::Matern32Kernel, d::Real) = (1+sqrt(3)*d)*exp(-sqrt(3)*d) metric(::Matern32Kernel) = Euclidean() @@ -41,8 +41,8 @@ The matern 5/2 kernel is an isotropic Mercer kernel given by the formula: κ(x,y) = (1+√(5)ρ‖x-y‖ + 5ρ²‖x-y‖^2/3)exp(-√(5)ρ‖x-y‖) ``` """ -struct Matern52Kernel <: Kernel end +struct Matern52Kernel <: BaseKernel end -@inline kappa(κ::Matern52Kernel, d::Real) = (1+sqrt(5)*d+5*d^2/3)*exp(-sqrt(5)*d) +kappa(κ::Matern52Kernel, d::Real) = (1+sqrt(5)*d+5*d^2/3)*exp(-sqrt(5)*d) metric(::Matern52Kernel) = Euclidean() diff --git a/src/kernels/polynomial.jl b/src/kernels/polynomial.jl index ad33a1d1e..7565909c9 100644 --- a/src/kernels/polynomial.jl +++ b/src/kernels/polynomial.jl @@ -6,19 +6,17 @@ The linear kernel is a Mercer kernel given by ``` Where `c` is a real number """ -struct LinearKernel{Tc<:Real} <: Kernel +struct LinearKernel{Tc<:Real} <: BaseKernel c::Tc function LinearKernel(c::T=0.0) where {T} new{T}(c) end end - - params(k::LinearKernel) = (k.c,) opt_params(k::LinearKernel) = (k.c,) -@inline kappa(κ::LinearKernel, xᵀy::Real) = xᵀy + κ.c +kappa(κ::LinearKernel, xᵀy::Real) = xᵀy + κ.c metric(::LinearKernel) = DotProduct() @@ -30,7 +28,7 @@ The polynomial kernel is a Mercer kernel given by ``` Where `c` is a real number, and `d` is a shape parameter bigger than 1 """ -struct PolynomialKernel{Td<:Real,Tc<:Real} <: Kernel +struct PolynomialKernel{Td<:Real,Tc<:Real} <: BaseKernel d::Td c::Tc function PolynomialKernel(d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} @@ -42,6 +40,6 @@ end params(k::PolynomialKernel) = (k.d,k.c) opt_params(k::PolynomialKernel) = (k.d,k.c) -@inline kappa(κ::PolynomialKernel, xᵀy::T) where {T<:Real} = (xᵀy + κ.c)^(κ.d) +kappa(κ::PolynomialKernel, xᵀy::T) where {T<:Real} = (xᵀy + κ.c)^(κ.d) metric(::PolynomialKernel) = DotProduct() diff --git a/src/kernels/rationalquad.jl b/src/kernels/rationalquad.jl index e1d537c23..d21f3da23 100644 --- a/src/kernels/rationalquad.jl +++ b/src/kernels/rationalquad.jl @@ -6,7 +6,7 @@ The rational-quadratic kernel is an isotropic Mercer kernel given by the formula ``` where `α` is a shape parameter of the Euclidean distance. Check [`GammaRationalQuadraticKernel`](@ref) for a generalization. """ -struct RationalQuadraticKernel{Tα<:Real} <: Kernel +struct RationalQuadraticKernel{Tα<:Real} <: BaseKernel α::Tα function RationalQuadraticKernel(α::T=2.0) where {T} @check_args(RationalQuadraticKernel, α, α > zero(T), "α > 1") @@ -29,7 +29,7 @@ The Gamma-rational-quadratic kernel is an isotropic Mercer kernel given by the f ``` where `α` is a shape parameter of the Euclidean distance and `γ` is another shape parameter. """ -struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: Kernel +struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: BaseKernel α::Tα γ::Tγ function GammaRationalQuadraticKernel(α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} diff --git a/src/kernels/scaledkernel.jl b/src/kernels/scaledkernel.jl index c208a30c7..8be63f0b2 100644 --- a/src/kernels/scaledkernel.jl +++ b/src/kernels/scaledkernel.jl @@ -8,9 +8,9 @@ function ScaledKernel(kernel::Tk,σ::Tσ=1.0) where {Tk<:Kernel,Tσ<:Real} ScaledKernel{Tk,Tσ}(kernel,[σ]) end -@inline kappa(k::ScaledKernel, x) = first(k.σ)*kappa(k.kernel, x) +kappa(k::ScaledKernel, x) = first(k.σ)*kappa(k.kernel, x) -@inline metric(k::ScaledKernel) = metric(k.kernel) +metric(k::ScaledKernel) = metric(k.kernel) params(k::ScaledKernel) = (k.σ,params(k.kernel)) opt_params(k::ScaledKernel) = (k.σ,opt_params(k.kernel)) diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl index 8b914acb0..889e32665 100644 --- a/src/kernels/transformedkernel.jl +++ b/src/kernels/transformedkernel.jl @@ -3,13 +3,13 @@ struct TransformedKernel{Tk<:Kernel,Tr<:Transform} <: Kernel transform::Tr end -@inline kernel(κ) = κ.kernel +kernel(κ) = κ.kernel -@inline transform(κ::Kernel,t::Transform) = TransformedKernel(κ,t) +transform(κ::Kernel,t::Transform) = TransformedKernel(κ,t) -@inline kappa(κ::TransformedKernel, x) = kappa(κ.kernel, x) +kappa(κ::TransformedKernel, x) = kappa(κ.kernel, x) -@inline metric(κ::TransformedKernel) = metric(κ.kernel) +metric(κ::TransformedKernel) = metric(κ.kernel) params(κ::TransformedKernel) = (params(κ.transform),params(κ.kernel)) opt_params(κ::TransformedKernel) = (opt_params(κ.transform),opt_params(κ.kernel)) diff --git a/src/transform/chaintransform.jl b/src/transform/chaintransform.jl index 0547f6ae1..91d8880b8 100644 --- a/src/transform/chaintransform.jl +++ b/src/transform/chaintransform.jl @@ -17,6 +17,7 @@ function ChainTransform(v::AbstractVector{<:Transform}) ChainTransform(v) end +## Constructor to create a chain transform with an array of parameters function ChainTransform(v::AbstractVector{<:Type{<:Transform}},θ::AbstractVector) @assert length(v) == length(θ) ChainTransform(v.(θ)) From 937a235f72c3b865bf5c0e67ac78ae8e44dbb233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Tue, 11 Feb 2020 17:18:22 +0100 Subject: [PATCH 14/23] Improved tests on KernelProduct and KernelSum --- src/kernels/kernelproduct.jl | 9 ++---- src/kernels/kernelsum.jl | 58 ++++++++++++++++++++---------------- test/test_kernelmatrix.jl | 1 - test/test_kernels.jl | 18 +++++++++-- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/kernels/kernelproduct.jl b/src/kernels/kernelproduct.jl index 5e90f67c3..1613ca8a8 100644 --- a/src/kernels/kernelproduct.jl +++ b/src/kernels/kernelproduct.jl @@ -14,11 +14,8 @@ struct KernelProduct <: Kernel kernels::Vector{Kernel} end -KernelProduct(kernels::AbstractVector{<:Kernel}) = KernelProduct(kernels) - params(k::KernelProduct) = params.(k.kernels) opt_params(k::KernelProduct) = opt_params.(k.kernels) -duplicate(k::KernelProduct,θ) = KernelProduct(duplicate.(k.kernels,θ)) Base.:*(k1::Kernel,k2::Kernel) = KernelProduct([k1,k2]) Base.:*(k1::KernelProduct,k2::KernelProduct) = KernelProduct(vcat(k1.kernels,k2.kernels)) #TODO Add test @@ -26,10 +23,8 @@ Base.:*(k::Kernel,kp::KernelProduct) = KernelProduct(vcat(k,kp.kernels)) Base.:*(kp::KernelProduct,k::Kernel) = KernelProduct(vcat(kp.kernels,k)) Base.length(k::KernelProduct) = length(k.kernels) -metric(k::KernelProduct) = metric.(k.kernels) #TODO Add test -transform(k::KernelProduct) = transform.(k.kernels) #TODO Add test -transform(k::KernelProduct,x::AbstractVecOrMat) = transform.(k.kernels,[x]) #TODO Add test -transform(k::KernelProduct,x::AbstractVecOrMat,obsdim::Int) = transform.(k.kernels,[x],obsdim) #TODO Add test + +kappa(κ::KernelProduct, x ,y) = prod(kappa(k, x, y) for k in κ.kernels) hadamard(x,y) = x.*y diff --git a/src/kernels/kernelsum.jl b/src/kernels/kernelsum.jl index 14486d701..0cd8cb8c0 100644 --- a/src/kernels/kernelsum.jl +++ b/src/kernels/kernelsum.jl @@ -16,47 +16,53 @@ struct KernelSum <: Kernel weights::Vector{Real} end -function KernelSum(kernels::AbstractVector{<:Kernel}; weights::AbstractVector{<:Real}=ones(Float64,length(kernels))) - @assert length(kernels)==length(weights) "Weights and kernel vector should be of the same length" - @assert all(weights.>=0) "All weights should be positive" - KernelSum(kernels,weights) +function KernelSum( + kernels::AbstractVector{<:Kernel}; + weights::AbstractVector{<:Real} = ones(Float64, length(kernels)), +) + @assert length(kernels) == length(weights) "Weights and kernel vector should be of the same length" + @assert all(weights .>= 0) "All weights should be positive" + KernelSum(kernels, weights) end -params(k::KernelSum) = (k.weights,params.(k.kernels)) -opt_params(k::KernelSum) = (k.weights,opt_params.(k.kernels)) -duplicate(k::KernelSum,θ) = KernelSum(duplicate.(k.kernels,θ[end]),weights=first(θ)) - -Base.:+(k1::Kernel,k2::Kernel) = KernelSum([k1,k2],weights=[1.0,1.0]) -Base.:+(k1::KernelSum,k2::KernelSum) = KernelSum(vcat(k1.kernels,k2.kernels),weights=vcat(k1.weights,k2.weights)) -Base.:+(k::Kernel,ks::KernelSum) = KernelSum(vcat(k,ks.kernels),weights=vcat(1.0,ks.weights)) -Base.:+(ks::KernelSum,k::Kernel) = KernelSum(vcat(ks.kernels,k),weights=vcat(ks.weights,1.0)) -Base.:*(w::Real,k::KernelSum) = KernelSum(k.kernels,weights=w*k.weights) #TODO add tests +params(k::KernelSum) = (k.weights, params.(k.kernels)) +opt_params(k::KernelSum) = (k.weights, opt_params.(k.kernels)) +Base.:+(k1::Kernel, k2::Kernel) = KernelSum([k1, k2], weights = [1.0, 1.0]) +Base.:+(k1::ScaledKernel, k2::ScaledKernel) = KernelSum([kernel(k1), kernel(k2)], weights = [first(k1.σ), first(k2.σ)]) +Base.:+(k1::KernelSum, k2::KernelSum) = + KernelSum(vcat(k1.kernels, k2.kernels), weights = vcat(k1.weights, k2.weights)) +Base.:+(k::Kernel, ks::KernelSum) = + KernelSum(vcat(k, ks.kernels), weights = vcat(1.0, ks.weights)) +Base.:+(k::ScaledKernel, ks::KernelSum) = + KernelSum(vcat(kernel(k), ks.kernels), weights = vcat(first(k.σ), ks.weights)) +Base.:+(ks::KernelSum, k::Kernel) = + KernelSum(vcat(ks.kernels, k), weights = vcat(ks.weights, 1.0)) +Base.:+(ks::KernelSum, k::ScaledKernel) = + KernelSum(vcat(ks.kernels, kernel(k)), weights = vcat(ks.weights, first(k.σ))) +Base.:*(w::Real, k::KernelSum) = KernelSum(k.kernels, weights = w * k.weights) #TODO add tests Base.length(k::KernelSum) = length(k.kernels) -metric(k::KernelSum) = metric.(k.kernels) -transform(k::KernelSum) = transform.(k.kernels) -transform(k::KernelSum,x::AbstractVecOrMat) = transform.(k.kernels,[x]) -transform(k::KernelSum,x::AbstractVecOrMat,obsdim::Int) = transform.(k.kernels,[x],obsdim=obsdim) -function kernelmatrix( - κ::KernelSum, - X::AbstractMatrix; - obsdim::Int=defaultobs) - sum(κ.weights[i]*kernelmatrix(κ.kernels[i],X,obsdim=obsdim) for i in 1:length(κ)) +kappa(κ::KernelSum, x, y) = sum(κ.weights[i] * kappa(κ.kernels[i], x, y) for i in 1:length(κ)) + +function kernelmatrix(κ::KernelSum, X::AbstractMatrix; obsdim::Int = defaultobs) + sum(κ.weights[i] * kernelmatrix(κ.kernels[i], X, obsdim = obsdim) for i in 1:length(κ)) end function kernelmatrix( κ::KernelSum, X::AbstractMatrix, Y::AbstractMatrix; - obsdim::Int=defaultobs) - sum(κ.weights[i]*_kernelmatrix(κ.kernels[i],X,Y,obsdim) for i in 1:length(κ)) + obsdim::Int = defaultobs, +) + sum(κ.weights[i] * _kernelmatrix(κ.kernels[i], X, Y, obsdim) for i in 1:length(κ)) end function kerneldiagmatrix( κ::KernelSum, X::AbstractMatrix; - obsdim::Int=defaultobs) - sum(κ.weights[i]*kerneldiagmatrix(κ.kernels[i],X,obsdim=obsdim) for i in 1:length(κ)) + obsdim::Int = defaultobs, +) + sum(κ.weights[i] * kerneldiagmatrix(κ.kernels[i], X, obsdim = obsdim) for i in 1:length(κ)) end diff --git a/test/test_kernelmatrix.jl b/test/test_kernelmatrix.jl index 942f1d122..6fda63674 100644 --- a/test/test_kernelmatrix.jl +++ b/test/test_kernelmatrix.jl @@ -70,7 +70,6 @@ kt = sqexponentialkernel(s) k3 = RationalQuadraticKernel() kp = k1 * k2 kp2 = k1 * k3 - @test all(KernelFunctions.metric(kp).==[KernelFunctions.metric(k1),KernelFunctions.metric(k2)]) @test all(kernelmatrix(kp,A) .≈ kernelmatrix(k1,A) .* kernelmatrix(k2,A)) @test all(kernelmatrix(kp*k1,A) .≈ kernelmatrix(k1,A).^2 .* kernelmatrix(k2,A)) @test all(kernelmatrix(k1*kp,A) .≈ kernelmatrix(k1,A).^2 .* kernelmatrix(k2,A)) diff --git a/test/test_kernels.jl b/test/test_kernels.jl index 7b1959c54..0a7831bb2 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -148,14 +148,26 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @testset "KernelCombinations" begin k1 = LinearKernel() k2 = SqExponentialKernel() + k3 = RationalQuadraticKernel() X = rand(2,2) @testset "KernelSum" begin - k = k1 + k2 - @test KernelFunctions.metric(k) == [KernelFunctions.DotProduct(),KernelFunctions.SqEuclidean()] + w = [2.0,0.5] + k = KernelSum([k1,k2],w) + ks1 = 2.0*k1 + ks2 = 0.5*k2 @test length(k) == 2 + @test kappa(k,v1,v2) == kappa(2.0*k1+0.5*k2,v1,v2) + @test kappa(k+k3,v1,v2) ≈ kappa(k3+k,v1,v2) + @test kappa(k1+k2,v1,v2) == kappa(KernelSum([k1,k2]),v1,v2) + @test kappa(k+ks1,v1,v2) ≈ kappa(ks1+k,v1,v2) + @test kappa(k+k,v1,v2) == kappa(KernelSum([k1,k2,k1,k2],vcat(w,w)),v1,v2) end @testset "KernelProduct" begin - + k = KernelProduct([k1,k2]) + @test length(k) == 2 + @test kappa(k,v1,v2) == kappa(k1*k2,v1,v2) + @test kappa(k*k,v1,v2) ≈ kappa(k,v1,v2)^2 + @test kappa(k*k3,v1,v2) ≈ kappa(k3*k,v1,v2) end end end From 22d29920b99128d0bafca25c10ed1df8bab83d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 14 Feb 2020 14:59:58 +0100 Subject: [PATCH 15/23] Readapted the generic methods to subtypes of BaseKernel with subtypes --- Project.toml | 1 + src/KernelFunctions.jl | 1 + src/generic.jl | 16 +++++----------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 8e1711e18..ff1163f24 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.2.4" [deps] Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 132cb6e43..e0090bd81 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -20,6 +20,7 @@ using SpecialFunctions: logabsgamma, besselk using ZygoteRules: @adjoint using StatsFuns: logtwo using PDMats: PDMat +using InteractiveUtils: subtypes const defaultobs = 2 diff --git a/src/generic.jl b/src/generic.jl index aea60e5cf..432871a53 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -12,7 +12,7 @@ _scale(t::ScaleTransform, metric::Union{SqEuclidean,DotProduct}, x, y) = first( _scale(t::ScaleTransform, metric, x, y) = evaluate(metric, apply(t, x), apply(t, y)) ### Syntactic sugar for creating matrices and using kernel functions -for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:MaternKernel,:Matern32Kernel,:Matern52Kernel,:LinearKernel,:PolynomialKernel,:ExponentiatedKernel,:ZeroKernel,:WhiteKernel,:ConstantKernel,:RationalQuadraticKernel,:GammaRationalQuadraticKernel] +for k in subtypes(BaseKernel) @eval begin @inline (κ::$k)(d::Real) = kappa(κ,d) #TODO Add test @inline (κ::$k)(x::AbstractVector{<:Real}, y::AbstractVector{<:Real}) = kappa(κ, x, y) @@ -21,22 +21,16 @@ for k in [:ExponentialKernel,:SqExponentialKernel,:GammaExponentialKernel,:Mater end end -## Constructors for kernels without parameters -# for kernel in [:ExponentialKernel,:SqExponentialKernel,:Matern32Kernel,:Matern52Kernel,:ExponentiatedKernel] -# @eval begin -# $kernel() = $kernel(IdentityTransform()) -# $kernel(ρ::Real) = $kernel(ScaleTransform(ρ)) -# $kernel(ρ::AbstractVector{<:Real}) = $kernel(ARDTransform(ρ)) -# end -# end - -for k in [:SqExponentialKernel,:ExponentialKernel,:GammaExponentialKernel] +for k in Symbol.(subtypes(BaseKernel)) + k = Symbol(string(k)[17:end]) new_k = Symbol(lowercase(string(k))) @eval begin $new_k(args...) = $k(args...) $new_k(ρ::Real,args...) = TransformedKernel($k(args...),ScaleTransform(ρ)) $new_k(ρ::AbstractVector{<:Real},args...) = TransformedKernel($k(args...),ARDTransform(ρ)) $new_k(t::Transform,args...) = TransformedKernel($k(args...),t) + @deprecate($k(ρ::Real,args...),$new_k(ρ,args...)) + @deprecate($k(ρ::AbstractVector{<:Real},args...),$new_k(ρ,args...)) export $new_k end end From 54c48724147f52d96f2adcf5d7f43adb12bd09a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 14 Feb 2020 15:00:16 +0100 Subject: [PATCH 16/23] Started readapting documentation --- README.md | 8 ++++---- docs/src/api.md | 6 ++++-- docs/src/kernels.md | 16 +++++++++++++++- docs/src/metrics.md | 10 +++++++++- docs/src/transform.md | 5 +++-- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 65dba72b0..3b56b6281 100644 --- a/README.md +++ b/README.md @@ -13,19 +13,19 @@ The aim is to make the API as model-agnostic as possible while still being user- ```julia X = reshape(collect(range(-3.0,3.0,length=100)),:,1) # Set simple scaling of the data - k₁ = SqExponentialKernel(1.0) + k₁ = sqexponentialkernel(1.0) K₁ = kernelmatrix(k₁,X,obsdim=1) # Set a function transformation on the data - k₂ = MaternKernel(FunctionTransform(x->sin.(x))) + k₂ = TransformedKernel(Matern32Kernel(),FunctionTransform(x->sin.(x))) K₂ = kernelmatrix(k₂,X,obsdim=1) # Set a matrix premultiplication on the data - k₃ = PolynomialKernel(LowRankTransform(randn(4,1)),2.0,0.0) + k₃ = polynomialkernel(LowRankTransform(randn(4,1)),2.0,0.0) K₃ = kernelmatrix(k₃,X,obsdim=1) # Add and sum kernels - k₄ = 0.5*SqExponentialKernel()*LinearKernel(0.5) + 0.4*k₂ + k₄ = 0.5*SqExponentialKernel()*linearkernel(0.5) + 0.4*k₂ K₄ = kernelmatrix(k₄,X,obsdim=1) plot(heatmap.([K₁,K₂,K₃,K₄],yflip=true,colorbar=false)...,layout=(2,2),title=["K₁" "K₂" "K₃" "K₄"]) diff --git a/docs/src/api.md b/docs/src/api.md index a4bc4600b..22601cfe3 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -14,7 +14,7 @@ CurrentModule = KernelFunctions KernelFunctions ``` -## Kernel Functions +## Base Kernels ```@docs SqExponentialKernel @@ -33,9 +33,11 @@ ConstantKernel WhiteKernel ``` -## Kernel Combinations +## Composite Kernels ```@docs +TransformedKernel +ScaledKernel KernelSum KernelProduct ``` diff --git a/docs/src/kernels.md b/docs/src/kernels.md index 8d5173526..a80b1b900 100644 --- a/docs/src/kernels.md +++ b/docs/src/kernels.md @@ -2,6 +2,10 @@ CurrentModule = KernelFunctions ``` +# Base Kernels + +These are the basic kernels without any transformation of the data. They are the building blocks of KernelFunctions + ## Exponential Kernels ### Exponential Kernel @@ -13,7 +17,7 @@ The [Exponential Kernel](@ref ExponentialKernel) is defined as ### Square Exponential Kernel -The [Square Exponential Kernel](@ref KernelFunctions.SqExponentialKernel) is defined as +The [Square Exponential Kernel](@ref KernelFunctions.SqExponentialKernel) is defined as ```math k(x,x') = \exp\left(-\|x-x'\|^2\right) ``` @@ -91,3 +95,13 @@ The [Square Exponential Kernel](@ref KernelFunctions.SqExponentialKernel) is def ```math k(x,x') = 0 ``` + +# Composite Kernels + +## TransformedKernel + +## ScaledKernel + +## KernelSum + +## KernelProduct diff --git a/docs/src/metrics.md b/docs/src/metrics.md index 905237cac..b93779daf 100644 --- a/docs/src/metrics.md +++ b/docs/src/metrics.md @@ -2,7 +2,15 @@ KernelFunctions.jl relies on [Distances.jl]() for computing the pairwise matrix. To do so a distance measure is needed for each kernel. Two very common ones can already be used : `SqEuclidean` and `Euclidean`. -However all kernels do not rely on distances metrics respecting all the definitions. That's why two additional metrics come with the package : `DotProduct` (``) and `Delta` (`δ(x,y)`). If you want to create a new distance just implement the following : +However all kernels do not rely on distances metrics respecting all the definitions. That's why two additional metrics come with the package : `DotProduct` (``) and `Delta` (`δ(x,y)`). +Note that all base kernels must have a defined metric defined as : +```julia + metric(::CustomKernel) = SqEuclidean() +``` + +## Adding a new metric + +If you want to create a new distance just implement the following : ```julia struct Delta <: Distances.PreMetric diff --git a/docs/src/transform.md b/docs/src/transform.md index 8b4d5c595..31744434a 100644 --- a/docs/src/transform.md +++ b/docs/src/transform.md @@ -1,10 +1,10 @@ # Transform -`Transform` is the object that takes care of transforming the input data before distances are being computed. It can be as standard as `IdentityTransform` returning the same input, can be a scalar with `ScaleTransform` multiplying the vectors by a scalar or a vector. +`Transform` is the object that takes care of transforming the input data before distances are being computed. It can be as standard as `IdentityTransform` returning the same input, or multiplying the data by a scalar with `ScaleTransform` or by a vector with `ARDTransform`. There is a more general `Transform`: `FunctionTransform` that uses a function and apply it on each vector via `mapslices`. You can also create a pipeline of `Transform` via `TransformChain`. For example `LowRankTransform(rand(10,5))∘ScaleTransform(2.0)`. -One apply a transformation on a matrix or a vector via `transform(t::Transform,v::AbstractVecOrMat)` +One apply a transformation on a matrix or a vector via `KernelFunctions.apply(t::Transform,v::AbstractVecOrMat)` ## Transforms : ```@meta @@ -14,6 +14,7 @@ CurrentModule = KernelFunctions ```@docs IdentityTransform ScaleTransform + ARDTransform LowRankTransform FunctionTransform ChainTransform From f3f85bfc04f6f06fadcc6708f762b41e96e5a406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 14 Feb 2020 15:00:34 +0100 Subject: [PATCH 17/23] Removed depecrated `duplicate` function --- src/transform/transform.jl | 1 - src/utils.jl | 18 ------------------ 2 files changed, 19 deletions(-) diff --git a/src/transform/transform.jl b/src/transform/transform.jl index e278adc50..0ddc22bd9 100644 --- a/src/transform/transform.jl +++ b/src/transform/transform.jl @@ -23,7 +23,6 @@ Return exactly the input struct IdentityTransform <: Transform end params(t::IdentityTransform) = nothing -duplicate(t::IdentityTransform,θ) = t apply(t::IdentityTransform, x; obsdim::Int=defaultobs) = x #TODO add test diff --git a/src/utils.jl b/src/utils.jl index d2ce93e2f..c291e402f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -28,25 +28,7 @@ feature_dim(obsdim::Int) = obsdim == 1 ? 2 : 1 base_kernel(k::Kernel) = eval(nameof(typeof(k))) -base_transform(k::Kernel) = base_transform(transform(k)) base_transform(t::Transform) = eval(nameof(typeof(t))) -_tail(v::AbstractVector) = view(v,2:length(v)) - -""" -```julia - duplicate(k::Kernel,θ) - duplicate(t::Transform,θ) -``` -Recreate a kernel (transform) with the same structure as `k` (`t`) with the appropriate new parameters `θ`. -`theta` should have the same structure then the one given by `params(k)` (`params(t)`). -""" -duplicate - -duplicate(k::Kernel,θ::AbstractVector) = base_kernel(k)(duplicate(transform(k),first(θ)),_tail(θ)...) -duplicate(k::Kernel,θ::Tuple) = base_kernel(k)(duplicate(transform(k),first(θ)),Base.tail(θ)...) -duplicate(t::Transform,θ) = base_transform(t)(θ) - -dim(k::Kernel) = length(params(k)) """ ```julia From 3d9a52e8b06a7cf37943a22dc7d74b92ce78f092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 14 Feb 2020 15:49:58 +0100 Subject: [PATCH 18/23] Moved to keyword based constructors for BaseKernel --- src/generic.jl | 12 ++++++------ src/kernels/constant.jl | 2 +- src/kernels/exponential.jl | 2 +- src/kernels/matern.jl | 2 +- src/kernels/polynomial.jl | 4 ++-- src/kernels/rationalquad.jl | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 432871a53..03fae7444 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -25,12 +25,12 @@ for k in Symbol.(subtypes(BaseKernel)) k = Symbol(string(k)[17:end]) new_k = Symbol(lowercase(string(k))) @eval begin - $new_k(args...) = $k(args...) - $new_k(ρ::Real,args...) = TransformedKernel($k(args...),ScaleTransform(ρ)) - $new_k(ρ::AbstractVector{<:Real},args...) = TransformedKernel($k(args...),ARDTransform(ρ)) - $new_k(t::Transform,args...) = TransformedKernel($k(args...),t) - @deprecate($k(ρ::Real,args...),$new_k(ρ,args...)) - @deprecate($k(ρ::AbstractVector{<:Real},args...),$new_k(ρ,args...)) + $new_k(;args...) = $k(;args...) + $new_k(ρ::Real;args...) = TransformedKernel($k(;args...),ScaleTransform(ρ)) + $new_k(ρ::AbstractVector{<:Real};args...) = TransformedKernel($k(;args...),ARDTransform(ρ)) + $new_k(t::Transform;args...) = TransformedKernel($k(;args...),t) + @deprecate($k(ρ::Real;args...),$new_k(ρ;args...)) + @deprecate($k(ρ::AbstractVector{<:Real};args...),$new_k(ρ;args...)) export $new_k end end diff --git a/src/kernels/constant.jl b/src/kernels/constant.jl index 4d9549864..402f6c06e 100644 --- a/src/kernels/constant.jl +++ b/src/kernels/constant.jl @@ -36,7 +36,7 @@ Kernel function always returning a constant value `c` """ struct ConstantKernel{Tc<:Real} <: BaseKernel c::Tc - function ConstantKernel(c::T=1.0) where {T<:Real} + function ConstantKernel(;c::T=1.0) where {T<:Real} new{T}(c) end end diff --git a/src/kernels/exponential.jl b/src/kernels/exponential.jl index 7747d844c..7d903109e 100644 --- a/src/kernels/exponential.jl +++ b/src/kernels/exponential.jl @@ -48,7 +48,7 @@ The γ-exponential kernel is an isotropic Mercer kernel given by the formula: """ struct GammaExponentialKernel{Tγ<:Real} <: BaseKernel γ::Tγ - function GammaExponentialKernel(γ::T=2.0) where {T<:Real} + function GammaExponentialKernel(;γ::T=2.0) where {T<:Real} @check_args(GammaExponentialKernel, γ, γ >= zero(T), "γ > 0") return new{T}(γ) end diff --git a/src/kernels/matern.jl b/src/kernels/matern.jl index af8dc2dc2..b818a4602 100644 --- a/src/kernels/matern.jl +++ b/src/kernels/matern.jl @@ -8,7 +8,7 @@ For `ν=n+1/2, n=0,1,2,...` it can be simplified and you should instead use [`Ex """ struct MaternKernel{Tν<:Real} <: BaseKernel ν::Tν - function MaternKernel(ν::T=1.5) where {T<:Real} + function MaternKernel(;ν::T=1.5) where {T<:Real} @check_args(MaternKernel, ν, ν > zero(T), "ν > 0") return new{T}(ν) end diff --git a/src/kernels/polynomial.jl b/src/kernels/polynomial.jl index 7565909c9..c44af9b43 100644 --- a/src/kernels/polynomial.jl +++ b/src/kernels/polynomial.jl @@ -8,7 +8,7 @@ Where `c` is a real number """ struct LinearKernel{Tc<:Real} <: BaseKernel c::Tc - function LinearKernel(c::T=0.0) where {T} + function LinearKernel(;c::T=0.0) where {T} new{T}(c) end end @@ -31,7 +31,7 @@ Where `c` is a real number, and `d` is a shape parameter bigger than 1 struct PolynomialKernel{Td<:Real,Tc<:Real} <: BaseKernel d::Td c::Tc - function PolynomialKernel(d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} + function PolynomialKernel(;d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} @check_args(PolynomialKernel, d, d >= one(Td), "d >= 1") return new{Td, Tc}(d, c) end diff --git a/src/kernels/rationalquad.jl b/src/kernels/rationalquad.jl index d21f3da23..dc0ec11ed 100644 --- a/src/kernels/rationalquad.jl +++ b/src/kernels/rationalquad.jl @@ -8,7 +8,7 @@ where `α` is a shape parameter of the Euclidean distance. Check [`GammaRational """ struct RationalQuadraticKernel{Tα<:Real} <: BaseKernel α::Tα - function RationalQuadraticKernel(α::T=2.0) where {T} + function RationalQuadraticKernel(;α::T=2.0) where {T} @check_args(RationalQuadraticKernel, α, α > zero(T), "α > 1") return new{T}(α) end @@ -17,7 +17,7 @@ end params(k::RationalQuadraticKernel) = (k.α,) opt_params(k::RationalQuadraticKernel) = (k.α,) -@inline kappa(κ::RationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²/κ.α)^(-κ.α) +kappa(κ::RationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²/κ.α)^(-κ.α) metric(::RationalQuadraticKernel) = SqEuclidean() @@ -32,7 +32,7 @@ where `α` is a shape parameter of the Euclidean distance and `γ` is another sh struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: BaseKernel α::Tα γ::Tγ - function GammaRationalQuadraticKernel(α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} + function GammaRationalQuadraticKernel(;α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} @check_args(GammaRationalQuadraticKernel, α, α > one(Tα), "α > 1") @check_args(GammaRationalQuadraticKernel, γ, γ >= one(Tγ), "γ >= 1") return new{Tα, Tγ}(α, γ) @@ -42,6 +42,6 @@ end params(k::GammaRationalQuadraticKernel) = (k.α,k.γ) opt_params(k::GammaRationalQuadraticKernel) = (k.α,k.γ) -@inline kappa(κ::GammaRationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²^κ.γ/κ.α)^(-κ.α) +kappa(κ::GammaRationalQuadraticKernel, d²::T) where {T<:Real} = (one(T)+d²^κ.γ/κ.α)^(-κ.α) metric(::GammaRationalQuadraticKernel) = SqEuclidean() From f6c3e7a6a659afb2a7f42cab9510e2a77ac2f7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 14 Feb 2020 16:00:13 +0100 Subject: [PATCH 19/23] Adapting tests and constructors --- src/kernels/polynomial.jl | 2 +- src/kernels/rationalquad.jl | 2 +- src/transform/scaletransform.jl | 2 ++ test/test_constructors.jl | 30 +++++++--------------- test/test_kernels.jl | 45 ++++++++++----------------------- 5 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/kernels/polynomial.jl b/src/kernels/polynomial.jl index c44af9b43..4e60ecdc1 100644 --- a/src/kernels/polynomial.jl +++ b/src/kernels/polynomial.jl @@ -31,7 +31,7 @@ Where `c` is a real number, and `d` is a shape parameter bigger than 1 struct PolynomialKernel{Td<:Real,Tc<:Real} <: BaseKernel d::Td c::Tc - function PolynomialKernel(;d::Td=2.0, c::Tc=zero(Td)) where {Td<:Real, Tc<:Real} + function PolynomialKernel(; d::Td=2.0, c::Tc=0.0) where {Td<:Real, Tc<:Real} @check_args(PolynomialKernel, d, d >= one(Td), "d >= 1") return new{Td, Tc}(d, c) end diff --git a/src/kernels/rationalquad.jl b/src/kernels/rationalquad.jl index dc0ec11ed..76aa00493 100644 --- a/src/kernels/rationalquad.jl +++ b/src/kernels/rationalquad.jl @@ -32,7 +32,7 @@ where `α` is a shape parameter of the Euclidean distance and `γ` is another sh struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: BaseKernel α::Tα γ::Tγ - function GammaRationalQuadraticKernel(;α::Tα=2.0, γ::Tγ=2*one(Tα)) where {Tα<:Real, Tγ<:Real} + function GammaRationalQuadraticKernel(;α::Tα=2.0, γ::Tγ=2.0) where {Tα<:Real, Tγ<:Real} @check_args(GammaRationalQuadraticKernel, α, α > one(Tα), "α > 1") @check_args(GammaRationalQuadraticKernel, γ, γ >= one(Tγ), "γ >= 1") return new{Tα, Tγ}(α, γ) diff --git a/src/transform/scaletransform.jl b/src/transform/scaletransform.jl index 3efdc6ae8..c7d57835b 100644 --- a/src/transform/scaletransform.jl +++ b/src/transform/scaletransform.jl @@ -22,3 +22,5 @@ dim(str::ScaleTransform) = 1 apply(t::ScaleTransform,x::AbstractVecOrMat;obsdim::Int=defaultobs) = first(t.s) * x Base.isequal(t::ScaleTransform,t2::ScaleTransform) = isequal(first(t.s),first(t2.s)) + +Base.show(io::IO,t::ScaleTransform) = print(io,"Scale Transform s=$(first(t.s))") diff --git a/test/test_constructors.jl b/test/test_constructors.jl index 7eea7734e..978786084 100644 --- a/test/test_constructors.jl +++ b/test/test_constructors.jl @@ -12,27 +12,15 @@ s = ScaleTransform(l) @test KernelFunctions.metric(ExponentialKernel()) == Euclidean() @test KernelFunctions.metric(SqExponentialKernel()) == SqEuclidean() @test KernelFunctions.metric(GammaExponentialKernel()) == SqEuclidean() - @test KernelFunctions.metric(GammaExponentialKernel(2.0)) == SqEuclidean() - # @test isequal(transform(SqExponentialKernel(l)),s) - # @test KernelFunctions.transform(SqExponentialKernel(vl)) == ARDTransform(vl) - # @test isequal(KernelFunctions.transform(SqExponentialKernel(s)),s) + @test KernelFunctions.metric(GammaExponentialKernel(γ=2.0)) == SqEuclidean() end ## MaternKernel @testset "MaternKernel" begin @test KernelFunctions.metric(MaternKernel()) == Euclidean() - @test KernelFunctions.metric(MaternKernel(2.0)) == Euclidean() + @test KernelFunctions.metric(MaternKernel(ν=2.0)) == Euclidean() @test KernelFunctions.metric(Matern32Kernel()) == Euclidean() @test KernelFunctions.metric(Matern52Kernel()) == Euclidean() - # @test isequal(KernelFunctions.transform(MaternKernel(l)),s) - # @test isequal(KernelFunctions.transform(Matern32Kernel(l)),s) - # @test isequal(KernelFunctions.transform(Matern52Kernel(l)),s) - # @test KernelFunctions.transform(MaternKernel(vl)) == ARDTransform(vl) - # @test KernelFunctions.transform(Matern32Kernel(vl)) == ARDTransform(vl) - # @test KernelFunctions.transform(Matern52Kernel(vl)) == ARDTransform(vl) - # @test KernelFunctions.transform(MaternKernel(s)) == s - # @test KernelFunctions.transform(Matern32Kernel(s)) == s - # @test KernelFunctions.transform(Matern52Kernel(s)) == s end @testset "Exponentiated" begin @@ -41,23 +29,23 @@ end @testset "Constant" begin @test KernelFunctions.metric(ConstantKernel()) == KernelFunctions.Delta() - @test KernelFunctions.metric(ConstantKernel(2.0)) == KernelFunctions.Delta() + @test KernelFunctions.metric(ConstantKernel(c=2.0)) == KernelFunctions.Delta() @test KernelFunctions.metric(WhiteKernel()) == KernelFunctions.Delta() @test KernelFunctions.metric(ZeroKernel()) == KernelFunctions.Delta() end @testset "Polynomial" begin @test KernelFunctions.metric(LinearKernel()) == KernelFunctions.DotProduct() - @test KernelFunctions.metric(LinearKernel(2.0)) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(LinearKernel(c=2.0)) == KernelFunctions.DotProduct() @test KernelFunctions.metric(PolynomialKernel()) == KernelFunctions.DotProduct() - @test KernelFunctions.metric(PolynomialKernel(3.0)) == KernelFunctions.DotProduct() - @test KernelFunctions.metric(PolynomialKernel(3.0,2.0)) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(PolynomialKernel(d=3.0)) == KernelFunctions.DotProduct() + @test KernelFunctions.metric(PolynomialKernel(d=3.0,c=2.0)) == KernelFunctions.DotProduct() end @testset "RationalQuadratic" begin @test KernelFunctions.metric(RationalQuadraticKernel()) == SqEuclidean() - @test KernelFunctions.metric(RationalQuadraticKernel(2.0)) == SqEuclidean() + @test KernelFunctions.metric(RationalQuadraticKernel(α=2.0)) == SqEuclidean() @test KernelFunctions.metric(GammaRationalQuadraticKernel()) == SqEuclidean() - @test KernelFunctions.metric(GammaRationalQuadraticKernel(2.0)) == SqEuclidean() - @test KernelFunctions.metric(GammaRationalQuadraticKernel(2.0,3.0)) == SqEuclidean() + @test KernelFunctions.metric(GammaRationalQuadraticKernel(γ=2.0)) == SqEuclidean() + @test KernelFunctions.metric(GammaRationalQuadraticKernel(γ=2.0,α=3.0)) == SqEuclidean() end diff --git a/test/test_kernels.jl b/test/test_kernels.jl index 0a7831bb2..8bc05025c 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -19,7 +19,7 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() end @testset "ConstantKernel" begin c = 2.0 - k = ConstantKernel(c) + k = ConstantKernel(c=c) @test eltype(k) == Any @test kappa(k,1.0) == c @test kappa(k,0.5) == c @@ -31,39 +31,22 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(k,x) ≈ exp(-x) @test k(v1,v2) ≈ exp(-norm(v1-v2)^2) @test kappa(SqExponentialKernel(),x) == kappa(k,x) - # l = 0.5 - # k = SqExponentialKernel(l) - # @test k(v1,v2) ≈ exp(-l^2*norm(v1-v2)^2) - # v = rand(3) - # k = SqExponentialKernel(v) - # @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2))^2) end @testset "ExponentialKernel" begin k = ExponentialKernel() @test kappa(k,x) ≈ exp(-x) @test k(v1,v2) ≈ exp(-norm(v1-v2)) @test kappa(ExponentialKernel(),x) == kappa(k,x) - # l = 0.5 - # k = ExponentialKernel(l) - # @test k(v1,v2) ≈ exp(-l*norm(v1-v2)) - # v = rand(3) - # k = ExponentialKernel(v) - # @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2))) end @testset "GammaExponentialKernel" begin - k = GammaExponentialKernel(2.0) - @test kappa(k,x) ≈ exp(-(x)^(k.γ)) - @test k(v1,v2) ≈ exp(-norm(v1-v2)^(2k.γ)) + γ = 2.0 + k = GammaExponentialKernel(γ=γ) + @test kappa(k,x) ≈ exp(-(x)^(γ)) + @test k(v1,v2) ≈ exp(-norm(v1-v2)^(2γ)) @test kappa(GammaExponentialKernel(),x) == kappa(k,x) - # l = 0.5 - # k = GammaExponentialKernel(l,1.5) - # @test k(v1,v2) ≈ exp(-l^(3.0)*norm(v1-v2)^(3.0)) - # v = rand(3) - # k = GammaExponentialKernel(v,3.0) - # @test k(v1,v2) ≈ exp(-norm(v.*(v1-v2)).^6.0) #Coherence : - @test KernelFunctions._kernel(GammaExponentialKernel(1.0),v1,v2) ≈ KernelFunctions._kernel(SqExponentialKernel(),v1,v2) - @test KernelFunctions._kernel(GammaExponentialKernel(0.5),v1,v2) ≈ KernelFunctions._kernel(ExponentialKernel(),v1,v2) + @test KernelFunctions._kernel(GammaExponentialKernel(γ=1.0),v1,v2) ≈ KernelFunctions._kernel(SqExponentialKernel(),v1,v2) + @test KernelFunctions._kernel(GammaExponentialKernel(γ=0.5),v1,v2) ≈ KernelFunctions._kernel(ExponentialKernel(),v1,v2) end end @testset "Exponentiated" begin @@ -77,11 +60,11 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @testset "Matern" begin @testset "MaternKernel" begin ν = 2.0 - k = MaternKernel(ν) + k = MaternKernel(ν=ν) matern(x,ν) = 2^(1-ν)/gamma(ν)*(sqrt(2ν)*x)^ν*besselk(ν,sqrt(2ν)*x) @test kappa(k,x) ≈ matern(x,ν) @test kappa(k,0.0) == 1.0 - @test kappa(MaternKernel(ν),x) == kappa(k,x) + @test kappa(MaternKernel(ν=ν),x) == kappa(k,x) end @testset "Matern32Kernel" begin k = Matern32Kernel() @@ -96,9 +79,9 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(Matern52Kernel(),x) == kappa(k,x) end @testset "Coherence Materns" begin - @test kappa(MaternKernel(0.5),x) ≈ kappa(ExponentialKernel(),x) - @test kappa(MaternKernel(1.5),x) ≈ kappa(Matern32Kernel(),x) - @test kappa(MaternKernel(2.5),x) ≈ kappa(Matern52Kernel(),x) + @test kappa(MaternKernel(ν=0.5),x) ≈ kappa(ExponentialKernel(),x) + @test kappa(MaternKernel(ν=1.5),x) ≈ kappa(Matern32Kernel(),x) + @test kappa(MaternKernel(ν=2.5),x) ≈ kappa(Matern52Kernel(),x) end end @testset "Polynomial" begin @@ -115,7 +98,7 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test k(v1,v2) ≈ dot(v1,v2)^2 @test kappa(PolynomialKernel(),x) == kappa(k,x) #Coherence test - @test kappa(PolynomialKernel(1.0,c),x) ≈ kappa(LinearKernel(c),x) + @test kappa(PolynomialKernel(d=1.0,c=c),x) ≈ kappa(LinearKernel(c=c),x) end end @testset "RationalQuadratic" begin @@ -132,7 +115,7 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(GammaRationalQuadraticKernel(),x) == kappa(k,x) a = 1.0 + rand() #Coherence test - @test kappa(GammaRationalQuadraticKernel(a,1.0),x) ≈ kappa(RationalQuadraticKernel(a),x) + @test kappa(GammaRationalQuadraticKernel(α=a,γ=1.0),x) ≈ kappa(RationalQuadraticKernel(α=a),x) end end @testset "Transformed/Scaled Kernel" begin From efb7d8c46fc3466841e78676f20c0dcec9af8b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Thu, 27 Feb 2020 19:01:42 +0100 Subject: [PATCH 20/23] First work on printing output --- Project.toml | 2 +- src/KernelFunctions.jl | 1 + src/generic.jl | 13 +++---------- src/kernels/kernelsum.jl | 11 +++++++++++ src/kernels/scaledkernel.jl | 2 ++ src/kernels/transformedkernel.jl | 5 +---- src/transform/transform.jl | 1 - 7 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Project.toml b/Project.toml index ff1163f24..af3bd736b 100644 --- a/Project.toml +++ b/Project.toml @@ -13,7 +13,7 @@ StatsFuns = "4c63d2b9-4356-54db-8cca-17b64c39e42c" ZygoteRules = "700de1a5-db45-46bc-99cf-38207098b444" [compat] -Compat = "2.2, 3.2" +Compat = "2.2, 3" Distances = "0.8" PDMats = "0.9" SpecialFunctions = "0.8, 0.9, 0.10" diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index e0090bd81..9c0db9cff 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -11,6 +11,7 @@ export MaternKernel, Matern32Kernel, Matern52Kernel export LinearKernel, PolynomialKernel export RationalQuadraticKernel, GammaRationalQuadraticKernel export KernelSum, KernelProduct +export TransformedKernel, ScaledKernel export Transform, SelectTransform, ChainTransform, ScaleTransform, LowRankTransform, IdentityTransform, FunctionTransform diff --git a/src/generic.jl b/src/generic.jl index 03fae7444..5ae84844a 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -21,16 +21,9 @@ for k in subtypes(BaseKernel) end end -for k in Symbol.(subtypes(BaseKernel)) - k = Symbol(string(k)[17:end]) - new_k = Symbol(lowercase(string(k))) +for k in nameof.(subtypes(BaseKernel)) @eval begin - $new_k(;args...) = $k(;args...) - $new_k(ρ::Real;args...) = TransformedKernel($k(;args...),ScaleTransform(ρ)) - $new_k(ρ::AbstractVector{<:Real};args...) = TransformedKernel($k(;args...),ARDTransform(ρ)) - $new_k(t::Transform;args...) = TransformedKernel($k(;args...),t) - @deprecate($k(ρ::Real;args...),$new_k(ρ;args...)) - @deprecate($k(ρ::AbstractVector{<:Real};args...),$new_k(ρ;args...)) - export $new_k + @deprecate($k(ρ::Real;args...),TransformedKernel($k(args...),ScaleTransform(ρ))) + @deprecate($k(ρ::AbstractVector{<:Real};args...),TransformedKernel($k(args...),ARDTransform(ρ))) end end diff --git a/src/kernels/kernelsum.jl b/src/kernels/kernelsum.jl index 0cd8cb8c0..349a41b80 100644 --- a/src/kernels/kernelsum.jl +++ b/src/kernels/kernelsum.jl @@ -36,10 +36,14 @@ Base.:+(k::Kernel, ks::KernelSum) = KernelSum(vcat(k, ks.kernels), weights = vcat(1.0, ks.weights)) Base.:+(k::ScaledKernel, ks::KernelSum) = KernelSum(vcat(kernel(k), ks.kernels), weights = vcat(first(k.σ), ks.weights)) +Base.:+(k::ScaledKernel, ks::Kernel) = + KernelSum(vcat(kernel(k), ks), weights = vcat(first(k.σ), 1.0)) Base.:+(ks::KernelSum, k::Kernel) = KernelSum(vcat(ks.kernels, k), weights = vcat(ks.weights, 1.0)) Base.:+(ks::KernelSum, k::ScaledKernel) = KernelSum(vcat(ks.kernels, kernel(k)), weights = vcat(ks.weights, first(k.σ))) +Base.:+(ks::Kernel, k::ScaledKernel) = + KernelSum(vcat(ks, kernel(k)), weights = vcat(1.0, first(k.σ))) Base.:*(w::Real, k::KernelSum) = KernelSum(k.kernels, weights = w * k.weights) #TODO add tests Base.length(k::KernelSum) = length(k.kernels) @@ -66,3 +70,10 @@ function kerneldiagmatrix( ) sum(κ.weights[i] * kerneldiagmatrix(κ.kernels[i], X, obsdim = obsdim) for i in 1:length(κ)) end + +function Base.show(io::IO,κ::KernelSum) + print(io,"Sum of $(length(κ)) kernels:") + for i in 1:length(κ) + print(io,"\n\t- (w=$(κ.weights[i])) $(κ.kernels[i])") + end +end diff --git a/src/kernels/scaledkernel.jl b/src/kernels/scaledkernel.jl index 8be63f0b2..8d124b880 100644 --- a/src/kernels/scaledkernel.jl +++ b/src/kernels/scaledkernel.jl @@ -16,3 +16,5 @@ params(k::ScaledKernel) = (k.σ,params(k.kernel)) opt_params(k::ScaledKernel) = (k.σ,opt_params(k.kernel)) Base.:*(w::Real,k::Kernel) = ScaledKernel(k,w) + +Base.show(io::IO,κ::ScaledKernel) = print(io,"$(κ.kernel)\n\t- σ = $(first(κ.σ))") diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl index 889e32665..1a3b3a4ea 100644 --- a/src/kernels/transformedkernel.jl +++ b/src/kernels/transformedkernel.jl @@ -5,13 +5,10 @@ end kernel(κ) = κ.kernel -transform(κ::Kernel,t::Transform) = TransformedKernel(κ,t) - kappa(κ::TransformedKernel, x) = kappa(κ.kernel, x) metric(κ::TransformedKernel) = metric(κ.kernel) params(κ::TransformedKernel) = (params(κ.transform),params(κ.kernel)) -opt_params(κ::TransformedKernel) = (opt_params(κ.transform),opt_params(κ.kernel)) -Base.show(io::IO,κ::TransformedKernel) = print(io,"$(κ.kernel) with $(κ.transform)") +Base.show(io::IO,κ::TransformedKernel) = print(io,"$(κ.kernel)\n\t- $(κ.transform)") diff --git a/src/transform/transform.jl b/src/transform/transform.jl index 0ddc22bd9..b0cb2d2fc 100644 --- a/src/transform/transform.jl +++ b/src/transform/transform.jl @@ -3,7 +3,6 @@ export Transform, IdentityTransform, ScaleTransform, ARDTransform, LowRankTransf """ ```julia transform(t::Transform, X::AbstractMatrix) - transform(k::Kernel, X::AbstractMatrix) ``` Apply the transfomration `t` or `k.transform` on the input `X` """ From a26740609be6728eda6bebd1db7f6b23fde155a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Thu, 27 Feb 2020 19:30:56 +0100 Subject: [PATCH 21/23] Print of the full kernel structure via recursion --- src/generic.jl | 3 +++ src/kernels/kernelproduct.jl | 12 ++++++++++++ src/kernels/kernelsum.jl | 7 ++++++- src/kernels/scaledkernel.jl | 7 ++++++- src/kernels/transformedkernel.jl | 7 ++++++- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/generic.jl b/src/generic.jl index 5ae84844a..903e54391 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -11,6 +11,9 @@ _scale(t::ScaleTransform, metric::Euclidean, x, y) = first(t.s) * evaluate(metr _scale(t::ScaleTransform, metric::Union{SqEuclidean,DotProduct}, x, y) = first(t.s)^2 * evaluate(metric, x, y) _scale(t::ScaleTransform, metric, x, y) = evaluate(metric, apply(t, x), apply(t, y)) +printshifted(io::IO,κ::Kernel,shift::Int) = print(io,"$κ") +Base.show(io::IO,κ::Kernel) = print(io,nameof(typeof(κ))) + ### Syntactic sugar for creating matrices and using kernel functions for k in subtypes(BaseKernel) @eval begin diff --git a/src/kernels/kernelproduct.jl b/src/kernels/kernelproduct.jl index 1613ca8a8..0480dd430 100644 --- a/src/kernels/kernelproduct.jl +++ b/src/kernels/kernelproduct.jl @@ -49,3 +49,15 @@ function kerneldiagmatrix( obsdim::Int=defaultobs) #TODO Add test reduce(hadamard,kerneldiagmatrix(κ.kernels[i],X,obsdim=obsdim) for i in 1:length(κ)) end + +function Base.show(io::IO,κ::KernelProduct) + printshifted(io,κ,0) +end + +function printshifted(io::IO,κ::KernelProduct, shift::Int) + print(io,"Product of $(length(κ)) kernels:") + for i in 1:length(κ) + print(io,"\n"*("\t"^(shift+1))*"- ") + printshifted(io,κ.kernels[i],shift+2) + end +end diff --git a/src/kernels/kernelsum.jl b/src/kernels/kernelsum.jl index 349a41b80..8e4402be0 100644 --- a/src/kernels/kernelsum.jl +++ b/src/kernels/kernelsum.jl @@ -72,8 +72,13 @@ function kerneldiagmatrix( end function Base.show(io::IO,κ::KernelSum) + printshifted(io,κ,0) +end + +function printshifted(io::IO,κ::KernelSum, shift::Int) print(io,"Sum of $(length(κ)) kernels:") for i in 1:length(κ) - print(io,"\n\t- (w=$(κ.weights[i])) $(κ.kernels[i])") + print(io,"\n"*("\t"^(shift+1))*"- (w=$(κ.weights[i])) ") + printshifted(io,κ.kernels[i],shift+2) end end diff --git a/src/kernels/scaledkernel.jl b/src/kernels/scaledkernel.jl index 8d124b880..8f85df249 100644 --- a/src/kernels/scaledkernel.jl +++ b/src/kernels/scaledkernel.jl @@ -17,4 +17,9 @@ opt_params(k::ScaledKernel) = (k.σ,opt_params(k.kernel)) Base.:*(w::Real,k::Kernel) = ScaledKernel(k,w) -Base.show(io::IO,κ::ScaledKernel) = print(io,"$(κ.kernel)\n\t- σ = $(first(κ.σ))") +Base.show(io::IO,κ::ScaledKernel) = printshifted(io,κ,0) + +function printshifted(io::IO,κ::ScaledKernel,shift::Int) + printshifted(io,κ.kernel,shift) + print(io,"\n"*("\t"^(shift+1))*"- σ = $(first(κ.σ))") +end diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl index 1a3b3a4ea..0fde34288 100644 --- a/src/kernels/transformedkernel.jl +++ b/src/kernels/transformedkernel.jl @@ -11,4 +11,9 @@ metric(κ::TransformedKernel) = metric(κ.kernel) params(κ::TransformedKernel) = (params(κ.transform),params(κ.kernel)) -Base.show(io::IO,κ::TransformedKernel) = print(io,"$(κ.kernel)\n\t- $(κ.transform)") +Base.show(io::IO,κ::TransformedKernel) = printshifted(io,κ,0) + +function printshifted(io::IO,κ::TransformedKernel,shift::Int) + printshifted(io,κ.kernel,shift) + print(io,"\n"*("\t"^(shift+1))*"- $(κ.transform)") +end From fe5487ec4b9acb636022ab85194c0e16054a7eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 28 Feb 2020 17:06:39 +0100 Subject: [PATCH 22/23] Corrected transform behavior as a constructor --- .gitignore | 1 + README.md | 6 +++--- src/KernelFunctions.jl | 1 + src/generic.jl | 4 ++-- src/kernels/transformedkernel.jl | 18 ++++++++++++++++++ src/transform/transform.jl | 8 -------- test/test_kernelmatrix.jl | 2 +- test/test_kernels.jl | 13 ++++++++++--- 8 files changed, 36 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 680f24775..b3360eb7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.json +*.cov Manifest.toml coverage/ diff --git a/README.md b/README.md index 3b56b6281..b4597248b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ The aim is to make the API as model-agnostic as possible while still being user- ```julia X = reshape(collect(range(-3.0,3.0,length=100)),:,1) # Set simple scaling of the data - k₁ = sqexponentialkernel(1.0) + k₁ = SqExponentialKernel() K₁ = kernelmatrix(k₁,X,obsdim=1) # Set a function transformation on the data @@ -21,11 +21,11 @@ The aim is to make the API as model-agnostic as possible while still being user- K₂ = kernelmatrix(k₂,X,obsdim=1) # Set a matrix premultiplication on the data - k₃ = polynomialkernel(LowRankTransform(randn(4,1)),2.0,0.0) + k₃ = transform(PolynomialKernel(c=2.0,d=2.0),LowRankTransform(randn(4,1))) K₃ = kernelmatrix(k₃,X,obsdim=1) # Add and sum kernels - k₄ = 0.5*SqExponentialKernel()*linearkernel(0.5) + 0.4*k₂ + k₄ = 0.5*SqExponentialKernel()*LinearKernel(c=0.5) + 0.4*k₂ K₄ = kernelmatrix(k₄,X,obsdim=1) plot(heatmap.([K₁,K₂,K₃,K₄],yflip=true,colorbar=false)...,layout=(2,2),title=["K₁" "K₂" "K₃" "K₄"]) diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 9c0db9cff..e1ff999e6 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -1,6 +1,7 @@ module KernelFunctions export kernelmatrix, kernelmatrix!, kerneldiagmatrix, kerneldiagmatrix!, kappa, kernelpdmat # Main matrix functions +export transform export params, duplicate, set! # Helpers export Kernel diff --git a/src/generic.jl b/src/generic.jl index 903e54391..da2e0ad03 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -26,7 +26,7 @@ end for k in nameof.(subtypes(BaseKernel)) @eval begin - @deprecate($k(ρ::Real;args...),TransformedKernel($k(args...),ScaleTransform(ρ))) - @deprecate($k(ρ::AbstractVector{<:Real};args...),TransformedKernel($k(args...),ARDTransform(ρ))) + @deprecate($k(ρ::Real;args...),transform($k(args...),ρ)) + @deprecate($k(ρ::AbstractVector{<:Real};args...),transform($k(args...),ρ)) end end diff --git a/src/kernels/transformedkernel.jl b/src/kernels/transformedkernel.jl index 0fde34288..ef02be638 100644 --- a/src/kernels/transformedkernel.jl +++ b/src/kernels/transformedkernel.jl @@ -3,6 +3,24 @@ struct TransformedKernel{Tk<:Kernel,Tr<:Transform} <: Kernel transform::Tr end +""" +```julia + transform(k::BaseKernel, t::Transform) (1) + transform(k::BaseKernel, ρ::Real) (2) + transform(k::BaseKernel, ρ::AbstractVector) (3) +``` +(1) Create a TransformedKernel with transform `t` and kernel `k` +(2) Same as (1) with a `ScaleTransform` with scale `ρ` +(3) Same as (1) with an `ARDTransform` with scales `ρ` +""" +transform + +transform(k::BaseKernel, t::Transform) = TransformedKernel(k, t) + +transform(k::BaseKernel, ρ::Real) = TransformedKernel(k, ScaleTransform(ρ)) + +transform(k::BaseKernel,ρ::AbstractVector) = TransformedKernel(k, ARDTransform(ρ)) + kernel(κ) = κ.kernel kappa(κ::TransformedKernel, x) = kappa(κ.kernel, x) diff --git a/src/transform/transform.jl b/src/transform/transform.jl index b0cb2d2fc..6b2c7b988 100644 --- a/src/transform/transform.jl +++ b/src/transform/transform.jl @@ -1,13 +1,5 @@ export Transform, IdentityTransform, ScaleTransform, ARDTransform, LowRankTransform, FunctionTransform, ChainTransform -""" -```julia - transform(t::Transform, X::AbstractMatrix) -``` -Apply the transfomration `t` or `k.transform` on the input `X` -""" -transform - include("scaletransform.jl") include("ardtransform.jl") include("lowranktransform.jl") diff --git a/test/test_kernelmatrix.jl b/test/test_kernelmatrix.jl index 6fda63674..2f8602bbf 100644 --- a/test/test_kernelmatrix.jl +++ b/test/test_kernelmatrix.jl @@ -12,7 +12,7 @@ K = [zeros(dims[1],dims[1]),zeros(dims[2],dims[2])] Kdiag = [zeros(dims[1]),zeros(dims[2])] s = rand() k = SqExponentialKernel() -kt = sqexponentialkernel(s) +kt = transform(SqExponentialKernel(),s) @testset "Kernel Matrix Operations" begin @testset "Inplace Kernel Matrix" begin for obsdim in [1,2] diff --git a/test/test_kernels.jl b/test/test_kernels.jl index 8bc05025c..1046004db 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -120,10 +120,17 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() end @testset "Transformed/Scaled Kernel" begin s = rand() + v = rand(3) k = SqExponentialKernel() - kt = KernelFunctions.TransformedKernel(k,ScaleTransform(s)) - ks = KernelFunctions.ScaledKernel(k,s) - @test KernelFunctions.kappa(kt,v1,v2) == KernelFunctions.kappa(KernelFunctions.transform(k,ScaleTransform(s)),v1,v2) + kt = TransformedKernel(k,ScaleTransform(s)) + ktard = TransformedKernel(k,ARDTransform(v)) + ks = ScaledKernel(k,s) + @test kappa(kt,v1,v2) == kappa(transform(k,ScaleTransform(s)),v1,v2) + @test kappa(kt,v1,v2) == kappa(transform(k,s),v1,v2) + @test kappa(kt,v1,v2) == kappa(k,s*v1,s*v2) + @test kappa(ktard,v1,v2) == kappa(transform(k,ARDTransform(v)),v1,v2) + @test kappa(ktard,v1,v2) == kappa(transform(k,v),v1,v2) + @test kappa(ktard,v1,v2) == kappa(k,v.*v1,v.*v2) @test KernelFunctions.metric(kt) == KernelFunctions.metric(k) @test kappa(ks,x) == s*kappa(k,x) @test kappa(ks,x) == kappa(s*k,x) From ee6fc0a15ef5e9a79205d64c208f1e21bd91edce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Galy-Fajou?= Date: Fri, 28 Feb 2020 17:20:04 +0100 Subject: [PATCH 23/23] Readding StatsBase --- src/KernelFunctions.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 3bc0cac32..000c99034 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -25,6 +25,7 @@ using SpecialFunctions: logabsgamma, besselk using ZygoteRules: @adjoint using StatsFuns: logtwo using InteractiveUtils: subtypes +using StatsBase const defaultobs = 2