Skip to content

Commit 4569859

Browse files
Adds kernel related to the Intrinsic Coregionalization Model (#263)
* Adds first version of Intrinsic Coregionalization Multi-Output Kernel. * Changes the name of the kernel. Removes "implementation code" for the parameterization of the coregionalization matrix. Removes white space. Renames file. Updates names in module file. Corrects check_args. Corrects using incorrect field name in coregion covariance function. Cleans white space. * Adds tests for CoregionMOKernel. * Adds coregion tests to runtests file. * Adds docstring to coregion kernel. * Update src/mokernels/coregion.jl Makes parameter explicit in function definition. Co-authored-by: David Widmann <[email protected]> * Makes notation in docstring conformant with the rest of the package. Co-authored-by: David Widmann <[email protected]> * Makes code respect formating style guidelines. * Adds Coregion kernel to docs file. * Removes unused argument. * Creates new methods for Finitedifferences.to_vec to deal with the inputs of multi-output kernels. * Creates new methods to compute the jacobian of multioutput kernels. * Formatting. * Reworkes to_vec so that the reimplementation of _jvp and jacobian is not needed. * Renames kernel. * Changes all affected files by the kernel renaming. * Adds test_interface method to generate inputs for MOkernels. * Makes IntrinsicCoregionMOKernel use the standard suit of tests. * Improves MOkernels tests to test different outputs dimensions. * Passes RNG object to rand method. * Adds MOKernel methods for testfunction and testdiagfunction. * Adds test_FiniteDiff method for MOKernels. * Changes test_FiniteDiff for MOKernel signature to have the same number of arguments as the methods for other kernels. * Corrects bug in tests where the created covariance matrix between outputs could be non PSD. * Adapts compare_gradient for MOKernels. * Adapts test_AD for MOKernels. * Adapts test_ADs for MOKernels. * Corrects type annotation. * Corrects argument splatting. * Rewrites variables as named tuple. * Updates arguments. * Adds standardised AD tests for IntrinsicCoregionMOKernel. * Adds include of finitedifferences file to runtests. * Passes dims argument. * Formatting. * Formatting. * Corrects a bug where the wrong tuple fields were used. * Corrects test. * Implements @theogf approach to testing gradients of MOkernels. * With new approach this file is not needed. * Correctly implement @theogf approach to MOkernels AD testing. * Changes tests to use SqExponentialKernel instead of ExponentialKernel because of the latter's AD instability. * Deletes unsused include. This file is not needed anymore. Co-authored-by: David Widmann <[email protected]> * Simplifies docstring. Co-authored-by: David Widmann <[email protected]> * Improves error message. Co-authored-by: David Widmann <[email protected]> * Deletes file because we currently pass just the Real part to gradient functions when testing. * Adds kernel and number of outputs when printing Icoregionkernel. * Adds test for different inputs as arguments. Co-authored-by: David Widmann <[email protected]> * Stops infringing character limit. Co-authored-by: David Widmann <[email protected]> * Further simplify docstring. Co-authored-by: David Widmann <[email protected]> * Adds tests. * Deletes unuseful tests. * Updates print test. * Stops using interpolations. * Creates constructor with keyword arguments. * Restore constructor. * Deletes non kwargs outer constructor. Co-authored-by: David Widmann <[email protected]> * Formatting. * Updates code to be in consistent with kwargs constructor. * Updates docstring. * Update Project.toml * Formatting. Co-authored-by: David Widmann <[email protected]>
1 parent 75b0d25 commit 4569859

File tree

8 files changed

+196
-2
lines changed

8 files changed

+196
-2
lines changed

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.10.1"
3+
version = "0.10.2"
44

55
[deps]
66
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"

docs/src/kernels.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,5 @@ NormalizedKernel
133133
MOKernel
134134
IndependentMOKernel
135135
LatentFactorMOKernel
136+
IntrinsicCoregionMOKernel
136137
```

src/KernelFunctions.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export spectral_mixture_kernel, spectral_mixture_product_kernel
3636
export ColVecs, RowVecs
3737

3838
export MOInput
39-
export IndependentMOKernel, LatentFactorMOKernel
39+
export IndependentMOKernel, LatentFactorMOKernel, IntrinsicCoregionMOKernel
4040

4141
# Reexports
4242
export tensor, , compose
@@ -105,6 +105,7 @@ include(joinpath("mokernels", "mokernel.jl"))
105105
include(joinpath("mokernels", "moinput.jl"))
106106
include(joinpath("mokernels", "independent.jl"))
107107
include(joinpath("mokernels", "slfm.jl"))
108+
include(joinpath("mokernels", "intrinsiccoregion.jl"))
108109

109110
include("chainrules.jl")
110111
include("zygoterules.jl")

src/mokernels/intrinsiccoregion.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@doc raw"""
2+
IntrinsicIntrinsicCoregionMOKernel(; kernel::Kernel, B::AbstractMatrix)
3+
4+
Kernel associated with the intrinsic coregionalization model.
5+
6+
# Definition
7+
8+
For inputs ``x, x'`` and output dimensions ``p, p'``, the kernel is defined as[^ARL]
9+
```math
10+
k\big((x, p), (x', p'); B, \tilde{k}\big) = B_{p, p'} \tilde{k}\big(x, x'\big),
11+
```
12+
where ``B`` is a positive semidefinite matrix of size ``m \times m``, with ``m`` being the
13+
number of outputs, and ``\tilde{k}`` is a scalar-valued kernel shared by the latent
14+
processes.
15+
16+
[^ARL]: M. Álvarez, L. Rosasco, & N. Lawrence (2012). [Kernels for Vector-Valued Functions: a Review](https://arxiv.org/pdf/1106.6251.pdf).
17+
"""
18+
struct IntrinsicCoregionMOKernel{K<:Kernel,T<:AbstractMatrix} <: MOKernel
19+
kernel::K
20+
B::T
21+
22+
function IntrinsicCoregionMOKernel{K,T}(kernel::K, B::T) where {K,T}
23+
@check_args(
24+
IntrinsicCoregionMOKernel,
25+
B,
26+
eigmin(B) >= 0,
27+
"B has to be positive semi-definite"
28+
)
29+
return new{K,T}(kernel, B)
30+
end
31+
end
32+
33+
function IntrinsicCoregionMOKernel(; kernel::Kernel, B::AbstractMatrix)
34+
return IntrinsicCoregionMOKernel{typeof(kernel),typeof(B)}(kernel, B)
35+
end
36+
37+
function (k::IntrinsicCoregionMOKernel)((x, px)::Tuple{Any,Int}, (y, py)::Tuple{Any,Int})
38+
return k.B[px, py] * k.kernel(x, y)
39+
end
40+
41+
function Base.show(io::IO, k::IntrinsicCoregionMOKernel)
42+
return print(
43+
io, "Intrinsic Coregion Kernel: ", k.kernel, " with ", size(k.B, 1), " outputs"
44+
)
45+
end

src/test_utils.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ function test_interface(
9797
)
9898
end
9999

100+
function test_interface(
101+
rng::AbstractRNG, k::MOKernel, ::Type{Vector{Tuple{T,Int}}}; dim_out=1, kwargs...
102+
) where {T<:Real}
103+
return test_interface(
104+
k,
105+
[(randn(rng, T), rand(rng, 1:dim_out)) for i in 1:51],
106+
[(randn(rng, T), rand(rng, 1:dim_out)) for i in 1:51],
107+
[(randn(rng, T), rand(rng, 1:dim_out)) for i in 1:50];
108+
kwargs...,
109+
)
110+
end
111+
100112
function test_interface(
101113
rng::AbstractRNG, k::Kernel, ::Type{<:ColVecs{T}}; dim_in=2, kwargs...
102114
) where {T<:Real}

test/mokernels/intrinsiccoregion.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@testset "intrinsiccoregion" begin
2+
rng = MersenneTwister(123)
3+
4+
dims = (in=3, out=2, obs=3)
5+
rank = 1
6+
7+
A = randn(dims.out, rank)
8+
B = A * transpose(A) + Diagonal(rand(dims.out))
9+
10+
X = [(rand(dims.in), rand(1:(dims.out))) for i in 1:(dims.obs)]
11+
12+
kernel = SqExponentialKernel()
13+
icoregionkernel = IntrinsicCoregionMOKernel(; kernel=kernel, B=B)
14+
15+
@test icoregionkernel.B == B
16+
@test icoregionkernel.kernel == kernel
17+
@test icoregionkernel(X[1], X[1]) B[X[1][2], X[1][2]] * kernel(X[1][1], X[1][1])
18+
@test icoregionkernel(X[1], X[end]) B[X[1][2], X[end][2]] * kernel(X[1][1], X[end][1])
19+
20+
KernelFunctions.TestUtils.test_interface(
21+
icoregionkernel, Vector{Tuple{Float64,Int}}; dim_out=dims.out
22+
)
23+
24+
test_ADs(icoregionkernel; dims=dims)
25+
26+
@test string(icoregionkernel) ==
27+
string("Intrinsic Coregion Kernel: ", kernel, " with ", dims.out, " outputs")
28+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ include("test_utils.jl")
139139
include(joinpath("mokernels", "moinput.jl"))
140140
include(joinpath("mokernels", "independent.jl"))
141141
include(joinpath("mokernels", "slfm.jl"))
142+
include(joinpath("mokernels", "intrinsiccoregion.jl"))
142143
end
143144
@info "Ran tests on Multi-Output Kernels"
144145

test/test_utils.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ testfunction(k, A, dim) = sum(kernelmatrix(k, A; obsdim=dim))
5353
testdiagfunction(k, A, dim) = sum(kernelmatrix_diag(k, A; obsdim=dim))
5454
testdiagfunction(k, A, B, dim) = sum(kernelmatrix_diag(k, A, B; obsdim=dim))
5555

56+
testfunction(k::MOKernel, A, B) = sum(kernelmatrix(k, A, B))
57+
testfunction(k::MOKernel, A) = sum(kernelmatrix(k, A))
58+
testdiagfunction(k::MOKernel, A) = sum(kernelmatrix_diag(k, A))
59+
testdiagfunction(k::MOKernel, A, B) = sum(kernelmatrix_diag(k, A, B))
60+
5661
function test_ADs(
5762
kernelfunction, args=nothing; ADs=[:Zygote, :ForwardDiff, :ReverseDiff], dims=[3, 3]
5863
)
@@ -64,6 +69,17 @@ function test_ADs(
6469
end
6570
end
6671

72+
function test_ADs(
73+
k::MOKernel; ADs=[:Zygote, :ForwardDiff, :ReverseDiff], dims=(in=3, out=2, obs=3)
74+
)
75+
test_fd = test_FiniteDiff(k, dims)
76+
if !test_fd.anynonpass
77+
for AD in ADs
78+
test_AD(AD, k, dims)
79+
end
80+
end
81+
end
82+
6783
function test_FiniteDiff(kernelfunction, args=nothing, dims=[3, 3])
6884
# Init arguments :
6985
k = if args === nothing
@@ -128,6 +144,50 @@ function test_FiniteDiff(kernelfunction, args=nothing, dims=[3, 3])
128144
end
129145
end
130146

147+
function test_FiniteDiff(k::MOKernel, dims=(in=3, out=2, obs=3))
148+
rng = MersenneTwister(42)
149+
@testset "FiniteDifferences" begin
150+
## Testing Kernel Functions
151+
x = (rand(rng, dims.in), rand(rng, 1:(dims.out)))
152+
y = (rand(rng, dims.in), rand(rng, 1:(dims.out)))
153+
154+
@test_nowarn gradient(:FiniteDiff, x[1]) do a
155+
k((a, x[2]), y)
156+
end
157+
158+
## Testing Kernel Matrices
159+
160+
A = [(randn(rng, dims.in), rand(rng, 1:(dims.out))) for i in 1:(dims.obs)]
161+
B = [(randn(rng, dims.in), rand(rng, 1:(dims.out))) for i in 1:(dims.obs)]
162+
163+
@test_nowarn gradient(:FiniteDiff, reduce(hcat, first.(A))) do a
164+
A = tuple.(eachcol(a), last.(A))
165+
testfunction(k, A)
166+
end
167+
@test_nowarn gradient(:FiniteDiff, reduce(hcat, first.(A))) do a
168+
A = tuple.(eachcol(a), last.(A))
169+
testfunction(k, A, B)
170+
end
171+
@test_nowarn gradient(:FiniteDiff, reduce(hcat, first.(B))) do b
172+
B = tuple.(eachcol(b), last.(B))
173+
testfunction(k, A, B)
174+
end
175+
176+
@test_nowarn gradient(:FiniteDiff, reduce(hcat, first.(A))) do a
177+
A = tuple.(eachcol(a), last.(A))
178+
testdiagfunction(k, A)
179+
end
180+
@test_nowarn gradient(:FiniteDiff, reduce(hcat, first.(A))) do a
181+
A = tuple.(eachcol(a), last.(A))
182+
testdiagfunction(k, A, B)
183+
end
184+
@test_nowarn gradient(:FiniteDiff, reduce(hcat, first.(B))) do b
185+
B = tuple.(eachcol(b), last.(B))
186+
testdiagfunction(k, A, B)
187+
end
188+
end
189+
end
190+
131191
function test_AD(AD::Symbol, kernelfunction, args=nothing, dims=[3, 3])
132192
@testset "$(AD)" begin
133193
# Test kappa function
@@ -194,3 +254,49 @@ function test_AD(AD::Symbol, kernelfunction, args=nothing, dims=[3, 3])
194254
end
195255
end
196256
end
257+
258+
function test_AD(AD::Symbol, k::MOKernel, dims=(in=3, out=2, obs=3))
259+
@testset "$(AD)" begin
260+
rng = MersenneTwister(42)
261+
262+
# Testing kernel evaluations
263+
x = (rand(rng, dims.in), rand(rng, 1:(dims.out)))
264+
y = (rand(rng, dims.in), rand(rng, 1:(dims.out)))
265+
266+
compare_gradient(AD, x[1]) do a
267+
k((a, x[2]), y)
268+
end
269+
compare_gradient(AD, y[1]) do b
270+
k(x, (b, y[2]))
271+
end
272+
273+
# Testing kernel matrices
274+
A = [(randn(rng, dims.in), rand(rng, 1:(dims.out))) for i in 1:(dims.obs)]
275+
B = [(randn(rng, dims.in), rand(rng, 1:(dims.out))) for i in 1:(dims.obs)]
276+
277+
compare_gradient(AD, reduce(hcat, first.(A))) do a
278+
A = tuple.(eachcol(a), last.(A))
279+
testfunction(k, A)
280+
end
281+
compare_gradient(AD, reduce(hcat, first.(A))) do a
282+
A = tuple.(eachcol(a), last.(A))
283+
testfunction(k, A, B)
284+
end
285+
compare_gradient(AD, reduce(hcat, first.(B))) do b
286+
B = tuple.(eachcol(b), last.(B))
287+
testfunction(k, A, B)
288+
end
289+
compare_gradient(AD, reduce(hcat, first.(A))) do a
290+
A = tuple.(eachcol(a), last.(A))
291+
testdiagfunction(k, A)
292+
end
293+
compare_gradient(AD, reduce(hcat, first.(A))) do a
294+
A = tuple.(eachcol(a), last.(A))
295+
testdiagfunction(k, A, B)
296+
end
297+
compare_gradient(AD, reduce(hcat, first.(B))) do b
298+
B = tuple.(eachcol(b), last.(B))
299+
testdiagfunction(k, A, B)
300+
end
301+
end
302+
end

0 commit comments

Comments
 (0)