From 20a325ba5bae180a9a6ca15495302bf967c67f02 Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Tue, 17 Mar 2020 16:33:01 +0100 Subject: [PATCH 1/9] Created a first model for the PeriodicKernel --- src/KernelFunctions.jl | 4 +++- src/distances/sinus.jl | 13 +++++++++++++ src/kernels/periodic.jl | 15 +++++++++++++++ src/trainable.jl | 2 ++ src/zygote_adjoints.jl | 12 ++++++++++-- 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/distances/sinus.jl create mode 100644 src/kernels/periodic.jl diff --git a/src/KernelFunctions.jl b/src/KernelFunctions.jl index 2b2155552..141f9758e 100644 --- a/src/KernelFunctions.jl +++ b/src/KernelFunctions.jl @@ -14,6 +14,7 @@ export ExponentiatedKernel export MaternKernel, Matern32Kernel, Matern52Kernel export LinearKernel, PolynomialKernel export RationalQuadraticKernel, GammaRationalQuadraticKernel +export PeriodicKernel export KernelSum, KernelProduct export TransformedKernel, ScaledKernel @@ -42,9 +43,10 @@ abstract type BaseKernel <: Kernel end include("utils.jl") include("distances/dotproduct.jl") include("distances/delta.jl") +include("distances/sinus.jl") include("transform/transform.jl") -for k in ["exponential","matern","polynomial","constant","rationalquad","exponentiated"] +for k in ["constant","exponential","exponentiated","matern","periodic","polynomial","rationalquad"] include(joinpath("kernels",k*".jl")) end include("kernels/transformedkernel.jl") diff --git a/src/distances/sinus.jl b/src/distances/sinus.jl new file mode 100644 index 000000000..be724f2d7 --- /dev/null +++ b/src/distances/sinus.jl @@ -0,0 +1,13 @@ +struct Sinus{T} <: Distances.PreMetric + r::Vector{T} +end + +@inline function Distances._evaluate(d::Sinus,a::AbstractVector{T},b::AbstractVector{T}) where {T} + @boundscheck if (length(a) != length(b)) || length(a) != length(d.r) + throw(DimensionMismatch("Dimensions of the inputs are not matching : a = $(length(a)), b = $(length(b)), r = $(length(d.r))")) + end + return sum(abs2,sin.(π.*(a-b))./d.r) +end + +@inline (dist::Sinus)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist,a,b) +@inline (dist::Sinus)(a::Number,b::Number) = (sin(π*(a-b))/first(dist.r))^2 diff --git a/src/kernels/periodic.jl b/src/kernels/periodic.jl new file mode 100644 index 000000000..f2def0b40 --- /dev/null +++ b/src/kernels/periodic.jl @@ -0,0 +1,15 @@ +""" +PeriodicKernel() +``` + κ(x,y) = exp(-2 * sum_i(sin (π(x_i - y_i))/r_i)) +``` +""" +struct PeriodicKernel{T} <: BaseKernel + r::Vector{T} +end + +PeriodicKernel(dims::Int) = PeriodicKernel{Float64}(ones(Float64,dims)) + +metric(κ::PeriodicKernel) = Sinus(κ.r) + +kappa(κ::PeriodicKernel, d::Real) = exp(-0.5 * d) diff --git a/src/trainable.jl b/src/trainable.jl index e5f551303..c8f4426dc 100644 --- a/src/trainable.jl +++ b/src/trainable.jl @@ -12,6 +12,8 @@ trainable(k::MaternKernel) = (k.ν,) trainable(k::LinearKernel) = (k.c,) +trainable(k::PeriodicKernel) = (k.r,) + trainable(k::PolynomialKernel) = (k.d, k.c) trainable(k::RationalQuadraticKernel) = (k.α,) diff --git a/src/zygote_adjoints.jl b/src/zygote_adjoints.jl index b4ecfb0c1..ab01f356e 100644 --- a/src/zygote_adjoints.jl +++ b/src/zygote_adjoints.jl @@ -1,5 +1,13 @@ @adjoint function evaluate(s::DotProduct, x::AbstractVector, y::AbstractVector) - dot(x,y), Δ -> begin - (nothing, Δ.*y, Δ.*x) + dot(x, y), Δ -> begin + (nothing, Δ .* y, Δ .* x) end end + +# @adjoint function evaluate(s::Sinus, x::AbstractVector, y::AbstractVector) +# d = evaluate(s, x, y) +# s = sum(sin.(π*(x-y))) +# d, Δ -> begin +# (Sinus(Δ ./ s.r), 2Δ .* cos.(x - y) * d, -2Δ .* cos.(x - y) * d) +# end +# end From a85add8a33f6c2b0f34ee2fafdd7fcfd8647e2aa Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Tue, 17 Mar 2020 16:33:12 +0100 Subject: [PATCH 2/9] Adding tests --- test/test_distances.jl | 8 ++++++++ test/test_kernels.jl | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/test/test_distances.jl b/test/test_distances.jl index c3b5cd661..07f605210 100644 --- a/test/test_distances.jl +++ b/test/test_distances.jl @@ -19,4 +19,12 @@ B = rand(20,5) @test d(1,1) == 1 @test_throws DimensionMismatch d(rand(3),rand(4)) end + @testset "Sinus" begin + d = KernelFunctions.Sinus(ones(5)) + @test d(1,1) == 0 + @test d(0,1) ≈ 0 atol=1e-16 + @test d(0,0.5) ≈ 1 + @test_throws DimensionMismatch d(rand(5),rand(4)) + @test_throw DimensionMismatch d(rand(3),rand(3)) + end end diff --git a/test/test_kernels.jl b/test/test_kernels.jl index 1046004db..e04b44ae8 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -118,6 +118,14 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() @test kappa(GammaRationalQuadraticKernel(α=a,γ=1.0),x) ≈ kappa(RationalQuadraticKernel(α=a),x) end end + @testset "Periodic Kernel" begin + r = rand(3) + k = PeriodicKernel(r) + @test kappa(k, x) ≈ exp(-0.5x) + @test k(v1, v2) ≈ exp(-0.5 * sum(abs2,sin.(π*(v1-v2))./r)) + @test k(v1, v2) == k(v2, v1) + + end @testset "Transformed/Scaled Kernel" begin s = rand() v = rand(3) From 44178662afb0408402f0628b891cdf4e27cea358 Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Wed, 18 Mar 2020 11:45:08 +0100 Subject: [PATCH 3/9] Replaced docs --- src/kernels/periodic.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/kernels/periodic.jl b/src/kernels/periodic.jl index f2def0b40..05b6fe49d 100644 --- a/src/kernels/periodic.jl +++ b/src/kernels/periodic.jl @@ -1,5 +1,6 @@ """ -PeriodicKernel() + PeriodicKernel(r::AbstractVector) + PeriodicKernel(dims::Int) ``` κ(x,y) = exp(-2 * sum_i(sin (π(x_i - y_i))/r_i)) ``` From cdccace2babd6dc5288963a1e53ae0b69cfb8c52 Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Wed, 18 Mar 2020 11:45:36 +0100 Subject: [PATCH 4/9] Used SemiMetric and added eval_op for later use --- src/distances/dotproduct.jl | 12 +++++++----- src/distances/sinus.jl | 13 +++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/distances/dotproduct.jl b/src/distances/dotproduct.jl index 508de6096..da1eeecbb 100644 --- a/src/distances/dotproduct.jl +++ b/src/distances/dotproduct.jl @@ -1,12 +1,14 @@ -struct DotProduct <: Distances.PreMetric -end +struct DotProduct <: Distances.SemiMetric end -@inline function Distances._evaluate(::DotProduct,a::AbstractVector{T},b::AbstractVector{T}) where {T} +@inline function Distances._evaluate(::DotProduct, a::AbstractVector{T}, b::AbstractVector{T}) where {T} @boundscheck if length(a) != length(b) throw(DimensionMismatch("first array has length $(length(a)) which does not match the length of the second, $(length(b)).")) end return dot(a,b) end -@inline (dist::DotProduct)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist,a,b) -@inline (dist::DotProduct)(a::Number,b::Number) = a*b +# For later convenience once Distances.jl open their API +@inline Distances.eval_op(::DotProduct, a::Real, b::Real) = a * b + +@inline (dist::DotProduct)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist, a, b) +@inline (dist::DotProduct)(a::Number,b::Number) = Distances.eval_op(dist, a, b) diff --git a/src/distances/sinus.jl b/src/distances/sinus.jl index be724f2d7..e3e907df7 100644 --- a/src/distances/sinus.jl +++ b/src/distances/sinus.jl @@ -1,13 +1,18 @@ -struct Sinus{T} <: Distances.PreMetric +struct Sinus{T} <: Distances.SemiMetric r::Vector{T} end -@inline function Distances._evaluate(d::Sinus,a::AbstractVector{T},b::AbstractVector{T}) where {T} +Distances.parameters(d::Sinus) = d.r + +@inline function Distances._evaluate(d::Sinus, a::AbstractVector{T}, b::AbstractVector{T}) where {T} @boundscheck if (length(a) != length(b)) || length(a) != length(d.r) throw(DimensionMismatch("Dimensions of the inputs are not matching : a = $(length(a)), b = $(length(b)), r = $(length(d.r))")) end return sum(abs2,sin.(π.*(a-b))./d.r) end -@inline (dist::Sinus)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist,a,b) -@inline (dist::Sinus)(a::Number,b::Number) = (sin(π*(a-b))/first(dist.r))^2 +# For later convenience once Distances.jl open their API +@inline Distances.eval_op(::Sinus, a::Real, b::Real, p::Real) = abs2(sin(π * (a - b)) / p) + +@inline (dist::Sinus)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist, a, b) +@inline (dist::Sinus)(a::Number,b::Number) = abs2(sin(π * (a - b)) / first(dist.r)) From 53864bda630f3ec802d7055ef1b59c8110400b9d Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Wed, 18 Mar 2020 11:52:48 +0100 Subject: [PATCH 5/9] Type test_throws --- test/test_distances.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_distances.jl b/test/test_distances.jl index 07f605210..d440de848 100644 --- a/test/test_distances.jl +++ b/test/test_distances.jl @@ -25,6 +25,6 @@ B = rand(20,5) @test d(0,1) ≈ 0 atol=1e-16 @test d(0,0.5) ≈ 1 @test_throws DimensionMismatch d(rand(5),rand(4)) - @test_throw DimensionMismatch d(rand(3),rand(3)) + @test_throws DimensionMismatch d(rand(3),rand(3)) end end From 53d831ba8e0595824a5661d2fb30691bb8c3984f Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Wed, 18 Mar 2020 12:02:50 +0100 Subject: [PATCH 6/9] Reversed DotProduct to PreMetric --- src/distances/dotproduct.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distances/dotproduct.jl b/src/distances/dotproduct.jl index da1eeecbb..d8db59e9d 100644 --- a/src/distances/dotproduct.jl +++ b/src/distances/dotproduct.jl @@ -1,4 +1,4 @@ -struct DotProduct <: Distances.SemiMetric end +struct DotProduct <: Distances.PreMetric end @inline function Distances._evaluate(::DotProduct, a::AbstractVector{T}, b::AbstractVector{T}) where {T} @boundscheck if length(a) != length(b) From f1dad6eee67e9212cee16ebe59eef80561e42e5a Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Thu, 26 Mar 2020 16:42:38 +0100 Subject: [PATCH 7/9] Replaced sin(\pi) by sinpi and corrected the constructors and some tests --- src/distances/sinus.jl | 6 +++--- src/kernels/periodic.jl | 8 +++++++- test/test_flux.jl | 21 ++++++++++++++++----- test/test_kernels.jl | 28 ++++++++++++++-------------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/distances/sinus.jl b/src/distances/sinus.jl index e3e907df7..ca47f60ba 100644 --- a/src/distances/sinus.jl +++ b/src/distances/sinus.jl @@ -8,11 +8,11 @@ Distances.parameters(d::Sinus) = d.r @boundscheck if (length(a) != length(b)) || length(a) != length(d.r) throw(DimensionMismatch("Dimensions of the inputs are not matching : a = $(length(a)), b = $(length(b)), r = $(length(d.r))")) end - return sum(abs2,sin.(π.*(a-b))./d.r) + return sum(abs2, sinpi.(a - b) ./ d.r) end # For later convenience once Distances.jl open their API -@inline Distances.eval_op(::Sinus, a::Real, b::Real, p::Real) = abs2(sin(π * (a - b)) / p) +@inline Distances.eval_op(::Sinus, a::Real, b::Real, p::Real) = abs2(sinpi(a - b) / p) @inline (dist::Sinus)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist, a, b) -@inline (dist::Sinus)(a::Number,b::Number) = abs2(sin(π * (a - b)) / first(dist.r)) +@inline (dist::Sinus)(a::Number,b::Number) = abs2(sinpi(a - b) / first(dist.r)) diff --git a/src/kernels/periodic.jl b/src/kernels/periodic.jl index 05b6fe49d..f027f6019 100644 --- a/src/kernels/periodic.jl +++ b/src/kernels/periodic.jl @@ -7,9 +7,15 @@ """ struct PeriodicKernel{T} <: BaseKernel r::Vector{T} + function PeriodicKernel(; r::AbstractVector{T} = ones(Float64, 1)) where {T<:Real} + @assert all(r .> 0) + new{T}(r) + end end -PeriodicKernel(dims::Int) = PeriodicKernel{Float64}(ones(Float64,dims)) +PeriodicKernel(dims::Int = 1) = PeriodicKernel(Float64, dims) + +PeriodicKernel(T::DataType, dims::Int = 1) = PeriodicKernel(r = ones(T, dims)) metric(κ::PeriodicKernel) = Sinus(κ.r) diff --git a/test/test_flux.jl b/test/test_flux.jl index debdc0dca..4fdac27c7 100644 --- a/test/test_flux.jl +++ b/test/test_flux.jl @@ -2,20 +2,31 @@ using KernelFunctions using Test using Flux +#Alphabetic order @testset "Params" begin - ν = 2.0; c = 3.0; d = 2.0; γ = 2.0; α = 2.5 + ν = 2.0; c = 3.0; d = 2.0; γ = 2.0; α = 2.5; r = 2.0 + ## Base kernels kc = ConstantKernel(c=c) @test all(params(kc) .== params([c])) - km = MaternKernel(ν=ν) - @test all(params(km) .== params([ν])) - kl = LinearKernel(c=c) - @test all(params(kl) .== params([c])) + kge = GammaExponentialKernel(γ=γ) @test all(params(kge) .== params([γ])) + kgr = GammaRationalQuadraticKernel(γ=γ, α=α) @test all(params(kgr) .== params([α], [γ])) + + km = MaternKernel(ν=ν) + @test all(params(km) .== params([ν])) + + kl = LinearKernel(c=c) + @test all(params(kl) .== params([c])) + + kpe = PeriodicKernel(r=[r]) + @test all(params(kpe) .== params(r)) + kp = PolynomialKernel(c=c, d=d) @test all(params(kp) .== params([d], [c])) + kr = RationalQuadraticKernel(α=α) @test all(params(kr) .== params([α])) diff --git a/test/test_kernels.jl b/test/test_kernels.jl index e04b44ae8..18a52ec22 100644 --- a/test/test_kernels.jl +++ b/test/test_kernels.jl @@ -120,28 +120,28 @@ x = rand()*2; v1 = rand(3); v2 = rand(3); id = IdentityTransform() end @testset "Periodic Kernel" begin r = rand(3) - k = PeriodicKernel(r) + k = PeriodicKernel(r = r) @test kappa(k, x) ≈ exp(-0.5x) - @test k(v1, v2) ≈ exp(-0.5 * sum(abs2,sin.(π*(v1-v2))./r)) + @test k(v1, v2) ≈ exp(-0.5 * sum(abs2, sinpi.(v1 - v2) ./ r)) @test k(v1, v2) == k(v2, v1) - + @test PeriodicKernel(3)(v1, v2) == PeriodicKernel(r = ones(3))(v1, v2) end @testset "Transformed/Scaled Kernel" begin s = rand() v = rand(3) k = SqExponentialKernel() - 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) + 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) + @test kappa(ks, x) == s * kappa(k, x) + @test kappa(ks, x) == kappa(s * k, x) end @testset "KernelCombinations" begin k1 = LinearKernel() From 8038d1fb20e810c9703521ead8d972f248387993 Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Mon, 13 Apr 2020 12:24:11 +0200 Subject: [PATCH 8/9] Adapted PR to the new pkg structure --- Project.toml | 2 +- src/distances/delta.jl | 6 +++--- src/distances/dotproduct.jl | 5 ++--- src/distances/sinus.jl | 10 ++++------ src/kernels/periodic.jl | 2 +- test/distances/sinus.jl | 9 +++++++++ test/kernels/periodic.jl | 9 +++++++++ test/runtests.jl | 2 ++ test/trainable.jl | 5 ++++- 9 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 test/distances/sinus.jl create mode 100644 test/kernels/periodic.jl diff --git a/Project.toml b/Project.toml index 8b5e0d1c3..ee294854c 100644 --- a/Project.toml +++ b/Project.toml @@ -15,7 +15,7 @@ ZygoteRules = "700de1a5-db45-46bc-99cf-38207098b444" [compat] Compat = "2.2, 3" -Distances = "0.8" +Distances = "0.8.2" Requires = "1.0.1" SpecialFunctions = "0.8, 0.9, 0.10" StatsBase = "0.32, 0.33" diff --git a/src/distances/delta.jl b/src/distances/delta.jl index 10ef1b41f..b986ef73f 100644 --- a/src/distances/delta.jl +++ b/src/distances/delta.jl @@ -5,8 +5,8 @@ end @boundscheck if length(a) != length(b) throw(DimensionMismatch("first array has length $(length(a)) which does not match the length of the second, $(length(b)).")) end - return a==b + return a == b end -@inline (dist::Delta)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist,a,b) -@inline (dist::Delta)(a::Number,b::Number) = a==b +@inline (dist::Delta)(a::AbstractArray, b::AbstractArray) = Distances._evaluate(dist, a, b) +@inline (dist::Delta)(a::Number,b::Number) = a == b diff --git a/src/distances/dotproduct.jl b/src/distances/dotproduct.jl index d8db59e9d..7d75266db 100644 --- a/src/distances/dotproduct.jl +++ b/src/distances/dotproduct.jl @@ -1,4 +1,5 @@ struct DotProduct <: Distances.PreMetric end +# struct DotProduct <: Distances.UnionSemiMetric end @inline function Distances._evaluate(::DotProduct, a::AbstractVector{T}, b::AbstractVector{T}) where {T} @boundscheck if length(a) != length(b) @@ -7,8 +8,6 @@ struct DotProduct <: Distances.PreMetric end return dot(a,b) end -# For later convenience once Distances.jl open their API @inline Distances.eval_op(::DotProduct, a::Real, b::Real) = a * b - @inline (dist::DotProduct)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist, a, b) -@inline (dist::DotProduct)(a::Number,b::Number) = Distances.eval_op(dist, a, b) +@inline (dist::DotProduct)(a::Number,b::Number) = a * b diff --git a/src/distances/sinus.jl b/src/distances/sinus.jl index ca47f60ba..7276e2e48 100644 --- a/src/distances/sinus.jl +++ b/src/distances/sinus.jl @@ -1,8 +1,12 @@ struct Sinus{T} <: Distances.SemiMetric +# struct Sinus{T} <: Distances.UnionSemiMetric r::Vector{T} end Distances.parameters(d::Sinus) = d.r +@inline Distances.eval_op(::Sinus, a::Real, b::Real, p::Real) = abs2(sinpi(a - b) / p) +@inline (dist::Sinus)(a::AbstractArray, b::AbstractArray) = Distances._evaluate(dist, a, b) +@inline (dist::Sinus)(a::Number, b::Number) = abs2(sinpi(a - b) / first(dist.r)) @inline function Distances._evaluate(d::Sinus, a::AbstractVector{T}, b::AbstractVector{T}) where {T} @boundscheck if (length(a) != length(b)) || length(a) != length(d.r) @@ -10,9 +14,3 @@ Distances.parameters(d::Sinus) = d.r end return sum(abs2, sinpi.(a - b) ./ d.r) end - -# For later convenience once Distances.jl open their API -@inline Distances.eval_op(::Sinus, a::Real, b::Real, p::Real) = abs2(sinpi(a - b) / p) - -@inline (dist::Sinus)(a::AbstractArray,b::AbstractArray) = Distances._evaluate(dist, a, b) -@inline (dist::Sinus)(a::Number,b::Number) = abs2(sinpi(a - b) / first(dist.r)) diff --git a/src/kernels/periodic.jl b/src/kernels/periodic.jl index f027f6019..f7bbde3e4 100644 --- a/src/kernels/periodic.jl +++ b/src/kernels/periodic.jl @@ -13,7 +13,7 @@ struct PeriodicKernel{T} <: BaseKernel end end -PeriodicKernel(dims::Int = 1) = PeriodicKernel(Float64, dims) +PeriodicKernel(dims::Int) = PeriodicKernel(Float64, dims) PeriodicKernel(T::DataType, dims::Int = 1) = PeriodicKernel(r = ones(T, dims)) diff --git a/test/distances/sinus.jl b/test/distances/sinus.jl new file mode 100644 index 000000000..91ba1b028 --- /dev/null +++ b/test/distances/sinus.jl @@ -0,0 +1,9 @@ +@testset "sinus" begin + A = rand(10) + B = rand(10) + p = rand(10) + d = KernelFunctions.Sinus(p) + @test Distances.parameters(d) == p + @test evaluate(d, A, B) == sum(abs2.(sinpi.(A - B) ./ p)) + @test d(3.0, 2.0) == abs2(sinpi(3.0 - 2.0) / first(p)) +end diff --git a/test/kernels/periodic.jl b/test/kernels/periodic.jl new file mode 100644 index 000000000..f6d6330b1 --- /dev/null +++ b/test/kernels/periodic.jl @@ -0,0 +1,9 @@ +@testset "Periodic Kernel" begin + x = rand()*2; v1 = rand(3); v2 = rand(3); + r = rand(3) + k = PeriodicKernel(r = r) + @test kappa(k, x) ≈ exp(-0.5x) + @test k(v1, v2) ≈ exp(-0.5 * sum(abs2, sinpi.(v1 - v2) ./ r)) + @test k(v1, v2) == k(v2, v1) + @test PeriodicKernel(3)(v1, v2) == PeriodicKernel(r = ones(3))(v1, v2) +end diff --git a/test/runtests.jl b/test/runtests.jl index d28b415e9..ae1cc9ac3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -49,6 +49,7 @@ using KernelFunctions: metric @testset "distances" begin include(joinpath("distances", "dotproduct.jl")) include(joinpath("distances", "delta.jl")) + include(joinpath("distances", "sinus.jl")) end @testset "transform" begin @@ -71,6 +72,7 @@ using KernelFunctions: metric include(joinpath("kernels", "kernelproduct.jl")) include(joinpath("kernels", "kernelsum.jl")) include(joinpath("kernels", "matern.jl")) + include(joinpath("kernels", "periodic.jl")) include(joinpath("kernels", "polynomial.jl")) include(joinpath("kernels", "piecewisepolynomial.jl")) include(joinpath("kernels", "rationalquad.jl")) diff --git a/test/trainable.jl b/test/trainable.jl index 95f8b0ae3..2a1ed0f35 100644 --- a/test/trainable.jl +++ b/test/trainable.jl @@ -1,5 +1,5 @@ @testset "trainable" begin - ν = 2.0; c = 3.0; d = 2.0; γ = 2.0; α = 2.5; h = 0.5 + ν = 2.0; c = 3.0; d = 2.0; γ = 2.0; α = 2.5; h = 0.5; r = rand(3) kc = ConstantKernel(c=c) @test all(params(kc) .== params([c])) @@ -22,6 +22,9 @@ kp = PolynomialKernel(c=c, d=d) @test all(params(kp) .== params([d], [c])) + kpe = PeriodicKernel(r = r) + @test all(params(kpe) .== params(r)) + kr = RationalQuadraticKernel(α=α) @test all(params(kr) .== params([α])) From ff08795b4319943ce10068657b353dabebf75389 Mon Sep 17 00:00:00 2001 From: Theo Galy-Fajou Date: Mon, 13 Apr 2020 14:57:37 +0200 Subject: [PATCH 9/9] Added docs --- docs/src/api.md | 1 + docs/src/kernels.md | 8 ++++++++ src/kernels/periodic.jl | 9 +++++++-- test/kernels/periodic.jl | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/src/api.md b/docs/src/api.md index 830964516..0e009175f 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -28,6 +28,7 @@ LinearKernel PolynomialKernel RationalQuadraticKernel GammaRationalQuadraticKernel +PeriodicKernel ZeroKernel ConstantKernel WhiteKernel diff --git a/docs/src/kernels.md b/docs/src/kernels.md index 978ee6431..ea96a69eb 100644 --- a/docs/src/kernels.md +++ b/docs/src/kernels.md @@ -91,6 +91,14 @@ The [Polynomial Kernel](@ref KernelFunctions.PolynomialKernel) is defined as k(x,x';c,d) = \left(\langle x,x'\rangle + c\right)^d ``` +## Periodic Kernels + +### PeriodicKernel + +```math + k(x,x';r) = \exp\left(-0.5 \sum_i (sin (π(x_i - x'_i))/r_i)^2\right) +``` + ## Constant Kernels ### ConstantKernel diff --git a/src/kernels/periodic.jl b/src/kernels/periodic.jl index f7bbde3e4..f0036fbc9 100644 --- a/src/kernels/periodic.jl +++ b/src/kernels/periodic.jl @@ -1,8 +1,11 @@ """ PeriodicKernel(r::AbstractVector) PeriodicKernel(dims::Int) + PeriodicKernel(T::DataType, dims::Int) + +Periodic Kernel as described in http://www.inference.org.uk/mackay/gpB.pdf eq. 47. ``` - κ(x,y) = exp(-2 * sum_i(sin (π(x_i - y_i))/r_i)) + κ(x,y) = exp( - 0.5 sum_i(sin (π(x_i - y_i))/r_i)) ``` """ struct PeriodicKernel{T} <: BaseKernel @@ -19,4 +22,6 @@ PeriodicKernel(T::DataType, dims::Int = 1) = PeriodicKernel(r = ones(T, dims)) metric(κ::PeriodicKernel) = Sinus(κ.r) -kappa(κ::PeriodicKernel, d::Real) = exp(-0.5 * d) +kappa(κ::PeriodicKernel, d::Real) = exp(- 0.5d) + +Base.show(io::IO, κ::PeriodicKernel) = print(io, "Periodic Kernel, length(r) = $(length(κ.r))") diff --git a/test/kernels/periodic.jl b/test/kernels/periodic.jl index f6d6330b1..d2edbcb18 100644 --- a/test/kernels/periodic.jl +++ b/test/kernels/periodic.jl @@ -6,4 +6,5 @@ @test k(v1, v2) ≈ exp(-0.5 * sum(abs2, sinpi.(v1 - v2) ./ r)) @test k(v1, v2) == k(v2, v1) @test PeriodicKernel(3)(v1, v2) == PeriodicKernel(r = ones(3))(v1, v2) + @test_nowarn println(k) end