Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Rework on kernelmatrix to work with Vectors and more complex kernels #83

Merged
merged 26 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1801fc7
Added type SimpleKernel for when Distances can be used out of the box
theogf Apr 16, 2020
3554faa
Removed _kernel
theogf Apr 16, 2020
d8ec7b9
Defining the new kernelmatrix methods
theogf Apr 16, 2020
1cb4868
Merge branch 'colvecs' into general_kernelmatrix
theogf Apr 17, 2020
764fcd7
Implementation of all methods
theogf Apr 18, 2020
2b9d20a
Merge branch 'master' into general_kernelmatrix
theogf Apr 22, 2020
9455aca
Removed _kernel further
theogf Apr 22, 2020
7cc1eb2
Missing CosineKernel export
theogf Apr 22, 2020
576a2f5
Conflict with generic function
theogf Apr 22, 2020
6e21244
Relaxed tests on tensorproduct
theogf Apr 22, 2020
d57ab6b
Corrected the tests
theogf Apr 22, 2020
32a1e68
Added missing kernelmatrix function for piecewisepolynomial
theogf Apr 22, 2020
fd4cfd7
Correct version of kernelamtrix
theogf Apr 22, 2020
cbb024a
Added tests for the new methods. And corrected bugs
theogf Apr 22, 2020
220b162
Correction for Travis
theogf Apr 22, 2020
d117f25
Update src/basekernels/constant.jl
theogf Apr 22, 2020
7640550
Applied suggested corrections
theogf Apr 22, 2020
64d0753
Simplified piecewisepolynomial
theogf Apr 22, 2020
5ddf568
Made corrections for piecewise
theogf Apr 22, 2020
d6b88a1
Removed the abstractvector{<:real} wrapper and adapted the rest of th…
theogf Apr 22, 2020
1698e51
Merge branch 'master' into general_kernelmatrix
theogf Apr 22, 2020
87eeae7
Used vec_of_vecs for ambiguities
theogf Apr 22, 2020
e536233
Removed check for obsdim, improved error msg
theogf Apr 22, 2020
f1d0433
Missing function for RowVecs
theogf Apr 22, 2020
87be861
Applied formatting changes
theogf Apr 23, 2020
efa1479
Fixed bugs
theogf Apr 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/KernelFunctions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export duplicate, set! # Helpers

export Kernel
export ConstantKernel, WhiteKernel, EyeKernel, ZeroKernel
export CosineKernel
export SqExponentialKernel, RBFKernel, GaussianKernel, SEKernel
export LaplacianKernel, ExponentialKernel, GammaExponentialKernel
export ExponentiatedKernel
Expand Down Expand Up @@ -43,6 +44,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
abstract type SimpleKernel <: BaseKernel end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe DistancesKernel would highlight more clearly that for these kernels we make use of the pairwise functionality in Distances? SimpleKernel and BaseKernel both sound very similar and are both not very descriptive names IMO.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if it would be better but I was thinking we could change it as :

  • Kernel -> AbstractKernel
  • BaseKernel -> Kernel
  • SimpleKernel -> SimpleKernel/BaseKernel

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess AbstractKernel might be better and I guess Kernel would be fine instead of BaseKernel 👍 However, I think it would be nice to use something better than SimpleKernel or BaseKernel. At least I personally don't have an intuition for the names SimpleKernel or BaseKernel - is it referring to some mathematical property or form? That's why I think something more telling would be nice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the problem is that, to my knowledge, we make the separation only for computational reasons... So there is no clear name.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was the motivation for DistancesKernel since it might highlight the point that these kernels use Distances for computing the kernel matrix more explicitly. As mentioned before, an alternative would be to not encode this in the kernel type but just use a traits based system for separating kernels for computational reasons.


include("utils.jl")
include("distances/dotproduct.jl")
Expand Down
7 changes: 4 additions & 3 deletions src/basekernels/constant.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ Create a kernel that always returning zero
```
The output type depends of `x` and `y`
"""
struct ZeroKernel <: BaseKernel end
struct ZeroKernel <: SimpleKernel end

kappa(κ::ZeroKernel, d::T) where {T<:Real} = zero(T)

metric(::ZeroKernel) = Delta()

Base.show(io::IO, ::ZeroKernel) = print(io, "Zero Kernel")

issimple(ZeroKernel) = true
theogf marked this conversation as resolved.
Show resolved Hide resolved

"""
WhiteKernel()
Expand All @@ -24,7 +25,7 @@ Base.show(io::IO, ::ZeroKernel) = print(io, "Zero Kernel")
```
Kernel function working as an equivalent to add white noise. Can also be called via `EyeKernel()`
"""
struct WhiteKernel <: BaseKernel end
struct WhiteKernel <: SimpleKernel end

"""
EyeKernel()
Expand All @@ -48,7 +49,7 @@ Kernel function always returning a constant value `c`
κ(x,y) = c
```
"""
struct ConstantKernel{Tc<:Real} <: BaseKernel
struct ConstantKernel{Tc<:Real} <: SimpleKernel
c::Vector{Tc}
function ConstantKernel(;c::T=1.0) where {T<:Real}
new{T}([c])
Expand Down
2 changes: 1 addition & 1 deletion src/basekernels/cosine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The cosine kernel is a stationary kernel for a sinusoidal given by
κ(x,y) = cos( π * (x-y) )
```
"""
struct CosineKernel <: BaseKernel end
struct CosineKernel <: SimpleKernel end

kappa(κ::CosineKernel, d::Real) = cospi(d)
metric(::CosineKernel) = Euclidean()
Expand Down
6 changes: 3 additions & 3 deletions src/basekernels/exponential.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Can also be called via `SEKernel`, `GaussianKernel` or `SEKernel`.
See also [`ExponentialKernel`](@ref) for a
related form of the kernel or [`GammaExponentialKernel`](@ref) for a generalization.
"""
struct SqExponentialKernel <: BaseKernel end
struct SqExponentialKernel <: SimpleKernel end

kappa(κ::SqExponentialKernel, d²::Real) = exp(-d²)
iskroncompatible(::SqExponentialKernel) = true
Expand All @@ -30,7 +30,7 @@ The exponential kernel is a Mercer kernel given by the formula:
κ(x,y) = exp(-‖x-y‖)
```
"""
struct ExponentialKernel <: BaseKernel end
struct ExponentialKernel <: SimpleKernel end

kappa(κ::ExponentialKernel, d::Real) = exp(-d)
iskroncompatible(::ExponentialKernel) = true
Expand All @@ -51,7 +51,7 @@ The γ-exponential kernel is an isotropic Mercer kernel given by the formula:
Where `γ > 0`, (the keyword `γ` can be replaced by `gamma`)
For `γ = 1`, see `SqExponentialKernel` and `γ = 0.5`, see `ExponentialKernel`
"""
struct GammaExponentialKernel{Tγ<:Real} <: BaseKernel
struct GammaExponentialKernel{Tγ<:Real} <: SimpleKernel
γ::Vector{Tγ}
function GammaExponentialKernel(; gamma::T=2.0, γ::T=gamma) where {T<:Real}
@check_args(GammaExponentialKernel, γ, γ >= zero(T), "γ > 0")
Expand Down
2 changes: 1 addition & 1 deletion src/basekernels/exponentiated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The exponentiated kernel is a Mercer kernel given by:
κ(x,y) = exp(xᵀy)
```
"""
struct ExponentiatedKernel <: BaseKernel end
struct ExponentiatedKernel <: SimpleKernel end

kappa(κ::ExponentiatedKernel, xᵀy::Real) = exp(xᵀy)
metric(::ExponentiatedKernel) = DotProduct()
Expand Down
11 changes: 0 additions & 11 deletions src/basekernels/fbm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,6 @@ function kernelmatrix!(
return K
end

## Apply kernel on two vectors ##
function _kernel(
κ::FBMKernel,
x::AbstractVector,
y::AbstractVector;
obsdim::Int = defaultobs
)
@assert length(x) == length(y) "x and y don't have the same dimension!"
return kappa(κ, x, y)
end

function kappa(κ::FBMKernel, x::AbstractVector{<:Real}, y::AbstractVector{<:Real})
modX = sum(abs2, x)
modY = sum(abs2, y)
Expand Down
2 changes: 1 addition & 1 deletion src/basekernels/maha.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Mahalanobis distance-based kernel given by
where the matrix P is the metric.

"""
struct MahalanobisKernel{T<:Real, A<:AbstractMatrix{T}} <: BaseKernel
struct MahalanobisKernel{T<:Real, A<:AbstractMatrix{T}} <: SimpleKernel
P::A
function MahalanobisKernel(P::AbstractMatrix{T}) where {T<:Real}
LinearAlgebra.checksquare(P)
Expand Down
6 changes: 3 additions & 3 deletions src/basekernels/matern.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The matern kernel is a 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} <: BaseKernel
struct MaternKernel{Tν<:Real} <: SimpleKernel
ν::Vector{Tν}
function MaternKernel(;nu::T=1.5, ν::T=nu) where {T<:Real}
@check_args(MaternKernel, ν, ν > zero(T), "ν > 0")
Expand Down Expand Up @@ -37,7 +37,7 @@ The matern 3/2 kernel is a Mercer kernel given by the formula:
κ(x,y) = (1+√(3)‖x-y‖)exp(-√(3)‖x-y‖)
```
"""
struct Matern32Kernel <: BaseKernel end
struct Matern32Kernel <: SimpleKernel end

kappa(κ::Matern32Kernel, d::Real) = (1 + sqrt(3) * d) * exp(-sqrt(3) * d)
metric(::Matern32Kernel) = Euclidean()
Expand All @@ -52,7 +52,7 @@ The matern 5/2 kernel is a Mercer kernel given by the formula:
κ(x,y) = (1+√(5)‖x-y‖ + 5/3‖x-y‖^2)exp(-√(5)‖x-y‖)
```
"""
struct Matern52Kernel <: BaseKernel end
struct Matern52Kernel <: SimpleKernel end

kappa(κ::Matern52Kernel, d::Real) = (1 + sqrt(5) * d + 5 * d^2 / 3) * exp(-sqrt(5) * d)
metric(::Matern52Kernel) = Euclidean()
Expand Down
2 changes: 1 addition & 1 deletion src/basekernels/periodic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Periodic Kernel as described in http://www.inference.org.uk/mackay/gpB.pdf eq. 4
κ(x,y) = exp( - 0.5 sum_i(sin (π(x_i - y_i))/r_i))
```
"""
struct PeriodicKernel{T} <: BaseKernel
struct PeriodicKernel{T} <: SimpleKernel
r::Vector{T}
function PeriodicKernel(; r::AbstractVector{T} = ones(Float64, 1)) where {T<:Real}
@assert all(r .> 0)
Expand Down
21 changes: 11 additions & 10 deletions src/basekernels/piecewisepolynomial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,6 @@ function kappa(
return _piecewisepolynomial(κ, r, j)
end

function _kernel(
κ::PiecewisePolynomialKernel,
x::AbstractVector,
y::AbstractVector;
obsdim::Int = defaultobs,
)
@assert length(x) == length(y) "x and y don't have the same dimension!"
return kappa(κ,x,y)
end

function kernelmatrix(
κ::PiecewisePolynomialKernel{V},
X::AbstractMatrix;
Expand All @@ -62,6 +52,17 @@ function kernelmatrix(
return map(r->_piecewisepolynomial(κ, r, j), pairwise(metric(κ), X; dims=obsdim))
end

function kernelmatrix(
κ::PiecewisePolynomialKernel{V},
X::AbstractMatrix,
Y::AbstractMatrix;
obsdim::Int = defaultobs
) where {V}
j = div(size(X, feature_dim(obsdim)), 2) + V + 1
return map(r->_piecewisepolynomial(κ, r, j), pairwise(metric(κ), X, Y; dims=obsdim))
devmotion marked this conversation as resolved.
Show resolved Hide resolved
end


function _kernelmatrix(κ::PiecewisePolynomialKernel{V}, X, Y, obsdim) where {V}
j = div(size(X, feature_dim(obsdim)), 2) + V + 1
return map(r->_piecewisepolynomial(κ, r, j), pairwise(metric(κ), X, Y; dims=obsdim))
Expand Down
4 changes: 2 additions & 2 deletions src/basekernels/polynomial.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The linear kernel is a Mercer kernel given by
```
Where `c` is a real number
"""
struct LinearKernel{Tc<:Real} <: BaseKernel
struct LinearKernel{Tc<:Real} <: SimpleKernel
c::Vector{Tc}
function LinearKernel(;c::T=0.0) where {T}
new{T}([c])
Expand All @@ -28,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. For `d = 1` see [`LinearKernel`](@ref)
"""
struct PolynomialKernel{Td<:Real, Tc<:Real} <: BaseKernel
struct PolynomialKernel{Td<:Real, Tc<:Real} <: SimpleKernel
d::Vector{Td}
c::Vector{Tc}
function PolynomialKernel(; d::Td=2.0, c::Tc=0.0) where {Td<:Real, Tc<:Real}
Expand Down
4 changes: 2 additions & 2 deletions src/basekernels/rationalquad.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The rational-quadratic kernel is a 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} <: BaseKernel
struct RationalQuadraticKernel{Tα<:Real} <: SimpleKernel
α::Vector{Tα}
function RationalQuadraticKernel(;alpha::T=2.0, α::T=alpha) where {T}
@check_args(RationalQuadraticKernel, α, α > zero(T), "α > 1")
Expand All @@ -28,7 +28,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} <: BaseKernel
struct GammaRationalQuadraticKernel{Tα<:Real, Tγ<:Real} <: SimpleKernel
α::Vector{Tα}
γ::Vector{Tγ}
function GammaRationalQuadraticKernel(;alpha::Tα=2.0, gamma::Tγ=2.0, α::Tα=alpha, γ::Tγ=gamma) where {Tα<:Real, Tγ<:Real}
Expand Down
2 changes: 1 addition & 1 deletion src/generic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ end

for k in concretetypes(Kernel, [])
@eval begin
@inline (κ::$k)(x::AbstractVector{<:Real}, y::AbstractVector{<:Real}) = kappa(κ, x, y)
@inline (κ::$k)(x, y) = kappa(κ, x, y)
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
@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
Expand Down
2 changes: 2 additions & 0 deletions src/kernels/scaledkernel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ end

kappa(k::ScaledKernel, x) = first(k.σ²) * kappa(k.kernel, x)

kappa(k::ScaledKernel, x, y) = first(k.σ²) * kappa(k.kernel, x, y)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this been added?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I realised that ScaleKernel could only be applied to SimpleKernel

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, okay. And this is kappa in the kappa == kernelmatrix sense, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh no, it's in the sense of k(x,y), it multiplies by sigma for every function call. But this can get replaced when implementing #87


metric(k::ScaledKernel) = metric(k.kernel)

Base.:*(w::Real, k::Kernel) = ScaledKernel(k, w)
Expand Down
1 change: 0 additions & 1 deletion src/kernels/tensorproduct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ end

Base.length(kernel::TensorProduct) = length(kernel.kernels)

(kernel::TensorProduct)(x, y) = kappa(kernel, x, y)
function kappa(kernel::TensorProduct, x, y)
return prod(kappa(k, xi, yi) for (k, xi, yi) in zip(kernel.kernels, x, y))
end
Expand Down
14 changes: 14 additions & 0 deletions src/kernels/transformedkernel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ function printshifted(io::IO, κ::TransformedKernel, shift::Int)
printshifted(io, κ.kernel, shift)
print(io,"\n" * ("\t" ^ (shift + 1)) * "- $(κ.transform)")
end

# Kernel matrix operations

kernelmatrix!(K::AbstractMatrix, κ::TransformedKernel, X::AbstractMatrix; obsdim::Int = defaultobs) =
kernelmatrix!(K, kernel(κ), apply(κ.transform, X, obsdim = obsdim), obsdim = obsdim)

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)

kernelmatrix(κ::TransformedKernel, X::AbstractMatrix; obsdim::Int = defaultobs) =
kernelmatrix(kernel(κ), apply(κ.transform, X, obsdim = obsdim), obsdim = obsdim)

kernelmatrix(κ::TransformedKernel, X::AbstractMatrix, Y::AbstractMatrix; obsdim::Int = defaultobs) =
kernelmatrix(kernel(κ), apply(κ.transform, X, obsdim = obsdim), apply(κ.transform, Y, obsdim = obsdim), obsdim = obsdim)
willtebbutt marked this conversation as resolved.
Show resolved Hide resolved
Loading