Skip to content

Commit 744419e

Browse files
willtebbuttWill Tebbutttheogf
authored
Fix SqExponential and GammaExponential + style (#158)
* Fix style * Fix convention * Add Gamma Exponential kernel reference * Update src/matrix/kernelpdmat.jl Co-authored-by: Théo Galy-Fajou <[email protected]> * Warn about breaking change * Only warn when first loaded Co-authored-by: Will Tebbutt <[email protected]> Co-authored-by: Théo Galy-Fajou <[email protected]>
1 parent 5c24f1c commit 744419e

File tree

14 files changed

+124
-118
lines changed

14 files changed

+124
-118
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
*.cov
33
Manifest.toml
44
coverage/
5+
src/update_v0.8.0

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "KernelFunctions"
22
uuid = "ec8451be-7e33-11e9-00cf-bbf324bd1392"
3-
version = "0.7.2"
3+
version = "0.8.0"
44

55
[deps]
66
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"

src/KernelFunctions.jl

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ KernelFunctions. [Github](https://github.com/JuliaGaussianProcesses/KernelFuncti
44
"""
55
module KernelFunctions
66

7+
if !isfile(joinpath(@__DIR__, "update_v0.8.0"))
8+
printstyled(
9+
stdout,
10+
"""
11+
WARNING: SqExponentialKernel changed convention in version 0.8.0.
12+
This kernel now divides the squared distance by 2 to align with standard practice.
13+
This warning will be removed in 0.9.0.
14+
""";
15+
color = Base.info_color(),
16+
)
17+
touch(joinpath(@__DIR__, "update_v0.8.0"))
18+
end
19+
720
export kernelmatrix, kernelmatrix!, kerneldiagmatrix, kerneldiagmatrix!
821
export transform
922
export duplicate, set! # Helpers
@@ -53,35 +66,51 @@ abstract type Kernel end
5366
abstract type SimpleKernel <: Kernel end
5467

5568
include("utils.jl")
56-
include("distances/pairwise.jl")
57-
include("distances/dotproduct.jl")
58-
include("distances/delta.jl")
59-
include("distances/sinus.jl")
60-
include("transform/transform.jl")
61-
62-
for f in readdir(joinpath(@__DIR__, "basekernels"))
63-
endswith(f, ".jl") && include(joinpath("basekernels", f))
64-
end
65-
66-
include("kernels/transformedkernel.jl")
67-
include("kernels/scaledkernel.jl")
68-
include("matrix/kernelmatrix.jl")
69-
include("kernels/kernelsum.jl")
70-
include("kernels/kernelproduct.jl")
71-
include("kernels/tensorproduct.jl")
72-
include("approximations/nystrom.jl")
69+
include(joinpath("distances", "pairwise.jl"))
70+
include(joinpath("distances", "dotproduct.jl"))
71+
include(joinpath("distances", "delta.jl"))
72+
include(joinpath("distances", "sinus.jl"))
73+
include(joinpath("transform", "transform.jl"))
74+
75+
include(joinpath("basekernels", "constant.jl"))
76+
include(joinpath("basekernels", "cosine.jl"))
77+
include(joinpath("basekernels", "exponential.jl"))
78+
include(joinpath("basekernels", "exponentiated.jl"))
79+
include(joinpath("basekernels", "fbm.jl"))
80+
include(joinpath("basekernels", "gabor.jl"))
81+
include(joinpath("basekernels", "maha.jl"))
82+
include(joinpath("basekernels", "matern.jl"))
83+
include(joinpath("basekernels", "nn.jl"))
84+
include(joinpath("basekernels", "periodic.jl"))
85+
include(joinpath("basekernels", "piecewisepolynomial.jl"))
86+
include(joinpath("basekernels", "polynomial.jl"))
87+
include(joinpath("basekernels", "rationalquad.jl"))
88+
include(joinpath("basekernels", "sm.jl"))
89+
include(joinpath("basekernels", "wiener.jl"))
90+
91+
include(joinpath("kernels", "transformedkernel.jl"))
92+
include(joinpath("kernels", "scaledkernel.jl"))
93+
include(joinpath("matrix", "kernelmatrix.jl"))
94+
include(joinpath("kernels", "kernelsum.jl"))
95+
include(joinpath("kernels", "kernelproduct.jl"))
96+
include(joinpath("kernels", "tensorproduct.jl"))
97+
include(joinpath("approximations", "nystrom.jl"))
7398
include("generic.jl")
7499

75-
include("mokernels/mokernel.jl")
76-
include("mokernels/moinput.jl")
77-
include("mokernels/independent.jl")
78-
include("mokernels/slfm.jl")
100+
include(joinpath("mokernels", "mokernel.jl"))
101+
include(joinpath("mokernels", "moinput.jl"))
102+
include(joinpath("mokernels", "independent.jl"))
103+
include(joinpath("mokernels", "slfm.jl"))
79104

80105
include("zygote_adjoints.jl")
81106

82107
function __init__()
83-
@require Kronecker="2c470bb0-bcc8-11e8-3dad-c9649493f05e" include("matrix/kernelkroneckermat.jl")
84-
@require PDMats="90014a1f-27ba-587c-ab20-58faa44d9150" include("matrix/kernelpdmat.jl")
108+
@require Kronecker="2c470bb0-bcc8-11e8-3dad-c9649493f05e" begin
109+
include(joinpath("matrix", "kernelkroneckermat.jl"))
110+
end
111+
@require PDMats="90014a1f-27ba-587c-ab20-58faa44d9150" begin
112+
include(joinpath("matrix", "kernelpdmat.jl"))
113+
end
85114
end
86115

87116
end

src/basekernels/exponential.jl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ related form of the kernel or [`GammaExponentialKernel`](@ref) for a generalizat
1111
"""
1212
struct SqExponentialKernel <: SimpleKernel end
1313

14-
kappa::SqExponentialKernel, d²::Real) = exp(-d²)
14+
kappa::SqExponentialKernel, d²::Real) = exp(- / 2)
1515

1616
metric(::SqExponentialKernel) = SqEuclidean()
1717

@@ -48,12 +48,15 @@ const LaplacianKernel = ExponentialKernel
4848
"""
4949
GammaExponentialKernel(; γ = 2.0)
5050
51-
The γ-exponential kernel is an isotropic Mercer kernel given by the formula:
51+
The γ-exponential kernel [1] is an isotropic Mercer kernel given by the formula:
5252
```
53-
κ(x,y) = exp(-‖x-y‖^(2γ))
53+
κ(x,y) = exp(-‖x-y‖^γ)
5454
```
5555
Where `γ > 0`, (the keyword `γ` can be replaced by `gamma`)
56-
For `γ = 1`, see `SqExponentialKernel` and `γ = 0.5`, see `ExponentialKernel`
56+
For `γ = 2`, see `SqExponentialKernel` and `γ = 1`, see `ExponentialKernel`.
57+
58+
[1] - Gaussian Processes for Machine Learning, Carl Edward Rasmussen and Christopher K. I.
59+
Williams, MIT Press, 2006.
5760
"""
5861
struct GammaExponentialKernel{Tγ<:Real} <: SimpleKernel
5962
γ::Vector{Tγ}
@@ -65,10 +68,12 @@ end
6568

6669
@functor GammaExponentialKernel
6770

68-
kappa::GammaExponentialKernel, d²::Real) = exp(-d²^first.γ))
71+
kappa::GammaExponentialKernel, d::Real) = exp(-d^first.γ))
6972

70-
metric(::GammaExponentialKernel) = SqEuclidean()
73+
metric(::GammaExponentialKernel) = Euclidean()
7174

7275
iskroncompatible(::GammaExponentialKernel) = true
7376

74-
Base.show(io::IO, κ::GammaExponentialKernel) = print(io, "Gamma Exponential Kernel (γ = ", first.γ), ")")
77+
function Base.show(io::IO, κ::GammaExponentialKernel)
78+
print(io, "Gamma Exponential Kernel (γ = ", first.γ), ")")
79+
end

src/basekernels/gabor.jl

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct GaborKernel{K<:Kernel} <: Kernel
1111
kernel::K
1212
function GaborKernel(;ell=nothing, p=nothing)
1313
k = _gabor(ell=ell, p=p)
14-
new{typeof(k)}(k)
14+
return new{typeof(k)}(k)
1515
end
1616
end
1717

@@ -57,24 +57,17 @@ end
5757

5858
Base.show(io::IO, κ::GaborKernel) = print(io, "Gabor Kernel (ell = ", κ.ell, ", p = ", κ.p, ")")
5959

60-
function kernelmatrix(
61-
κ::GaborKernel,
62-
X::AbstractMatrix;
63-
obsdim::Int=defaultobs)
64-
kernelmatrix.kernel, X; obsdim=obsdim)
60+
function kernelmatrix::GaborKernel, X::AbstractMatrix; obsdim::Int=defaultobs)
61+
return kernelmatrix.kernel, X; obsdim=obsdim)
6562
end
6663

6764
function kernelmatrix(
68-
κ::GaborKernel,
69-
X::AbstractMatrix,
70-
Y::AbstractMatrix;
71-
obsdim::Int=defaultobs)
72-
kernelmatrix.kernel, X, Y; obsdim=obsdim)
65+
κ::GaborKernel, X::AbstractMatrix, Y::AbstractMatrix;
66+
obsdim::Int=defaultobs,
67+
)
68+
return kernelmatrix.kernel, X, Y; obsdim=obsdim)
7369
end
7470

75-
function kerneldiagmatrix(
76-
κ::GaborKernel,
77-
X::AbstractMatrix;
78-
obsdim::Int=defaultobs) #TODO Add test
79-
kerneldiagmatrix.kernel, X; obsdim=obsdim)
71+
function kerneldiagmatrix::GaborKernel, X::AbstractMatrix; obsdim::Int=defaultobs) #TODO Add test
72+
return kerneldiagmatrix.kernel, X; obsdim=obsdim)
8073
end

src/matrix/kernelkroneckermat.jl

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,16 @@ using .Kronecker
22

33
export kernelkronmat
44

5-
function kernelkronmat(
6-
κ::Kernel,
7-
X::AbstractVector,
8-
dims::Int
9-
)
5+
function kernelkronmat::Kernel, X::AbstractVector, dims::Int)
106
@assert iskroncompatible(κ) "The chosen kernel is not compatible for kroenecker matrices (see [`iskroncompatible`](@ref))"
117
k = kernelmatrix(κ, X)
128
kronecker(k, dims)
139
end
1410

1511
function kernelkronmat(
16-
κ::Kernel,
17-
X::AbstractVector{<:AbstractVector};
18-
obsdim::Int=defaultobs
19-
)
20-
@assert iskroncompatible(κ) "The chosen kernel is not compatible for kroenecker matrices"
12+
κ::Kernel, X::AbstractVector{<:AbstractVector}; obsdim::Int=defaultobs,
13+
)
14+
@assert iskroncompatible(κ) "The chosen kernel is not compatible for Kronecker matrices"
2115
Ks = kernelmatrix.(κ, X)
2216
K = reduce(, Ks)
2317
end

src/matrix/kernelmatrix.jl

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,7 @@ function kernelmatrix!(K::AbstractMatrix, κ::SimpleKernel, x::AbstractVector)
7474
end
7575

7676
function kernelmatrix!(
77-
K::AbstractMatrix,
78-
κ::SimpleKernel,
79-
x::AbstractVector,
80-
y::AbstractVector,
77+
K::AbstractMatrix, κ::SimpleKernel, x::AbstractVector, y::AbstractVector,
8178
)
8279
validate_inplace_dims(K, x, y)
8380
pairwise!(K, metric(κ), x, y)
@@ -102,19 +99,13 @@ end
10299
const defaultobs = 2
103100

104101
function kernelmatrix!(
105-
K::AbstractMatrix,
106-
κ::Kernel,
107-
X::AbstractMatrix;
108-
obsdim::Int = defaultobs
102+
K::AbstractMatrix, κ::Kernel, X::AbstractMatrix; obsdim::Int = defaultobs
109103
)
110104
return kernelmatrix!(K, κ, vec_of_vecs(X; obsdim=obsdim))
111105
end
112106

113107
function kernelmatrix!(
114-
K::AbstractMatrix,
115-
κ::Kernel,
116-
X::AbstractMatrix,
117-
Y::AbstractMatrix;
108+
K::AbstractMatrix, κ::Kernel, X::AbstractMatrix, Y::AbstractMatrix;
118109
obsdim::Int = defaultobs
119110
)
120111
x = vec_of_vecs(X; obsdim=obsdim)
@@ -133,10 +124,7 @@ function kernelmatrix(κ::Kernel, X::AbstractMatrix, Y::AbstractMatrix; obsdim=d
133124
end
134125

135126
function kerneldiagmatrix!(
136-
K::AbstractVector,
137-
κ::Kernel,
138-
X::AbstractMatrix;
139-
obsdim::Int = defaultobs
127+
K::AbstractVector, κ::Kernel, X::AbstractMatrix; obsdim::Int = defaultobs
140128
)
141129
return kerneldiagmatrix!(K, κ, vec_of_vecs(X; obsdim=obsdim))
142130
end

src/matrix/kernelpdmat.jl

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,28 @@ using .PDMats: PDMat
33
export kernelpdmat
44

55
"""
6-
Compute a positive-definite matrix in the form of a `PDMat` matrix see [PDMats.jl]() with the cholesky decomposition precomputed
7-
The algorithm recursively tries to add recursively a diagonal nugget until positive definiteness is achieved or that the noise is too big
6+
Compute a positive-definite matrix in the form of a `PDMat` matrix see [PDMats.jl](https://github.com/JuliaStats/PDMats.jl)
7+
with the cholesky decomposition precomputed.
8+
The algorithm recursively tries to add recursively a diagonal nugget until positive
9+
definiteness is achieved or that the noise is too big.
810
"""
9-
function kernelpdmat(
10-
κ::Kernel,
11-
X::AbstractMatrix;
12-
obsdim::Int = defaultobs
13-
)
14-
K = kernelmatrix(κ,X,obsdim=obsdim)
15-
Kmax =maximum(K)
11+
function kernelpdmat::Kernel, X::AbstractMatrix; obsdim::Int=defaultobs)
12+
K = kernelmatrix(κ, X; obsdim=obsdim)
13+
Kmax = maximum(K)
1614
α = eps(eltype(K))
17-
while !isposdef(K+α*I) && α < 0.01*Kmax
15+
while !isposdef(K + α * I) && α < 0.01 * Kmax
1816
α *= 2.0
1917
end
20-
if α >= 0.01*Kmax
21-
throw(ErrorException("Adding noise on the diagonal was not sufficient to build a positive-definite matrix:\n\t- Check that your kernel parameters are not extreme\n\t- Check that your data is sufficiently sparse\n\t- Maybe use a different kernel"))
18+
if α >= 0.01 * Kmax
19+
error(
20+
"Adding noise on the diagonal was not sufficient to build a positive-definite" *
21+
" matrix:\n\t- Check that your kernel parameters are not extreme\n\t- Check" *
22+
" that your data is sufficiently sparse\n\t- Maybe use a different kernel",
23+
)
2224
end
23-
return PDMat(K+α*I)
25+
return PDMat(K + α * I)
2426
end
2527

26-
kernelpdmat::Kernel,X::AbstractVector{<:Real};obsdim=defaultobs) = kernelpdmat(κ,reshape(X,1,:),obsdim=2)
28+
function kernelpdmat::Kernel, X::AbstractVector{<:Real}; obsdim=defaultobs)
29+
return kernelpdmat(κ, reshape(X, 1, :); obsdim=2)
30+
end

test/basekernels/exponential.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
v2 = rand(rng, 3)
66
@testset "SqExponentialKernel" begin
77
k = SqExponentialKernel()
8-
@test kappa(k,x) exp(-x)
9-
@test k(v1,v2) exp(-norm(v1-v2)^2)
8+
@test kappa(k,x) exp(-x / 2)
9+
@test k(v1,v2) exp(-norm(v1-v2)^2 / 2)
1010
@test kappa(SqExponentialKernel(),x) == kappa(k,x)
1111
@test metric(SqExponentialKernel()) == SqEuclidean()
1212
@test RBFKernel == SqExponentialKernel
@@ -30,18 +30,20 @@
3030
@testset "GammaExponentialKernel" begin
3131
γ = 2.0
3232
k = GammaExponentialKernel=γ)
33-
@test kappa(k,x) exp(-(x)^(γ))
34-
@test k(v1,v2) exp(-norm(v1-v2)^(2γ))
35-
@test kappa(GammaExponentialKernel(),x) == kappa(k,x)
33+
@test k(v1, v2) exp(-norm(v1 - v2)^γ)
34+
@test kappa(GammaExponentialKernel(), x) == kappa(k, x)
3635
@test GammaExponentialKernel(gamma=γ).γ == [γ]
37-
@test metric(GammaExponentialKernel()) == SqEuclidean()
38-
@test metric(GammaExponentialKernel=2.0)) == SqEuclidean()
36+
@test metric(GammaExponentialKernel()) == Euclidean()
37+
@test metric(GammaExponentialKernel=2.0)) == Euclidean()
3938
@test repr(k) == "Gamma Exponential Kernel (γ = $(γ))"
4039
@test KernelFunctions.iskroncompatible(k) == true
4140
test_ADs-> GammaExponentialKernel(gamma=first(γ)), [γ])
4241
test_params(k, ([γ],))
4342
#Coherence :
44-
@test GammaExponentialKernel=1.0)(v1,v2) SqExponentialKernel()(v1,v2)
45-
@test GammaExponentialKernel=0.5)(v1,v2) ExponentialKernel()(v1,v2)
43+
@test isapprox(
44+
GammaExponentialKernel=2.0)(sqrt(0.5) * v1, sqrt(0.5) * v2),
45+
SqExponentialKernel()(v1,v2),
46+
)
47+
@test GammaExponentialKernel=1.0)(v1, v2) ExponentialKernel()(v1, v2)
4648
end
4749
end

test/basekernels/gabor.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@test k.ell ell atol=1e-5
77
@test k.p p atol=1e-5
88

9-
k_manual = exp(-sqeuclidean(v1, v2) / (k.ell^2)) * cospi(euclidean(v1, v2) / k.p)
9+
k_manual = exp(-sqeuclidean(v1, v2) / (2 * k.ell^2)) * cospi(euclidean(v1, v2) / k.p)
1010
@test k(v1,v2) k_manual atol=1e-5
1111

1212
lhs_manual = transform(SqExponentialKernel(), 1/k.ell)(v1,v2)

test/basekernels/sm.jl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@
1212

1313
t = v1 - v2
1414

15-
@test k1(v1, v2) sum(αs₁ .* exp.(-(t' * γs)'.^2) .*
16-
cospi.((t' * ωs)')) atol=1e-5
15+
@test k1(v1, v2) sum(αs₁ .* exp.(-(t' * γs)'.^2 ./ 2) .* cospi.((t' * ωs)')) atol=1e-5
1716

18-
@test k2(v1, v2) prod(sum(αs₂[i,:]' .* exp.(-(γs[i,:]' * t[i]).^2) .*
19-
cospi.(ωs[i,:]' * t[i])) for i in 1:length(t)) atol=1e-5
17+
@test isapprox(
18+
k2(v1, v2),
19+
prod(
20+
[sum(αs₂[i,:]' .* exp.(-(γs[i,:]' * t[i]).^2 ./ 2) .*
21+
cospi.(ωs[i,:]' * t[i])) for i in 1:length(t)],
22+
);
23+
atol=1e-5,
24+
)
2025

2126
@test_throws DimensionMismatch spectral_mixture_kernel(rand(5) ,rand(4,3), rand(4,3))
2227
@test_throws DimensionMismatch spectral_mixture_kernel(rand(3) ,rand(4,3), rand(5,3))

test/kernels/custom.jl

Lines changed: 0 additions & 11 deletions
This file was deleted.

test/matrix/kernelmatrix.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Custom Kernel implementation that only defines how to evaluate itself. This is used to
22
# test that fallback kernelmatrix / kerneldiagmatrix methods work properly.
33
struct BaseSE <: KernelFunctions.Kernel end
4-
(k::BaseSE)(x, y) = exp(-evaluate(SqEuclidean(), x, y))
4+
(k::BaseSE)(x, y) = exp(-evaluate(SqEuclidean(), x, y) / 2)
55

66
# Custom kernel to test `SimpleKernel` interface on, independently the `SimpleKernel`s that
77
# are implemented in the package. That this happens to be an exponentiated quadratic kernel

0 commit comments

Comments
 (0)