From cebf7daab066a07eabc662d76b12df04e5b95b42 Mon Sep 17 00:00:00 2001 From: WT Date: Fri, 27 Aug 2021 16:49:19 +0100 Subject: [PATCH 1/4] Sketch of functionality --- src/AbstractGPs.jl | 3 ++ src/vector_valued_gp.jl | 77 ++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 4 +++ test/vector_valued_gp.jl | 14 ++++++++ 4 files changed, 98 insertions(+) create mode 100644 src/vector_valued_gp.jl create mode 100644 test/vector_valued_gp.jl diff --git a/src/AbstractGPs.jl b/src/AbstractGPs.jl index 784d8a3f..6177a51b 100644 --- a/src/AbstractGPs.jl +++ b/src/AbstractGPs.jl @@ -56,6 +56,9 @@ include("sparse_approximations.jl") # LatentGP and LatentFiniteGP objects to accommodate GPs with non-Gaussian likelihoods. include("latent_gp.jl") +# GPs whose output is a vector. +include("vector_valued_gp.jl") + # Plotting utilities. include(joinpath("util", "plotting.jl")) diff --git a/src/vector_valued_gp.jl b/src/vector_valued_gp.jl new file mode 100644 index 00000000..7d9d4628 --- /dev/null +++ b/src/vector_valued_gp.jl @@ -0,0 +1,77 @@ +# Represents a GP whose output is vector-valued. +struct VectorValuedGP{Tf<:AbstractGP} + f::Tf + num_outputs::Int +end + +# I gave up figuring out how to properly subtype MatrixDistribution, but I want this to +# subtype a distribution type which indicates that samples from this distribution produces +# matrix of size num_features x num_outputs, or something like that. +struct FiniteVectorValuedGP{Tv<:VectorValuedGP, Tx<:AbstractVector, TΣy<:Real} + v::Tv + x::Tx + Σy::TΣy +end + +Base.length(f::FiniteVectorValuedGP) = length(f.x) + +(f::VectorValuedGP)(x...) = FiniteVectorValuedGP(f, x...) + +function Statistics.mean(vx::FiniteVectorValuedGP) + + # Construct equivalent FiniteGP. + x_f = KernelFunctions.MOInputIsotopicByOutputs(vx.x, vx.v.num_outputs) + f = vx.v.f + fx = f(x_f, vx.Σy) + + # Compute quantity under equivalent FiniteGP. + m = mean(fx) + + # Construct the matrix-version of the quantity. + M = reshape(m, length(vx.x), vx.v.num_outputs) + return M +end + +function Statistics.var(vx::FiniteVectorValuedGP) + + # Construct equivalent FiniteGP. + x_f = KernelFunctions.MOInputIsotopicByOutputs(vx.x, vx.v.num_outputs) + f = vx.v.f + fx = f(x_f, vx.Σy) + + # Compute quantity under equivalent FiniteGP. + v = var(fx) + + # Construct the matrix-version of the quantity. + V = reshape(v, length(vx.x), vx.v.num_outputs) + return V +end + +function Random.rand(rng::AbstractRNG, vx::FiniteVectorValuedGP) + + # Construct equivalent FiniteGP. + x_f = KernelFunctions.MOInputIsotopicByOutputs(vx.x, vx.v.num_outputs) + f = vx.v.f + fx = f(x_f, vx.Σy) + + # Compute quantity under equivalent FiniteGP. + y = rand(rng, fx) + + # Construct the matrix-version of the quantity. + Y = reshape(y, length(vx.x), vx.v.num_outputs) + return Y +end + +function Distributions.logpdf(vx::FiniteVectorValuedGP, Y::AbstractMatrix{<:Real}) + + # Construct equivalent FiniteGP. + x_f = KernelFunctions.MOInputIsotopicByOutputs(vx.x, vx.v.num_outputs) + f = vx.v.f + fx = f(x_f, vx.Σy) + + # Construct flattened-version of observations. + y = vec(Y) + + # Compute logpdf using FiniteGP. + return logpdf(fx, y) +end diff --git a/test/runtests.jl b/test/runtests.jl index fc365ddc..6e22e7ee 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -77,6 +77,10 @@ include("test_util.jl") println(" ") @info "Ran latent_gp tests" + include("vector_valued_gp.jl") + println(" ") + @info "Ran vector_valued_gp tests" + include("deprecations.jl") println(" ") @info "Ran deprecation tests" diff --git a/test/vector_valued_gp.jl b/test/vector_valued_gp.jl new file mode 100644 index 00000000..33c271f6 --- /dev/null +++ b/test/vector_valued_gp.jl @@ -0,0 +1,14 @@ +@testset "vector_valued_gp" begin + f = GP(LinearMixingModelKernel([Matern52Kernel(), Matern12Kernel()], randn(2, 2))) + x = range(0.0, 10.0; length=3) + Σy = 0.1 + + v = AbstractGPs.VectorValuedGP(f, 2) + vx = v(x, Σy) + + M = mean(vx) + + rng = MersenneTwister(123456) + Y = rand(rng, vx) + logpdf(vx, Y) +end From 7d2c68e33ba3e9c9532db27f05dfc50f1224d0e5 Mon Sep 17 00:00:00 2001 From: WT Date: Fri, 27 Aug 2021 16:56:22 +0100 Subject: [PATCH 2/4] Remove faulty length implementation --- src/vector_valued_gp.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vector_valued_gp.jl b/src/vector_valued_gp.jl index 7d9d4628..ce9ebdb7 100644 --- a/src/vector_valued_gp.jl +++ b/src/vector_valued_gp.jl @@ -13,8 +13,6 @@ struct FiniteVectorValuedGP{Tv<:VectorValuedGP, Tx<:AbstractVector, TΣy<:Real} Σy::TΣy end -Base.length(f::FiniteVectorValuedGP) = length(f.x) - (f::VectorValuedGP)(x...) = FiniteVectorValuedGP(f, x...) function Statistics.mean(vx::FiniteVectorValuedGP) From a2ae2c2e4d948e2bdbe983c0ae4f5c0f8e231e46 Mon Sep 17 00:00:00 2001 From: willtebbutt Date: Fri, 27 Aug 2021 16:56:40 +0100 Subject: [PATCH 3/4] Update src/vector_valued_gp.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/vector_valued_gp.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector_valued_gp.jl b/src/vector_valued_gp.jl index ce9ebdb7..6e29a483 100644 --- a/src/vector_valued_gp.jl +++ b/src/vector_valued_gp.jl @@ -7,7 +7,7 @@ end # I gave up figuring out how to properly subtype MatrixDistribution, but I want this to # subtype a distribution type which indicates that samples from this distribution produces # matrix of size num_features x num_outputs, or something like that. -struct FiniteVectorValuedGP{Tv<:VectorValuedGP, Tx<:AbstractVector, TΣy<:Real} +struct FiniteVectorValuedGP{Tv<:VectorValuedGP,Tx<:AbstractVector,TΣy<:Real} v::Tv x::Tx Σy::TΣy From 9fff953deb2232186a92dc0bb47e249ccb5dcf2b Mon Sep 17 00:00:00 2001 From: WT Date: Fri, 27 Aug 2021 17:25:59 +0100 Subject: [PATCH 4/4] Add posterior --- src/vector_valued_gp.jl | 17 +++++++++++++++++ test/vector_valued_gp.jl | 2 ++ 2 files changed, 19 insertions(+) diff --git a/src/vector_valued_gp.jl b/src/vector_valued_gp.jl index ce9ebdb7..54ecfbaa 100644 --- a/src/vector_valued_gp.jl +++ b/src/vector_valued_gp.jl @@ -73,3 +73,20 @@ function Distributions.logpdf(vx::FiniteVectorValuedGP, Y::AbstractMatrix{<:Real # Compute logpdf using FiniteGP. return logpdf(fx, y) end + +function posterior(vx::FiniteVectorValuedGP, Y::AbstractMatrix{<:Real}) + + # Construct equivalent FiniteGP. + x_f = KernelFunctions.MOInputIsotopicByOutputs(vx.x, vx.v.num_outputs) + f = vx.v.f + fx = f(x_f, vx.Σy) + + # Construct flattened-version of observations. + y = vec(Y) + + # Construct posterior AbstractGP + f_post = posterior(fx, y) + + # Construct a new vector-valued GP. + return VectorValuedGP(f_post, vx.v.num_outputs) +end diff --git a/test/vector_valued_gp.jl b/test/vector_valued_gp.jl index 33c271f6..97500941 100644 --- a/test/vector_valued_gp.jl +++ b/test/vector_valued_gp.jl @@ -11,4 +11,6 @@ rng = MersenneTwister(123456) Y = rand(rng, vx) logpdf(vx, Y) + + v_post = posterior(vx, Y) end