brian-j-smith/Mamba.jl

Gaussian Process Positive Definite Exception

gcgibson opened this issue ยท 10 comments

I have written the following code for fitting a simple GP

using Mamba
## Model Specification
function kernel_with_noise(x,y,s2)
  dist = exp((-(x-y)^2))
  if x != y
    dist
  else
    1 + s2
  end
end


function kernel(x,y)
  dist = exp((-(x-y)^2))
end


line = Dict{Symbol, Any}(
  :x =>[1,2],
  :y => [1,2],
  :xtest => [1,2]
)
line[:xmat] = line[:x]
size_ = length(line[:x])

function posterior(xmat,xtest,y,s2)
  K = ones(length(xmat),length(xmat))
  K_ss = ones(length(xtest),length(xtest))
  K_s = ones(length(xtest),length(xmat))
  for i in 1:size(K)[1]
    for j in 1:size(K)[2]
      K[i,j] = kernel_with_noise(xmat[i],xmat[j],s2)
    end
  end
  for i in 1:size(K_ss)[1]
    for j in 1:size(K_ss)[2]
      K_ss[i,j] = kernel(xtest[i],xtest[j])
    end
  end
  for i in 1:size(K_s)[1]
    for j in 1:size(K_s)[2]
      K_s[i,j] = kernel(xtest[i],xmat[j])
    end
  end
  mu_star = K_s*inv(K)*y
  sigma_star = K_ss-transpose(K_s)*inv(K)*K_s
  mu_star,sigma_star
end


model = Model(

  y = Stochastic(1,
    (cov) ->  MvNormal(zeros(size_), cov),
    false
  ),
  ytilde = Logical(1,
    (s2,xtest,xmat) ->
    begin
      mu_star,sigma_star = posterior(xmat,xtest,line[:y],s2)
      d = MvNormal(mu_star,sigma_star)
      rand(d)
    end
  ),
  cov = Logical(2,
    (xmat,s2) ->
    begin
      x = eye(size_)
      for i in 1:size_
        for j in 1:size_
          tmp = kernel_with_noise(xmat[i],xmat[j],s2)
          x[i,j] = tmp
        end
      end
      x
    end
  ),
  s2 = Stochastic(
    () -> InverseGamma(.1, .1)
  )

)
scheme = [Slice([:s2],1.0)]


## Initial Values
inits = [
  Dict{Symbol, Any}(
    :y => line[:y],
    :s2 => rand(Gamma(.1, .1))
  )
for i in 1:1
]
setsamplers!(model, scheme)
sim1 = mcmc(model, line, inits, 10000, burnin=250, thin=2, chains=1)
describe(sim1)

which throws a

Base.LinAlg.PosDefException(1)

while exploring the posterior for certain samples. Is there anyway to reject a sample based on this exception and keep going? I am not sure that is the correct way of handling bad s^2 values so if anyone has other suggestions for what to do please let me know!

It may be easier to just write your own sampler rather than putting a lot of sampler logic in the Stochastic/Logical node definitions.

@bdeonovic That makes sense, but the custom samplers still require some return

Gibbs_sigma2 = Sampler([:sigma2],
  (mu, sigma2, y) ->
    begin
      a = length(y) / 2.0 + shape(sigma2.distr)
      b = sumabs2(y - mu) / 2.0 + scale(sigma2.distr)
      rand(InverseGamma(a, b))
    end
)

In this case a random draw from InvGamma. In the case of a non positive definite sample I would like to simple reject it from the chain, can I hack my sampler to do that?

EDIT: To clarify, if I wanted to put an if statement like so

Gibbs_sigma2 = Sampler([:sigma2],
  (mu, sigma2, y) ->
    begin
      a = length(y) / 2.0 + shape(sigma2.distr)
      b = sumabs2(y - mu) / 2.0 + scale(sigma2.distr)
      if matrix formed from some combination of a and b is positive semi definite:
           rand(InverseGamma(a, b))
      else
           not sure what to put here
    end
)

You might want to check why non-positive definite matrices are being formed. Sometimes a matrix is not positive definite in julia due to round off errors. In those cases you can explicitly specify to julia that the matrix is Positive definite.

Otherwise, you can always return the current iterate if the proposed matrix is not positive definite.

How do you get access to the underlying chain to return the last accepted step, I guess you don't need it you can just return the input to your sampler?

in the example you have

Gibbs_sigma2 = Sampler([:sigma2],
  (mu, sigma2, y) ->
    begin
      ##sigma2.value is the current value of sigma2 which you are trying to sample a new value for
      a = length(y) / 2.0 + shape(sigma2.distr)
      b = sumabs2(y - mu) / 2.0 + scale(sigma2.distr)
      if matrix formed from some combination of a and b is positive semi definite:
           rand(InverseGamma(a, b))
      else
           not sure what to put here
    end
)
``

Awesome! Thanks for your help! The matrices are PSD until s^2 gets sufficiently small

Unfortunately, I can't use Gibbs sampling because the conditional density is not available (unlike Bayesian linear regression , gaussian processes don't have an analytical distribution for s2 that I know of ). Is there anyway to get a code snippet like that into MH or NUTS?

Your custom sampler doesn't have to be a Gibbs sampler. It can be whatever you want. For MH you would just need to calculate the the acceptance probability for example.

But then I would need a proposal dist etc right? I would basically have to re-implement MH in that code block? Gibbs seemed nice because it was just actually sampling from known dists, but I would prefer to pass a flag to the MH sampler saying treat this exception as probability 0, if possible.