2013-06-05 14:11:49 +01:00
# Copyright (c) 2012, GPy authors (see AUTHORS.txt).
# Licensed under the BSD 3-clause license (see LICENSE.txt)
import numpy as np
import pylab as pb
2013-06-06 14:04:14 +01:00
from . . util . linalg import mdot , jitchol , tdot , symmetrify , backsub_both_sides , chol_inv , dtrtrs , dpotrs , dpotri
2013-06-05 14:11:49 +01:00
from scipy import linalg
2013-07-31 19:00:54 +01:00
from . . likelihoods import Gaussian , EP , EP_Mixed_Noise
2013-06-05 14:11:49 +01:00
from gp_base import GPBase
class SparseGP ( GPBase ) :
"""
Variational sparse GP model
: param X : inputs
2013-06-05 16:14:43 +01:00
: type X : np . ndarray ( num_data x input_dim )
2013-06-05 14:11:49 +01:00
: param likelihood : a likelihood instance , containing the observed data
: type likelihood : GPy . likelihood . ( Gaussian | EP | Laplace )
: param kernel : the kernel ( covariance function ) . See link kernels
: type kernel : a GPy . kern . kern instance
: param X_variance : The uncertainty in the measurements of X ( Gaussian variance )
2013-06-05 16:14:43 +01:00
: type X_variance : np . ndarray ( num_data x input_dim ) | None
2013-06-05 14:11:49 +01:00
: param Z : inducing inputs ( optional , see note )
: type Z : np . ndarray ( num_inducing x input_dim ) | None
: param num_inducing : Number of inducing points ( optional , default 10. Ignored if Z is not None )
: type num_inducing : int
: param normalize_ ( X | Y ) : whether to normalize the data before computing ( predictions will be in original scales )
: type normalize_ ( X | Y ) : bool
"""
def __init__ ( self , X , likelihood , kernel , Z , X_variance = None , normalize_X = False ) :
GPBase . __init__ ( self , X , likelihood , kernel , normalize_X = normalize_X )
self . Z = Z
self . num_inducing = Z . shape [ 0 ]
2013-06-25 17:43:42 +01:00
# self.likelihood = likelihood
2013-06-05 14:11:49 +01:00
if X_variance is None :
self . has_uncertain_inputs = False
2013-06-25 17:43:42 +01:00
self . X_variance = None
2013-06-05 14:11:49 +01:00
else :
assert X_variance . shape == X . shape
self . has_uncertain_inputs = True
self . X_variance = X_variance
if normalize_X :
2013-06-05 19:18:29 +01:00
self . Z = ( self . Z . copy ( ) - self . _Xoffset ) / self . _Xscale
2013-06-05 14:11:49 +01:00
# normalize X uncertainty also
if self . has_uncertain_inputs :
2013-06-05 19:18:29 +01:00
self . X_variance / = np . square ( self . _Xscale )
2013-08-21 09:53:49 +01:00
2013-08-02 16:36:51 +01:00
self . _const_jitter = None
2013-06-05 14:11:49 +01:00
2013-06-26 16:48:48 +01:00
def getstate ( self ) :
2013-06-25 17:43:42 +01:00
"""
Get the current state of the class ,
here just all the indices , rest can get recomputed
"""
2013-06-26 16:48:48 +01:00
return GPBase . getstate ( self ) + [ self . Z ,
2013-06-25 17:43:42 +01:00
self . num_inducing ,
self . has_uncertain_inputs ,
self . X_variance ]
2013-06-26 16:48:48 +01:00
def setstate ( self , state ) :
2013-06-25 17:43:42 +01:00
self . X_variance = state . pop ( )
self . has_uncertain_inputs = state . pop ( )
self . num_inducing = state . pop ( )
self . Z = state . pop ( )
2013-06-26 16:48:48 +01:00
GPBase . setstate ( self , state )
2013-06-25 17:43:42 +01:00
2013-06-05 14:11:49 +01:00
def _compute_kernel_matrices ( self ) :
# kernel computations, using BGPLVM notation
self . Kmm = self . kern . K ( self . Z )
if self . has_uncertain_inputs :
self . psi0 = self . kern . psi0 ( self . Z , self . X , self . X_variance )
2013-06-06 14:38:48 +01:00
self . psi1 = self . kern . psi1 ( self . Z , self . X , self . X_variance )
2013-06-05 14:11:49 +01:00
self . psi2 = self . kern . psi2 ( self . Z , self . X , self . X_variance )
else :
self . psi0 = self . kern . Kdiag ( self . X )
2013-06-06 14:38:48 +01:00
self . psi1 = self . kern . K ( self . X , self . Z )
2013-06-05 14:11:49 +01:00
self . psi2 = None
def _computations ( self ) :
2013-08-02 16:36:51 +01:00
if self . _const_jitter is None or not ( self . _const_jitter . shape [ 0 ] == self . num_inducing ) :
self . _const_jitter = np . eye ( self . num_inducing ) * 1e-7
2013-08-21 09:53:49 +01:00
# factor Kmm
self . _Lm = jitchol ( self . Kmm + self . _const_jitter )
2013-08-02 16:36:51 +01:00
# TODO: no white kernel needed anymore, all noise in likelihood --------
2013-06-05 14:11:49 +01:00
2013-08-21 09:53:49 +01:00
# The rather complex computations of self._A
2013-06-05 14:11:49 +01:00
if self . has_uncertain_inputs :
if self . likelihood . is_heteroscedastic :
2013-06-05 16:14:43 +01:00
psi2_beta = ( self . psi2 * ( self . likelihood . precision . flatten ( ) . reshape ( self . num_data , 1 , 1 ) ) ) . sum ( 0 )
2013-06-05 14:11:49 +01:00
else :
2013-07-30 11:43:07 +01:00
psi2_beta = self . psi2 . sum ( 0 ) * self . likelihood . precision
2013-06-05 14:11:49 +01:00
evals , evecs = linalg . eigh ( psi2_beta )
2013-07-30 11:43:07 +01:00
clipped_evals = np . clip ( evals , 0. , 1e6 ) # TODO: make clipping configurable
2013-07-30 10:17:40 +01:00
if not np . array_equal ( evals , clipped_evals ) :
2013-08-02 16:36:51 +01:00
pass # print evals
2013-06-05 14:11:49 +01:00
tmp = evecs * np . sqrt ( clipped_evals )
2013-06-06 14:38:48 +01:00
tmp = tmp . T
2013-06-05 14:11:49 +01:00
else :
if self . likelihood . is_heteroscedastic :
2013-06-25 17:43:42 +01:00
tmp = self . psi1 * ( np . sqrt ( self . likelihood . precision . flatten ( ) . reshape ( self . num_data , 1 ) ) )
2013-06-05 14:11:49 +01:00
else :
tmp = self . psi1 * ( np . sqrt ( self . likelihood . precision ) )
2013-08-21 09:53:49 +01:00
tmp , _ = dtrtrs ( self . _Lm , np . asfortranarray ( tmp . T ) , lower = 1 )
self . _A = tdot ( tmp )
2013-06-05 14:11:49 +01:00
# factor B
2013-08-21 09:53:49 +01:00
self . B = np . eye ( self . num_inducing ) + self . _A
2013-06-05 14:11:49 +01:00
self . LB = jitchol ( self . B )
2013-07-18 15:15:09 +01:00
# VVT_factor is a matrix such that tdot(VVT_factor) = VVT...this is for efficiency!
2013-06-14 14:04:53 +01:00
self . psi1Vf = np . dot ( self . psi1 . T , self . likelihood . VVT_factor )
2013-06-05 14:11:49 +01:00
2013-06-14 14:04:53 +01:00
# back substutue C into psi1Vf
2013-08-21 09:53:49 +01:00
tmp , info1 = dtrtrs ( self . _Lm , np . asfortranarray ( self . psi1Vf ) , lower = 1 , trans = 0 )
2013-06-14 14:04:53 +01:00
self . _LBi_Lmi_psi1Vf , _ = dtrtrs ( self . LB , np . asfortranarray ( tmp ) , lower = 1 , trans = 0 )
2013-08-02 16:36:51 +01:00
# tmp, info2 = dpotrs(self.LB, tmp, lower=1)
2013-07-30 10:17:40 +01:00
tmp , info2 = dtrtrs ( self . LB , self . _LBi_Lmi_psi1Vf , lower = 1 , trans = 1 )
2013-08-21 09:53:49 +01:00
self . Cpsi1Vf , info3 = dtrtrs ( self . _Lm , tmp , lower = 1 , trans = 1 )
2013-06-05 14:11:49 +01:00
# Compute dL_dKmm
2013-06-14 14:04:53 +01:00
tmp = tdot ( self . _LBi_Lmi_psi1Vf )
self . data_fit = np . trace ( tmp )
2013-06-05 16:14:43 +01:00
self . DBi_plus_BiPBi = backsub_both_sides ( self . LB , self . output_dim * np . eye ( self . num_inducing ) + tmp )
2013-06-05 14:11:49 +01:00
tmp = - 0.5 * self . DBi_plus_BiPBi
2013-06-05 16:14:43 +01:00
tmp + = - 0.5 * self . B * self . output_dim
tmp + = self . output_dim * np . eye ( self . num_inducing )
2013-08-21 09:53:49 +01:00
self . dL_dKmm = backsub_both_sides ( self . _Lm , tmp )
2013-06-05 14:11:49 +01:00
# Compute dL_dpsi # FIXME: this is untested for the heterscedastic + uncertain inputs case
2013-06-05 16:14:43 +01:00
self . dL_dpsi0 = - 0.5 * self . output_dim * ( self . likelihood . precision * np . ones ( [ self . num_data , 1 ] ) ) . flatten ( )
2013-06-14 14:04:53 +01:00
self . dL_dpsi1 = np . dot ( self . likelihood . VVT_factor , self . Cpsi1Vf . T )
2013-08-21 09:53:49 +01:00
dL_dpsi2_beta = 0.5 * backsub_both_sides ( self . _Lm , self . output_dim * np . eye ( self . num_inducing ) - self . DBi_plus_BiPBi )
2013-06-05 14:11:49 +01:00
if self . likelihood . is_heteroscedastic :
2013-09-13 12:45:58 +01:00
2013-06-05 14:11:49 +01:00
if self . has_uncertain_inputs :
self . dL_dpsi2 = self . likelihood . precision . flatten ( ) [ : , None , None ] * dL_dpsi2_beta [ None , : , : ]
else :
2013-06-06 14:38:48 +01:00
self . dL_dpsi1 + = 2. * np . dot ( dL_dpsi2_beta , ( self . psi1 * self . likelihood . precision . reshape ( self . num_data , 1 ) ) . T ) . T
2013-06-05 14:11:49 +01:00
self . dL_dpsi2 = None
else :
dL_dpsi2 = self . likelihood . precision * dL_dpsi2_beta
if self . has_uncertain_inputs :
# repeat for each of the N psi_2 matrices
2013-06-05 16:14:43 +01:00
self . dL_dpsi2 = np . repeat ( dL_dpsi2 [ None , : , : ] , self . num_data , axis = 0 )
2013-06-05 14:11:49 +01:00
else :
# subsume back into psi1 (==Kmn)
2013-06-06 14:38:48 +01:00
self . dL_dpsi1 + = 2. * np . dot ( self . psi1 , dL_dpsi2 )
2013-06-05 14:11:49 +01:00
self . dL_dpsi2 = None
# the partial derivative vector for the likelihood
if self . likelihood . Nparams == 0 :
# save computation here.
self . partial_for_likelihood = None
elif self . likelihood . is_heteroscedastic :
2013-09-13 12:45:58 +01:00
if self . has_uncertain_inputs :
raise NotImplementedError , " heteroscedatic derivates with uncertain inputs not implemented "
else :
2013-09-17 14:25:59 +01:00
LBi = chol_inv ( self . LB )
2013-09-13 18:09:59 +01:00
Lmi_psi1 , nil = dtrtrs ( self . _Lm , np . asfortranarray ( self . psi1 . T ) , lower = 1 , trans = 0 )
2013-09-13 12:45:58 +01:00
_LBi_Lmi_psi1 , _ = dtrtrs ( self . LB , np . asfortranarray ( Lmi_psi1 ) , lower = 1 , trans = 0 )
2013-09-17 14:25:59 +01:00
2013-09-13 12:45:58 +01:00
self . partial_for_likelihood = - 0.5 * self . likelihood . precision + 0.5 * self . likelihood . V * * 2
self . partial_for_likelihood + = 0.5 * self . output_dim * ( self . psi0 - np . sum ( Lmi_psi1 * * 2 , 0 ) ) [ : , None ] * self . likelihood . precision * * 2
2013-09-17 14:25:59 +01:00
self . partial_for_likelihood + = 0.5 * np . sum ( mdot ( LBi . T , LBi , Lmi_psi1 ) * Lmi_psi1 , 0 ) [ : , None ] * self . likelihood . precision * * 2
2013-09-13 12:45:58 +01:00
self . partial_for_likelihood + = - np . dot ( self . _LBi_Lmi_psi1Vf . T , _LBi_Lmi_psi1 ) . T * self . likelihood . Y * self . likelihood . precision * * 2
self . partial_for_likelihood + = 0.5 * np . dot ( self . _LBi_Lmi_psi1Vf . T , _LBi_Lmi_psi1 ) . T * * 2 * self . likelihood . precision * * 2
2013-06-05 14:11:49 +01:00
else :
2013-09-13 12:45:58 +01:00
# likelihood is not heteroscedatic
2013-06-05 16:14:43 +01:00
self . partial_for_likelihood = - 0.5 * self . num_data * self . output_dim * self . likelihood . precision + 0.5 * self . likelihood . trYYT * self . likelihood . precision * * 2
2013-08-21 09:53:49 +01:00
self . partial_for_likelihood + = 0.5 * self . output_dim * ( self . psi0 . sum ( ) * self . likelihood . precision * * 2 - np . trace ( self . _A ) * self . likelihood . precision )
self . partial_for_likelihood + = self . likelihood . precision * ( 0.5 * np . sum ( self . _A * self . DBi_plus_BiPBi ) - self . data_fit )
2013-06-05 14:11:49 +01:00
def log_likelihood ( self ) :
""" Compute the (lower bound on the) log marginal likelihood """
if self . likelihood . is_heteroscedastic :
2013-07-18 15:15:09 +01:00
A = - 0.5 * self . num_data * self . output_dim * np . log ( 2. * np . pi ) + 0.5 * np . sum ( np . log ( self . likelihood . precision ) ) - 0.5 * np . sum ( self . likelihood . V * self . likelihood . Y )
2013-08-21 09:53:49 +01:00
B = - 0.5 * self . output_dim * ( np . sum ( self . likelihood . precision . flatten ( ) * self . psi0 ) - np . trace ( self . _A ) )
2013-06-05 14:11:49 +01:00
else :
2013-06-05 16:14:43 +01:00
A = - 0.5 * self . num_data * self . output_dim * ( np . log ( 2. * np . pi ) - np . log ( self . likelihood . precision ) ) - 0.5 * self . likelihood . precision * self . likelihood . trYYT
2013-08-21 09:53:49 +01:00
B = - 0.5 * self . output_dim * ( np . sum ( self . likelihood . precision * self . psi0 ) - np . trace ( self . _A ) )
2013-06-05 14:52:37 +01:00
C = - self . output_dim * ( np . sum ( np . log ( np . diag ( self . LB ) ) ) ) # + 0.5 * self.num_inducing * np.log(sf2))
2013-06-14 14:04:53 +01:00
D = 0.5 * self . data_fit
2013-06-05 14:11:49 +01:00
return A + B + C + D + self . likelihood . Z
def _set_params ( self , p ) :
2013-06-05 16:14:30 +01:00
self . Z = p [ : self . num_inducing * self . input_dim ] . reshape ( self . num_inducing , self . input_dim )
2013-06-05 15:29:18 +01:00
self . kern . _set_params ( p [ self . Z . size : self . Z . size + self . kern . num_params ] )
self . likelihood . _set_params ( p [ self . Z . size + self . kern . num_params : ] )
2013-06-05 14:11:49 +01:00
self . _compute_kernel_matrices ( )
self . _computations ( )
2013-06-14 14:04:53 +01:00
self . Cpsi1V = None
2013-06-05 14:11:49 +01:00
def _get_params ( self ) :
return np . hstack ( [ self . Z . flatten ( ) , self . kern . _get_params_transformed ( ) , self . likelihood . _get_params ( ) ] )
def _get_param_names ( self ) :
2013-06-25 17:43:42 +01:00
return sum ( [ [ ' iip_ %i _ %i ' % ( i , j ) for j in range ( self . Z . shape [ 1 ] ) ] for i in range ( self . Z . shape [ 0 ] ) ] , [ ] ) \
2013-06-05 14:11:49 +01:00
+ self . kern . _get_param_names_transformed ( ) + self . likelihood . _get_param_names ( )
2013-09-16 12:24:37 +01:00
#def _get_print_names(self):
# return self.kern._get_param_names_transformed() + self.likelihood._get_param_names()
2013-09-02 11:53:45 +01:00
2013-06-05 14:11:49 +01:00
def update_likelihood_approximation ( self ) :
"""
Approximates a non - gaussian likelihood using Expectation Propagation
For a Gaussian likelihood , no iteration is required :
this function does nothing
"""
if not isinstance ( self . likelihood , Gaussian ) : # Updates not needed for Gaussian likelihood
2013-06-05 18:01:53 +01:00
self . likelihood . restart ( )
2013-06-05 14:11:49 +01:00
if self . has_uncertain_inputs :
2013-08-21 09:53:49 +01:00
Lmi = chol_inv ( self . _Lm )
2013-06-05 14:11:49 +01:00
Kmmi = tdot ( Lmi . T )
diag_tr_psi2Kmmi = np . array ( [ np . trace ( psi2_Kmmi ) for psi2_Kmmi in np . dot ( self . psi2 , Kmmi ) ] )
2013-06-06 14:38:48 +01:00
self . likelihood . fit_FITC ( self . Kmm , self . psi1 . T , diag_tr_psi2Kmmi ) # This uses the fit_FITC code, but does not perfomr a FITC-EP.#TODO solve potential confusion
2013-06-05 14:11:49 +01:00
# raise NotImplementedError, "EP approximation not implemented for uncertain inputs"
else :
2013-06-06 14:38:48 +01:00
self . likelihood . fit_DTC ( self . Kmm , self . psi1 . T )
2013-06-05 14:11:49 +01:00
# self.likelihood.fit_FITC(self.Kmm,self.psi1,self.psi0)
self . _set_params ( self . _get_params ( ) ) # update the GP
def _log_likelihood_gradients ( self ) :
return np . hstack ( ( self . dL_dZ ( ) . flatten ( ) , self . dL_dtheta ( ) , self . likelihood . _gradients ( partial = self . partial_for_likelihood ) ) )
def dL_dtheta ( self ) :
"""
Compute and return the derivative of the log marginal likelihood wrt the parameters of the kernel
"""
dL_dtheta = self . kern . dK_dtheta ( self . dL_dKmm , self . Z )
if self . has_uncertain_inputs :
dL_dtheta + = self . kern . dpsi0_dtheta ( self . dL_dpsi0 , self . Z , self . X , self . X_variance )
2013-06-06 14:38:48 +01:00
dL_dtheta + = self . kern . dpsi1_dtheta ( self . dL_dpsi1 , self . Z , self . X , self . X_variance )
2013-06-05 14:11:49 +01:00
dL_dtheta + = self . kern . dpsi2_dtheta ( self . dL_dpsi2 , self . Z , self . X , self . X_variance )
else :
2013-06-06 14:38:48 +01:00
dL_dtheta + = self . kern . dK_dtheta ( self . dL_dpsi1 , self . X , self . Z )
2013-06-05 14:11:49 +01:00
dL_dtheta + = self . kern . dKdiag_dtheta ( self . dL_dpsi0 , self . X )
return dL_dtheta
def dL_dZ ( self ) :
"""
The derivative of the bound wrt the inducing inputs Z
"""
2013-09-17 14:25:59 +01:00
dL_dZ = self . kern . dK_dX ( self . dL_dKmm , self . Z )
2013-06-05 14:11:49 +01:00
if self . has_uncertain_inputs :
dL_dZ + = self . kern . dpsi1_dZ ( self . dL_dpsi1 , self . Z , self . X , self . X_variance )
dL_dZ + = self . kern . dpsi2_dZ ( self . dL_dpsi2 , self . Z , self . X , self . X_variance )
else :
2013-06-06 14:38:48 +01:00
dL_dZ + = self . kern . dK_dX ( self . dL_dpsi1 . T , self . Z , self . X )
2013-06-05 14:11:49 +01:00
return dL_dZ
def _raw_predict ( self , Xnew , X_variance_new = None , which_parts = ' all ' , full_cov = False ) :
2013-08-21 09:53:49 +01:00
"""
Internal helper function for making predictions , does not account for
normalization or likelihood function
"""
2013-06-05 14:11:49 +01:00
2013-06-25 17:43:42 +01:00
Bi , _ = dpotri ( self . LB , lower = 0 ) # WTH? this lower switch should be 1, but that doesn't work!
2013-06-05 14:11:49 +01:00
symmetrify ( Bi )
2013-08-21 09:53:49 +01:00
Kmmi_LmiBLmi = backsub_both_sides ( self . _Lm , np . eye ( self . num_inducing ) - Bi )
2013-06-05 14:11:49 +01:00
2013-06-14 14:04:53 +01:00
if self . Cpsi1V is None :
2013-07-18 15:15:09 +01:00
psi1V = np . dot ( self . psi1 . T , self . likelihood . V )
2013-08-21 09:53:49 +01:00
tmp , _ = dtrtrs ( self . _Lm , np . asfortranarray ( psi1V ) , lower = 1 , trans = 0 )
2013-06-14 14:04:53 +01:00
tmp , _ = dpotrs ( self . LB , tmp , lower = 1 )
2013-08-21 09:53:49 +01:00
self . Cpsi1V , _ = dtrtrs ( self . _Lm , tmp , lower = 1 , trans = 1 )
2013-06-14 14:04:53 +01:00
2013-06-05 14:11:49 +01:00
if X_variance_new is None :
Kx = self . kern . K ( self . Z , Xnew , which_parts = which_parts )
mu = np . dot ( Kx . T , self . Cpsi1V )
if full_cov :
Kxx = self . kern . K ( Xnew , which_parts = which_parts )
var = Kxx - mdot ( Kx . T , Kmmi_LmiBLmi , Kx ) # NOTE this won't work for plotting
else :
Kxx = self . kern . Kdiag ( Xnew , which_parts = which_parts )
var = Kxx - np . sum ( Kx * np . dot ( Kmmi_LmiBLmi , Kx ) , 0 )
else :
2013-06-06 14:38:48 +01:00
# assert which_p.Tarts=='all', "swithching out parts of variational kernels is not implemented"
2013-06-05 14:11:49 +01:00
Kx = self . kern . psi1 ( self . Z , Xnew , X_variance_new ) # , which_parts=which_parts) TODO: which_parts
2013-06-06 16:27:39 +01:00
mu = np . dot ( Kx , self . Cpsi1V )
2013-06-05 14:11:49 +01:00
if full_cov :
raise NotImplementedError , " TODO "
else :
Kxx = self . kern . psi0 ( self . Z , Xnew , X_variance_new )
psi2 = self . kern . psi2 ( self . Z , Xnew , X_variance_new )
var = Kxx - np . sum ( np . sum ( psi2 * Kmmi_LmiBLmi [ None , : , : ] , 1 ) , 1 )
return mu , var [ : , None ]
def predict ( self , Xnew , X_variance_new = None , which_parts = ' all ' , full_cov = False ) :
"""
Predict the function ( s ) at the new point ( s ) Xnew .
Arguments
- - - - - - - - -
: param Xnew : The points at which to make a prediction
: type Xnew : np . ndarray , Nnew x self . input_dim
: param X_variance_new : The uncertainty in the prediction points
: type X_variance_new : np . ndarray , Nnew x self . input_dim
: param which_parts : specifies which outputs kernel ( s ) to use in prediction
: type which_parts : ( ' all ' , list of bools )
2013-09-13 12:45:58 +01:00
: param full_cov : whether to return the full covariance matrix , or just the diagonal
2013-06-05 14:11:49 +01:00
: type full_cov : bool
: rtype : posterior mean , a Numpy array , Nnew x self . input_dim
: rtype : posterior variance , a Numpy array , Nnew x 1 if full_cov = False , Nnew x Nnew otherwise
: rtype : lower and upper boundaries of the 95 % confidence intervals , Numpy arrays , Nnew x self . input_dim
If full_cov and self . input_dim > 1 , the return shape of var is Nnew x Nnew x self . input_dim . If self . input_dim == 1 , the return shape is Nnew x Nnew .
This is to allow for different normalizations of the output dimensions .
"""
# normalize X values
2013-06-05 19:18:29 +01:00
Xnew = ( Xnew . copy ( ) - self . _Xoffset ) / self . _Xscale
2013-06-05 14:11:49 +01:00
if X_variance_new is not None :
2013-06-05 19:18:29 +01:00
X_variance_new = X_variance_new / self . _Xscale * * 2
2013-06-05 14:11:49 +01:00
# here's the actual prediction by the GP model
mu , var = self . _raw_predict ( Xnew , X_variance_new , full_cov = full_cov , which_parts = which_parts )
# now push through likelihood
mean , var , _025pm , _975pm = self . likelihood . predictive_values ( mu , var , full_cov )
return mean , var , _025pm , _975pm
2013-08-02 20:10:02 +01:00
def plot ( self , samples = 0 , plot_limits = None , which_data = ' all ' , which_parts = ' all ' , resolution = None , levels = 20 , fignum = None , ax = None , output = None ) :
2013-06-05 14:11:49 +01:00
if ax is None :
fig = pb . figure ( num = fignum )
ax = fig . add_subplot ( 111 )
if which_data is ' all ' :
which_data = slice ( None )
2013-08-02 20:10:02 +01:00
GPBase . plot ( self , samples = 0 , plot_limits = plot_limits , which_data = ' all ' , which_parts = ' all ' , resolution = None , levels = 20 , ax = ax , output = output )
2013-09-17 16:54:31 +01:00
if not hasattr ( self , ' multioutput ' ) :
2013-08-02 20:10:02 +01:00
2013-09-17 16:54:31 +01:00
if self . X . shape [ 1 ] == 1 :
if self . has_uncertain_inputs :
Xu = self . X * self . _Xscale + self . _Xoffset # NOTE self.X are the normalized values now
ax . errorbar ( Xu [ which_data , 0 ] , self . likelihood . data [ which_data , 0 ] ,
xerr = 2 * np . sqrt ( self . X_variance [ which_data , 0 ] ) ,
ecolor = ' k ' , fmt = None , elinewidth = .5 , alpha = .5 )
Zu = self . Z * self . _Xscale + self . _Xoffset
ax . plot ( Zu , np . zeros_like ( Zu ) + ax . get_ylim ( ) [ 0 ] , ' r| ' , mew = 1.5 , markersize = 12 )
2013-08-02 20:10:02 +01:00
2013-09-17 16:54:31 +01:00
elif self . X . shape [ 1 ] == 2 :
Zu = self . Z * self . _Xscale + self . _Xoffset
ax . plot ( Zu [ : , 0 ] , Zu [ : , 1 ] , ' wo ' )
2013-08-02 20:10:02 +01:00
else :
2013-09-17 16:54:31 +01:00
pass
"""
if self . X . shape [ 1 ] == 2 and hasattr ( self , ' multioutput ' ) :
Xu = self . X [ self . X [ : , - 1 ] == output , : ]
if self . has_uncertain_inputs :
Xu = self . X * self . _Xscale + self . _Xoffset # NOTE self.X are the normalized values now
Xu = self . X [ self . X [ : , - 1 ] == output , 0 : 1 ] #??
ax . errorbar ( Xu [ which_data , 0 ] , self . likelihood . data [ which_data , 0 ] ,
xerr = 2 * np . sqrt ( self . X_variance [ which_data , 0 ] ) ,
ecolor = ' k ' , fmt = None , elinewidth = .5 , alpha = .5 )
Zu = self . Z [ self . Z [ : , - 1 ] == output , : ]
Zu = self . Z * self . _Xscale + self . _Xoffset
Zu = self . Z [ self . Z [ : , - 1 ] == output , 0 : 1 ] #??
ax . plot ( Zu , np . zeros_like ( Zu ) + ax . get_ylim ( ) [ 0 ] , ' r| ' , mew = 1.5 , markersize = 12 )
#ax.set_ylim(ax.get_ylim()[0],)
else :
raise NotImplementedError , " Cannot define a frame with more than two input dimensions "
"""
2013-08-02 20:10:02 +01:00
2013-07-31 19:00:54 +01:00
def predict_single_output ( self , Xnew , output = 0 , which_parts = ' all ' , full_cov = False ) :
"""
2013-09-13 12:45:58 +01:00
For a specific output , predict the function at the new point ( s ) Xnew .
2013-07-31 19:00:54 +01:00
Arguments
- - - - - - - - -
: param Xnew : The points at which to make a prediction
: type Xnew : np . ndarray , Nnew x self . input_dim
2013-09-13 12:45:58 +01:00
: param output : output to predict
: type output : integer in { 0 , . . . , num_outputs - 1 }
2013-07-31 19:00:54 +01:00
: param which_parts : specifies which outputs kernel ( s ) to use in prediction
: type which_parts : ( ' all ' , list of bools )
2013-09-13 12:45:58 +01:00
: param full_cov : whether to return the full covariance matrix , or just the diagonal
2013-07-31 19:00:54 +01:00
: type full_cov : bool
: rtype : posterior mean , a Numpy array , Nnew x self . input_dim
: rtype : posterior variance , a Numpy array , Nnew x 1 if full_cov = False , Nnew x Nnew otherwise
: rtype : lower and upper boundaries of the 95 % confidence intervals , Numpy arrays , Nnew x self . input_dim
2013-09-13 12:45:58 +01:00
. . Note : : For multiple output models only
2013-07-31 19:00:54 +01:00
"""
2013-09-13 12:45:58 +01:00
2013-08-02 20:10:02 +01:00
assert hasattr ( self , ' multioutput ' )
2013-07-31 19:00:54 +01:00
index = np . ones_like ( Xnew ) * output
Xnew = np . hstack ( ( Xnew , index ) )
# normalize X values
Xnew = ( Xnew . copy ( ) - self . _Xoffset ) / self . _Xscale
mu , var = self . _raw_predict ( Xnew , full_cov = full_cov , which_parts = which_parts )
# now push through likelihood
2013-09-13 12:45:58 +01:00
mean , var , _025pm , _975pm = self . likelihood . predictive_values ( mu , var , full_cov , noise_model = output )
2013-07-31 19:00:54 +01:00
return mean , var , _025pm , _975pm
2013-08-02 20:10:02 +01:00
def _raw_predict_single_output ( self , _Xnew , output = 0 , X_variance_new = None , which_parts = ' all ' , full_cov = False , stop = False ) :
"""
2013-09-13 12:45:58 +01:00
Internal helper function for making predictions for a specific output ,
does not account for normalization or likelihood
- - - - - - - - -
: param Xnew : The points at which to make a prediction
: type Xnew : np . ndarray , Nnew x self . input_dim
: param output : output to predict
: type output : integer in { 0 , . . . , num_outputs - 1 }
: param which_parts : specifies which outputs kernel ( s ) to use in prediction
: type which_parts : ( ' all ' , list of bools )
: param full_cov : whether to return the full covariance matrix , or just the diagonal
. . Note : : For multiple output models only
2013-08-02 20:10:02 +01:00
"""
Bi , _ = dpotri ( self . LB , lower = 0 ) # WTH? this lower switch should be 1, but that doesn't work!
symmetrify ( Bi )
2013-09-13 18:09:59 +01:00
Kmmi_LmiBLmi = backsub_both_sides ( self . _Lm , np . eye ( self . num_inducing ) - Bi )
2013-08-02 20:10:02 +01:00
if self . Cpsi1V is None :
psi1V = np . dot ( self . psi1 . T , self . likelihood . V )
2013-09-13 18:09:59 +01:00
tmp , _ = dtrtrs ( self . _Lm , np . asfortranarray ( psi1V ) , lower = 1 , trans = 0 )
2013-08-02 20:10:02 +01:00
tmp , _ = dpotrs ( self . LB , tmp , lower = 1 )
2013-09-13 18:09:59 +01:00
self . Cpsi1V , _ = dtrtrs ( self . _Lm , tmp , lower = 1 , trans = 1 )
2013-08-02 20:10:02 +01:00
assert hasattr ( self , ' multioutput ' )
index = np . ones_like ( _Xnew ) * output
_Xnew = np . hstack ( ( _Xnew , index ) )
if X_variance_new is None :
Kx = self . kern . K ( self . Z , _Xnew , which_parts = which_parts )
mu = np . dot ( Kx . T , self . Cpsi1V )
if full_cov :
Kxx = self . kern . K ( _Xnew , which_parts = which_parts )
var = Kxx - mdot ( Kx . T , Kmmi_LmiBLmi , Kx ) # NOTE this won't work for plotting
else :
Kxx = self . kern . Kdiag ( _Xnew , which_parts = which_parts )
var = Kxx - np . sum ( Kx * np . dot ( Kmmi_LmiBLmi , Kx ) , 0 )
else :
2013-09-13 12:45:58 +01:00
Kx = self . kern . psi1 ( self . Z , _Xnew , X_variance_new )
2013-08-02 20:10:02 +01:00
mu = np . dot ( Kx , self . Cpsi1V )
if full_cov :
raise NotImplementedError , " TODO "
else :
Kxx = self . kern . psi0 ( self . Z , _Xnew , X_variance_new )
psi2 = self . kern . psi2 ( self . Z , _Xnew , X_variance_new )
var = Kxx - np . sum ( np . sum ( psi2 * Kmmi_LmiBLmi [ None , : , : ] , 1 ) , 1 )
return mu , var [ : , None ]